diff options
| author | Tomás Touceda <chiiph@leap.se> | 2014-06-06 12:59:11 -0300 | 
|---|---|---|
| committer | Tomás Touceda <chiiph@leap.se> | 2014-06-11 12:16:42 -0300 | 
| commit | cfc7122f07a3b16f2d9cb5421b739dae252604cb (patch) | |
| tree | b9db4cca4f1230f3dffa5acc001d2298a3fb6adc | |
| parent | 6ca25420cce6264df850f580d7842910ab3d10d4 (diff) | |
Improve how pinned providers are handled
| -rw-r--r-- | changes/feature_improve_pinned_providers | 1 | ||||
| -rw-r--r-- | src/leap/bitmask/backend.py | 23 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/wizard.ui | 2 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/wizard.py | 110 | ||||
| -rw-r--r-- | src/leap/bitmask/provider/pinned.py | 73 | ||||
| -rw-r--r-- | src/leap/bitmask/provider/pinned_demobitmask.py | 115 | ||||
| -rw-r--r-- | src/leap/bitmask/provider/providerbootstrapper.py | 9 | 
7 files changed, 287 insertions, 46 deletions
| diff --git a/changes/feature_improve_pinned_providers b/changes/feature_improve_pinned_providers new file mode 100644 index 00000000..dd5f7ec3 --- /dev/null +++ b/changes/feature_improve_pinned_providers @@ -0,0 +1 @@ +- Improve how pinned providers are handled by hardcoding it instead of expecting them to be in the config. Closes #4733.
\ No newline at end of file diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index 3c97c797..7ebe7f97 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -38,6 +38,7 @@ from leap.bitmask.crypto.srpauth import SRPAuth  from leap.bitmask.crypto.srpregister import SRPRegister  from leap.bitmask.platform_init import IS_LINUX  from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper +from leap.bitmask.provider.pinned import PinnedProviders  from leap.bitmask.services import get_supported  from leap.bitmask.services.eip import eipconfig  from leap.bitmask.services.eip import get_openvpn_management @@ -275,6 +276,16 @@ class Provider(object):              self._signaler.PROV_GET_DETAILS,              self._provider_config.get_light_config(domain, lang)) +    def get_pinned_providers(self): +        """ +        Signal the list of pinned provider domains. + +        Signals: +            prov_get_pinned_providers -> list of provider domains +        """ +        self._signaler.signal( +            self._signaler.PROV_GET_PINNED_PROVIDERS, +            PinnedProviders.domains())  class Register(object):      """ @@ -1127,6 +1138,7 @@ class Signaler(QtCore.QObject):      prov_get_all_services = QtCore.Signal(object)      prov_get_supported_services = QtCore.Signal(object)      prov_get_details = QtCore.Signal(object) +    prov_get_pinned_providers = QtCore.Signal(object)      prov_cancelled_setup = QtCore.Signal(object) @@ -1240,6 +1252,7 @@ class Signaler(QtCore.QObject):      PROV_GET_ALL_SERVICES = "prov_get_all_services"      PROV_GET_SUPPORTED_SERVICES = "prov_get_supported_services"      PROV_GET_DETAILS = "prov_get_details" +    PROV_GET_PINNED_PROVIDERS = "prov_get_pinned_providers"      SRP_REGISTRATION_FINISHED = "srp_registration_finished"      SRP_REGISTRATION_FAILED = "srp_registration_failed" @@ -1340,6 +1353,7 @@ class Signaler(QtCore.QObject):              self.PROV_GET_ALL_SERVICES,              self.PROV_GET_SUPPORTED_SERVICES,              self.PROV_GET_DETAILS, +            self.PROV_GET_PINNED_PROVIDERS,              self.SRP_REGISTRATION_FINISHED,              self.SRP_REGISTRATION_FAILED, @@ -1681,6 +1695,15 @@ class Backend(object):          """          self._call_queue.put(("provider", "get_details", None, domain, lang)) +    def provider_get_pinned_providers(self): +        """ +        Signal the pinned providers. + +        Signals: +            prov_get_pinned_providers -> list of provider domains +        """ +        self._call_queue.put(("provider", "get_pinned_providers", None)) +      def user_register(self, provider, username, password):          """          Register a user using the domain and password given as parameters. diff --git a/src/leap/bitmask/gui/ui/wizard.ui b/src/leap/bitmask/gui/ui/wizard.ui index 6c592522..8c52897d 100644 --- a/src/leap/bitmask/gui/ui/wizard.ui +++ b/src/leap/bitmask/gui/ui/wizard.ui @@ -38,7 +38,7 @@    <property name="options">     <set>QWizard::IndependentPages</set>    </property> -  <widget class="QWizardPage" name="introduction_page"> +  <widget class="WizardPage" name="introduction_page">     <property name="title">      <string>Welcome</string>     </property> diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 4d774907..05cfbed3 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -33,6 +33,7 @@ from leap.bitmask.util.keyring_helpers import has_keyring  from ui_wizard import Ui_Wizard +QtDelayedCall = QtCore.QTimer.singleShot  logger = logging.getLogger(__name__) @@ -64,6 +65,8 @@ class Wizard(QtGui.QWizard):          self.ui = Ui_Wizard()          self.ui.setupUi(self) +        self._connected_signals = [] +          self.setPixmap(QtGui.QWizard.LogoPixmap,                         QtGui.QPixmap(":/images/mask-icon.png")) @@ -79,8 +82,8 @@ class Wizard(QtGui.QWizard):          self._use_existing_provider = False          self.ui.grpCheckProvider.setVisible(False) -        self.ui.btnCheck.clicked.connect(self._check_provider) -        self.ui.lnProvider.returnPressed.connect(self._check_provider) +        self._connect_and_track(self.ui.btnCheck.clicked, self._check_provider) +        self._connect_and_track(self.ui.lnProvider.returnPressed, self._check_provider)          self._backend = backend          self._backend_connect() @@ -95,24 +98,25 @@ class Wizard(QtGui.QWizard):          self._provider_select_defer = None          self._provider_setup_defer = None -        self.currentIdChanged.connect(self._current_id_changed) +        self._connect_and_track(self.currentIdChanged, self._current_id_changed) -        self.ui.lnProvider.textChanged.connect(self._enable_check) -        self.ui.rbNewProvider.toggled.connect( +        self._connect_and_track(self.ui.lnProvider.textChanged, self._enable_check) +        self._connect_and_track(self.ui.rbNewProvider.toggled,              lambda x: self._enable_check()) -        self.ui.cbProviders.currentIndexChanged[int].connect( +        self._connect_and_track(self.ui.cbProviders.currentIndexChanged[int],              self._reset_provider_check) -        self.ui.lblUser.returnPressed.connect( +        self._connect_and_track(self.ui.lblUser.returnPressed,              self._focus_password) -        self.ui.lblPassword.returnPressed.connect( +        self._connect_and_track(self.ui.lblPassword.returnPressed,              self._focus_second_password) -        self.ui.lblPassword2.returnPressed.connect( +        self._connect_and_track(self.ui.lblPassword2.returnPressed,              self._register) -        self.ui.btnRegister.clicked.connect( +        self._connect_and_track(self.ui.btnRegister.clicked,              self._register) -        self.ui.rbExistingProvider.toggled.connect(self._skip_provider_checks) +        self._connect_and_track(self.ui.rbExistingProvider.toggled, +                                self._skip_provider_checks)          usernameRe = QtCore.QRegExp(USERNAME_REGEX)          self.ui.lblUser.setValidator( @@ -137,7 +141,19 @@ class Wizard(QtGui.QWizard):          self._provider_checks_ok = False          self._provider_setup_ok = False -        self.finished.connect(self._wizard_finished) +        self._connect_and_track(self.finished, self._wizard_finished) + +    def _connect_and_track(self, signal, method): +        """ +        Helper to connect signals and keep track of them. + +        :param signal: the signal to connect to. +        :type signal: QtCore.Signal +        :param method: the method to call when the signal is triggered. +        :type method: callable, Slot or Signal +        """ +        self._connected_signals.append((signal, method)) +        signal.connect(method)      @QtCore.Slot()      def _wizard_finished(self): @@ -153,28 +169,35 @@ class Wizard(QtGui.QWizard):          self._provider_setup_ok = False          self.ui.lnProvider.setText('')          self.ui.grpCheckProvider.setVisible(False) -        self._backend_disconnect() +        self._disconnect_tracked()      def _load_configured_providers(self):          """          Loads the configured providers into the wizard providers combo box.          """ +        self._backend.provider_get_pinned_providers() + +    def _load_configured_providers_with_pinned(self, pinned): +        """ +        Once we have the pinned providers from the backend, we +        continue setting everything up + +        :param pinned: list of pinned providers +        :type pinned: list of str +        """          ls = LeapSettings()          providers = ls.get_configured_providers() -        if not providers: +        if not providers and not pinned:              self.ui.rbExistingProvider.setEnabled(False)              self.ui.label_8.setEnabled(False)  # 'https://' label              self.ui.cbProviders.setEnabled(False)              return -        pinned = []          user_added = []          # separate pinned providers from user added ones          for p in providers: -            if ls.is_pinned_provider(p): -                pinned.append(p) -            else: +            if p not in pinned:                  user_added.append(p)          if user_added: @@ -191,6 +214,9 @@ class Wizard(QtGui.QWizard):          # 'Use existing provider' option.          self.ui.rbExistingProvider.setChecked(True) +        # We need to set it as complete explicitly +        self.page(self.INTRO_PAGE).set_completed() +      def get_domain(self):          return self._domain @@ -727,36 +753,30 @@ class Wizard(QtGui.QWizard):          Connects all the backend signals with the wizard.          """          sig = self._backend.signaler -        sig.prov_name_resolution.connect(self._name_resolution) -        sig.prov_https_connection.connect(self._https_connection) -        sig.prov_download_provider_info.connect(self._download_provider_info) -        sig.prov_get_details.connect(self._provider_get_details) +        conntrack = self._connect_and_track +        conntrack(sig.prov_name_resolution, self._name_resolution) +        conntrack(sig.prov_https_connection, self._https_connection) +        conntrack(sig.prov_download_provider_info, +                  self._download_provider_info) +        conntrack(sig.prov_get_details, self._provider_get_details) +        conntrack(sig.prov_get_pinned_providers, +                  self._load_configured_providers_with_pinned) -        sig.prov_download_ca_cert.connect(self._download_ca_cert) -        sig.prov_check_ca_fingerprint.connect(self._check_ca_fingerprint) -        sig.prov_check_api_certificate.connect(self._check_api_certificate) +        conntrack(sig.prov_download_ca_cert, self._download_ca_cert) +        conntrack(sig.prov_check_ca_fingerprint, self._check_ca_fingerprint) +        conntrack(sig.prov_check_api_certificate, self._check_api_certificate) -        sig.srp_registration_finished.connect(self._registration_finished) -        sig.srp_registration_failed.connect(self._registration_failed) -        sig.srp_registration_taken.connect(self._registration_taken) +        conntrack(sig.srp_registration_finished, self._registration_finished) +        conntrack(sig.srp_registration_failed, self._registration_failed) +        conntrack(sig.srp_registration_taken, self._registration_taken) -    def _backend_disconnect(self): +    def _disconnect_tracked(self):          """          This method is called when the wizard dialog is closed. -        We disconnect all the backend signals in here. +        We disconnect all the signals in here.          """ -        sig = self._backend.signaler -        try: -            # disconnect backend signals -            sig.prov_name_resolution.disconnect(self._name_resolution) -            sig.prov_https_connection.disconnect(self._https_connection) -            sig.prov_download_provider_info.disconnect( -                self._download_provider_info) - -            sig.prov_download_ca_cert.disconnect(self._download_ca_cert) -            sig.prov_check_ca_fingerprint.disconnect( -                self._check_ca_fingerprint) -            sig.prov_check_api_certificate.disconnect( -                self._check_api_certificate) -        except RuntimeError: -            pass  # Signal was not connected +        for signal, method in self._connected_signals: +            try: +                signal.disconnect(method) +            except RuntimeError: +                pass  # Signal was not connected diff --git a/src/leap/bitmask/provider/pinned.py b/src/leap/bitmask/provider/pinned.py new file mode 100644 index 00000000..d9b74c3d --- /dev/null +++ b/src/leap/bitmask/provider/pinned.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# pinned.py +# Copyright (C) 2013-2014 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/>. +""" +Pinned Providers +""" +import logging + +from leap.bitmask.provider import pinned_demobitmask + +logger = logging.getLogger(__name__) + + +class PinnedProviders(object): +    """ +    Represents the providers that are pinned in Bitmask +    """ + +    CONFIG_KEY = "config" +    CACERT_KEY = "cacert" + +    PROVIDERS = { +        pinned_demobitmask.DOMAIN: { +            CONFIG_KEY: pinned_demobitmask.PROVIDER_JSON, +            CACERT_KEY: pinned_demobitmask.CACERT_PEM, +        } +    } + +    def __init__(self): +        pass + +    @classmethod +    def domains(self): +        """ +        Return the domains that are pinned in here + +        :rtype: list of str +        """ +        return self.PROVIDERS.keys() + +    @classmethod +    def save_hardcoded(self, domain, provider_path, cacert_path): +        """ +        Save the pinned content for provider.json and cacert.pem to +        the specified paths + +        :param domain: domain of the pinned provider +        :type domain: str +        :param provider_path: path where the pinned provider.json will +                              be saved +        :type provider_path: str +        :param cacert_path: path where the pinned cacert.pem will be +                            saved +        :type cacert_path: str +        """ +        with open(provider_path, "w") as f: +            f.write(self.PROVIDERS[domain][self.CONFIG_KEY]) + +        with open(cacert_path, "w") as f: +            f.write(self.PROVIDERS[domain][self.CACERT_KEY]) diff --git a/src/leap/bitmask/provider/pinned_demobitmask.py b/src/leap/bitmask/provider/pinned_demobitmask.py new file mode 100644 index 00000000..9c699a4c --- /dev/null +++ b/src/leap/bitmask/provider/pinned_demobitmask.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# pinned_demobitmask.py +# Copyright (C) 2013-2014 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/>. +""" +Pinned provider.json and cacert.pem for demo.bitmask.net +""" + +DOMAIN = "demo.bitmask.net" + +PROVIDER_JSON = """ +{ +  "api_uri": "https://api.demo.bitmask.net:4430", +  "api_version": "1", +  "ca_cert_fingerprint": "SHA256: 0f17c033115f6b76ff67871872303ff65034efe7dd1b910062ca323eb4da5c7e", +  "ca_cert_uri": "https://demo.bitmask.net/ca.crt", +  "default_language": "en", +  "description": { +    "el": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.", +    "en": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.", +    "es": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted." +  }, +  "domain": "demo.bitmask.net", +  "enrollment_policy": "open", +  "languages": [ +    "en" +  ], +  "name": { +    "en": "Bitmask" +  }, +  "service": { +    "allow_anonymous": true, +    "allow_free": true, +    "allow_limited_bandwidth": false, +    "allow_paid": true, +    "allow_registration": true, +    "allow_unlimited_bandwidth": true, +    "bandwidth_limit": 102400, +    "default_service_level": 1, +    "levels": [ +      { +        "id": 1, +        "name": "free", +        "storage": 50 +      }, +      { +        "id": 2, +        "name": "basic", +        "rate": [ +          "US$10", +          "\u20ac10" +        ], +        "storage": 1000 +      }, +      { +        "id": 3, +        "name": "pro", +        "rate": [ +          "US$20", +          "\u20ac20" +        ], +        "storage": 10000 +      } +    ] +  }, +  "services": [ +    "openvpn" +  ] +} +""" + +CACERT_PEM = """-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt +YXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v +Yml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw +FgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV +BAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai +dHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB +7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r/bEHUSevmR09BRp86syHZerdNGpXYhcQ84 +CA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+ +znCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe/RGk4 +MEqGFuOzrtsgEhPIX0hplhb0Tgz/rtug+yTT7oJjBa3u20AAOQ38/M99EfdeJvc4 +lPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu/qt3DfvLfhboq+0 +bQvLUPXrVDr70onv5UDjpmEA/cLmaIqqrduuTkFZOym65/PfAPvpGnt7crQj/Ibl +DEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB +lfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy +YMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw +XjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH/BAQDAgIE +MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w +DQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl +cXYyB6hY5hv/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY +k/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj +RnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG +htD/JKwt6xBmNwktH0GI/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX +EIQ0ZR56bFuJA/CwValBqV/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J +aF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l +mlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK +G6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co +Ja8zlx64jmMZPg/t3wWqkZgXZ14qnbyG5/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d +69db12/g4f6phldhxiWuGC/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e +yV8e +-----END CERTIFICATE-----""" diff --git a/src/leap/bitmask/provider/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py index 6cdfe4f4..8c96a8b5 100644 --- a/src/leap/bitmask/provider/providerbootstrapper.py +++ b/src/leap/bitmask/provider/providerbootstrapper.py @@ -29,6 +29,7 @@ from leap.bitmask import util  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.provider.pinned import PinnedProviders  from leap.bitmask.services.abstractbootstrapper import AbstractBootstrapper  from leap.bitmask.util.constants import REQUEST_TIMEOUT  from leap.bitmask.util.request_helpers import get_content @@ -176,6 +177,14 @@ class ProviderBootstrapper(AbstractBootstrapper):          provider_json = os.path.join(util.get_path_prefix(),                                       get_provider_path(domain)) +        if domain in PinnedProviders.domains() and \ +           not os.path.exists(provider_json): +            mkdir_p(os.path.join(os.path.dirname(provider_json), +                                 "keys", "ca")) +            cacert = os.path.join(os.path.dirname(provider_json), +                                  "keys", "ca", "cacert.pem") +            PinnedProviders.save_hardcoded(domain, provider_json, cacert) +          mtime = get_mtime(provider_json)          if self._download_if_needed and mtime: | 
