diff options
author | Tomás Touceda <chiiph@leap.se> | 2014-04-04 16:58:18 -0300 |
---|---|---|
committer | Tomás Touceda <chiiph@leap.se> | 2014-04-04 16:58:18 -0300 |
commit | 81715dc47d77934c4f67d2527a56c28f58f0345d (patch) | |
tree | 6e66940af735e089803c5ce05ad1ce1df16c9d1e /src/leap/bitmask/provider | |
parent | 496036f15cf257d16b6594770812da64a249280c (diff) | |
parent | eb4cdab9c6b8ff66bb4667cc6195d2c366122540 (diff) |
Merge branch 'release-0.5.0'
Diffstat (limited to 'src/leap/bitmask/provider')
-rw-r--r-- | src/leap/bitmask/provider/__init__.py | 33 | ||||
-rw-r--r-- | src/leap/bitmask/provider/providerbootstrapper.py | 103 | ||||
-rw-r--r-- | src/leap/bitmask/provider/supportedapis.py | 38 | ||||
-rw-r--r-- | src/leap/bitmask/provider/tests/test_providerbootstrapper.py | 11 |
4 files changed, 102 insertions, 83 deletions
diff --git a/src/leap/bitmask/provider/__init__.py b/src/leap/bitmask/provider/__init__.py index 53587d65..89ff5d95 100644 --- a/src/leap/bitmask/provider/__init__.py +++ b/src/leap/bitmask/provider/__init__.py @@ -15,12 +15,20 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ -Module initialization for leap.bitmask.provider +Provider utilities. """ import os + +from pkg_resources import parse_version + +from leap.bitmask import __short_version__ as BITMASK_VERSION from leap.common.check import leap_assert +# The currently supported API versions by the client. +SUPPORTED_APIS = ["1"] + + def get_provider_path(domain): """ Returns relative path for provider config. @@ -32,3 +40,26 @@ def get_provider_path(domain): """ leap_assert(domain is not None, "get_provider_path: We need a domain") return os.path.join("leap", "providers", domain, "provider.json") + + +def supports_api(api_version): + """ + :param api_version: the version number of the api that we need to check + :type api_version: str + + :returns: if that version is supported or not. + :return type: bool + """ + return api_version in SUPPORTED_APIS + + +def supports_client(minimum_version): + """ + :param minimum_version: the version number of the client that + we need to check. + :type minimum_version: str + + :returns: True if that version is supported or False otherwise. + :return type: bool + """ + return parse_version(minimum_version) <= parse_version(BITMASK_VERSION) diff --git a/src/leap/bitmask/provider/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py index f5a2003f..2a519206 100644 --- a/src/leap/bitmask/provider/providerbootstrapper.py +++ b/src/leap/bitmask/provider/providerbootstrapper.py @@ -24,18 +24,18 @@ import sys import requests -from PySide import QtCore - -from leap.bitmask.config.providerconfig import ProviderConfig, MissingCACert -from leap.bitmask.util.request_helpers import get_content +from leap.bitmask import provider from leap.bitmask import util -from leap.bitmask.util.constants import REQUEST_TIMEOUT +from leap.bitmask.config import flags +from leap.bitmask.config.providerconfig import ProviderConfig, MissingCACert +from leap.bitmask.provider import get_provider_path from leap.bitmask.services.abstractbootstrapper import AbstractBootstrapper -from leap.bitmask.provider.supportedapis import SupportedAPIs +from leap.bitmask.util.constants import REQUEST_TIMEOUT +from leap.bitmask.util.request_helpers import get_content from leap.common import ca_bundle from leap.common.certs import get_digest -from leap.common.files import check_and_fix_urw_only, get_mtime, mkdir_p from leap.common.check import leap_assert, leap_assert_type, leap_check +from leap.common.files import check_and_fix_urw_only, get_mtime, mkdir_p logger = logging.getLogger(__name__) @@ -47,6 +47,14 @@ class UnsupportedProviderAPI(Exception): pass +class UnsupportedClientVersionError(Exception): + """ + Raised when attempting to use a provider with an older + client than supported. + """ + pass + + class WrongFingerprint(Exception): """ Raised when a fingerprint comparison does not match. @@ -61,25 +69,21 @@ class ProviderBootstrapper(AbstractBootstrapper): If a check fails, the subsequent checks are not executed """ - # All dicts returned are of the form - # {"passed": bool, "error": str} - name_resolution = QtCore.Signal(dict) - https_connection = QtCore.Signal(dict) - download_provider_info = QtCore.Signal(dict) + MIN_CLIENT_VERSION = 'x-minimum-client-version' - download_ca_cert = QtCore.Signal(dict) - check_ca_fingerprint = QtCore.Signal(dict) - check_api_certificate = QtCore.Signal(dict) - - def __init__(self, bypass_checks=False): + def __init__(self, signaler=None, bypass_checks=False): """ Constructor for provider bootstrapper object + :param signaler: Signaler object used to receive notifications + from the backend + :type signaler: Signaler :param bypass_checks: Set to true if the app should bypass - first round of checks for CA certificates at bootstrap + first round of checks for CA + certificates at bootstrap :type bypass_checks: bool """ - AbstractBootstrapper.__init__(self, bypass_checks) + AbstractBootstrapper.__init__(self, signaler, bypass_checks) self._domain = None self._provider_config = None @@ -95,9 +99,14 @@ class ProviderBootstrapper(AbstractBootstrapper): :rtype: bool or str """ if self._bypass_checks: - verify = False + return False + + cert = flags.CA_CERT_FILE + if cert is not None: + verify = cert else: verify = ca_bundle.where() + return verify def _check_name_resolution(self): @@ -163,8 +172,8 @@ class ProviderBootstrapper(AbstractBootstrapper): headers = {} domain = self._domain.encode(sys.getfilesystemencoding()) provider_json = os.path.join(util.get_path_prefix(), - "leap", "providers", domain, - "provider.json") + get_provider_path(domain)) + mtime = get_mtime(provider_json) if self._download_if_needed and mtime: @@ -195,6 +204,8 @@ class ProviderBootstrapper(AbstractBootstrapper): res.raise_for_status() logger.debug("Request status code: {0}".format(res.status_code)) + min_client_version = res.headers.get(self.MIN_CLIENT_VERSION, '0') + # Not modified if res.status_code == 304: logger.debug("Provider definition has not been modified") @@ -202,6 +213,13 @@ class ProviderBootstrapper(AbstractBootstrapper): # end refactor, more or less... # XXX Watch out, have to check the supported api yet. else: + if flags.APP_VERSION_CHECK: + # TODO split + if not provider.supports_client(min_client_version): + self._signaler.signal( + self._signaler.PROV_UNSUPPORTED_CLIENT) + raise UnsupportedClientVersionError() + provider_definition, mtime = get_content(res) provider_config = ProviderConfig() @@ -209,17 +227,20 @@ class ProviderBootstrapper(AbstractBootstrapper): provider_config.save(["leap", "providers", domain, "provider.json"]) - api_version = provider_config.get_api_version() - if SupportedAPIs.supports(api_version): - logger.debug("Provider definition has been modified") - else: - api_supported = ', '.join(SupportedAPIs.SUPPORTED_APIS) - error = ('Unsupported provider API version. ' - 'Supported versions are: {0}. ' - 'Found: {1}.').format(api_supported, api_version) - - logger.error(error) - raise UnsupportedProviderAPI(error) + if flags.API_VERSION_CHECK: + # TODO split + api_version = provider_config.get_api_version() + if provider.supports_api(api_version): + logger.debug("Provider definition has been modified") + else: + api_supported = ', '.join(provider.SUPPORTED_APIS) + error = ('Unsupported provider API version. ' + 'Supported versions are: {0}. ' + 'Found: {1}.').format(api_supported, api_version) + + logger.error(error) + self._signaler.signal(self._signaler.PROV_UNSUPPORTED_API) + raise UnsupportedProviderAPI(error) def run_provider_select_checks(self, domain, download_if_needed=False): """ @@ -238,9 +259,11 @@ class ProviderBootstrapper(AbstractBootstrapper): self._download_if_needed = download_if_needed cb_chain = [ - (self._check_name_resolution, self.name_resolution), - (self._check_https, self.https_connection), - (self._download_provider_info, self.download_provider_info) + (self._check_name_resolution, + self._signaler.PROV_NAME_RESOLUTION_KEY), + (self._check_https, self._signaler.PROV_HTTPS_CONNECTION_KEY), + (self._download_provider_info, + self._signaler.PROV_DOWNLOAD_PROVIDER_INFO_KEY) ] return self.addCallbackChain(cb_chain) @@ -367,9 +390,11 @@ class ProviderBootstrapper(AbstractBootstrapper): self._download_if_needed = download_if_needed cb_chain = [ - (self._download_ca_cert, self.download_ca_cert), - (self._check_ca_fingerprint, self.check_ca_fingerprint), - (self._check_api_certificate, self.check_api_certificate) + (self._download_ca_cert, self._signaler.PROV_DOWNLOAD_CA_CERT_KEY), + (self._check_ca_fingerprint, + self._signaler.PROV_CHECK_CA_FINGERPRINT_KEY), + (self._check_api_certificate, + self._signaler.PROV_CHECK_API_CERTIFICATE_KEY) ] return self.addCallbackChain(cb_chain) diff --git a/src/leap/bitmask/provider/supportedapis.py b/src/leap/bitmask/provider/supportedapis.py deleted file mode 100644 index 3e650ba2..00000000 --- a/src/leap/bitmask/provider/supportedapis.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# supportedapis.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -""" -API Support check. -""" - - -class SupportedAPIs(object): - """ - Class responsible of checking for API compatibility. - """ - SUPPORTED_APIS = ["1"] - - @classmethod - def supports(self, api_version): - """ - :param api_version: the version number of the api that we need to check - :type api_version: str - - :returns: if that version is supported or not. - :return type: bool - """ - return api_version in self.SUPPORTED_APIS diff --git a/src/leap/bitmask/provider/tests/test_providerbootstrapper.py b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py index 88a4ff0b..6cf3e469 100644 --- a/src/leap/bitmask/provider/tests/test_providerbootstrapper.py +++ b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py @@ -36,21 +36,22 @@ from nose.twistedtools import deferred, reactor from twisted.internet import threads from requests.models import Response +from leap.bitmask import provider +from leap.bitmask import util +from leap.bitmask.backend import Signaler from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.crypto.tests import fake_provider from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper from leap.bitmask.provider.providerbootstrapper import UnsupportedProviderAPI from leap.bitmask.provider.providerbootstrapper import WrongFingerprint -from leap.bitmask.provider.supportedapis import SupportedAPIs -from leap.bitmask import util from leap.common.files import mkdir_p -from leap.common.testing.https_server import where from leap.common.testing.basetest import BaseLeapTest +from leap.common.testing.https_server import where class ProviderBootstrapperTest(BaseLeapTest): def setUp(self): - self.pb = ProviderBootstrapper() + self.pb = ProviderBootstrapper(Signaler()) def tearDown(self): pass @@ -488,7 +489,7 @@ class ProviderBootstrapperActiveTest(unittest.TestCase): 'leap.bitmask.config.providerconfig.ProviderConfig.get_ca_cert_path', lambda x: where('cacert.pem')) def test_download_provider_info_unsupported_api(self): - self._setup_provider_config_with(SupportedAPIs.SUPPORTED_APIS[0], + self._setup_provider_config_with(provider.SUPPORTED_APIS[0], tempfile.mkdtemp()) self._setup_providerbootstrapper(False) self._produce_dummy_provider_json() |