diff options
Diffstat (limited to 'src/leap/bitmask/backend.py')
-rw-r--r-- | src/leap/bitmask/backend.py | 146 |
1 files changed, 127 insertions, 19 deletions
diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index 8a289a79..45ea451c 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -18,8 +18,8 @@ Backend for everything """ import logging -import os +from functools import partial from Queue import Queue, Empty from twisted.internet import threads, defer @@ -29,6 +29,8 @@ from twisted.python import log import zope.interface from leap.bitmask.config.providerconfig import ProviderConfig +from leap.bitmask.crypto.srpregister import SRPRegister +from leap.bitmask.provider import get_provider_path from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper # Frontend side @@ -134,6 +136,9 @@ class Provider(object): :param provider: URL for the provider :type provider: unicode + + :returns: the defer for the operation running in a thread. + :rtype: twisted.internet.defer.Deferred """ log.msg("Setting up provider %s..." % (provider.encode("idna"),)) pb = self._provider_bootstrapper @@ -141,23 +146,31 @@ class Provider(object): self._download_provider_defer = d return d + def cancel_setup_provider(self): + """ + Cancel the ongoing setup provider defer (if any). + """ + d = self._download_provider_defer + if d is not None: + d.cancel() + def bootstrap(self, provider): """ Second stage of bootstrapping for a provider. :param provider: URL for the provider :type provider: unicode - """ + :returns: the defer for the operation running in a thread. + :rtype: twisted.internet.defer.Deferred + """ d = None # If there's no loaded provider or # we want to connect to other provider... if (not self._provider_config.loaded() or self._provider_config.get_domain() != provider): - self._provider_config.load( - os.path.join("leap", "providers", - provider, "provider.json")) + self._provider_config.load(get_provider_path(provider)) if self._provider_config.loaded(): d = self._provider_bootstrapper.run_provider_setup_checks( @@ -174,6 +187,57 @@ class Provider(object): return d +class Register(object): + """ + Interfaces with setup and bootstrapping operations for a provider + """ + + zope.interface.implements(ILEAPComponent) + + def __init__(self, signaler=None): + """ + Constructor for the Register component + + :param signaler: Object in charge of handling communication + back to the frontend + :type signaler: Signaler + """ + object.__init__(self) + self.key = "register" + self._signaler = signaler + self._provider_config = ProviderConfig() + + def register_user(self, domain, username, password): + """ + Register a user using the domain and password given as parameters. + + :param domain: the domain we need to register the user. + :type domain: unicode + :param username: the user name + :type username: unicode + :param password: the password for the username + :type password: unicode + + :returns: the defer for the operation running in a thread. + :rtype: twisted.internet.defer.Deferred + """ + # If there's no loaded provider or + # we want to connect to other provider... + if (not self._provider_config.loaded() or + self._provider_config.get_domain() != domain): + self._provider_config.load(get_provider_path(domain)) + + if self._provider_config.loaded(): + srpregister = SRPRegister(signaler=self._signaler, + provider_config=self._provider_config) + return threads.deferToThread( + partial(srpregister.register_user, username, password)) + else: + if self._signaler is not None: + self._signaler.signal(self._signaler.srp_registration_failed) + logger.error("Could not load provider configuration.") + + class Signaler(QtCore.QObject): """ Signaler object, handles converting string commands to Qt signals. @@ -182,8 +246,9 @@ class Signaler(QtCore.QObject): live in the frontend. """ - # Signals for the ProviderBootstrapper + #################### # These will only exist in the frontend + # Signals for the ProviderBootstrapper prov_name_resolution = QtCore.Signal(object) prov_https_connection = QtCore.Signal(object) prov_download_provider_info = QtCore.Signal(object) @@ -194,7 +259,18 @@ class Signaler(QtCore.QObject): prov_problem_with_provider = QtCore.Signal(object) - # These will exist both in the backend and the front end. + prov_unsupported_client = QtCore.Signal(object) + prov_unsupported_api = QtCore.Signal(object) + + prov_cancelled_setup = QtCore.Signal(object) + + # Signals for SRPRegister + srp_registration_finished = QtCore.Signal(object) + srp_registration_failed = QtCore.Signal(object) + srp_registration_taken = QtCore.Signal(object) + + #################### + # These will exist both in the backend AND the front end. # The frontend might choose to not "interpret" all the signals # from the backend, but the backend needs to have all the signals # it's going to emit defined here @@ -204,7 +280,14 @@ class Signaler(QtCore.QObject): PROV_DOWNLOAD_CA_CERT_KEY = "prov_download_ca_cert" PROV_CHECK_CA_FINGERPRINT_KEY = "prov_check_ca_fingerprint" PROV_CHECK_API_CERTIFICATE_KEY = "prov_check_api_certificate" - PROV_PROV_PROBLEM_WITH_PROVIER_KEY = "prov_problem_with_provider" + PROV_PROBLEM_WITH_PROVIDER_KEY = "prov_problem_with_provider" + PROV_UNSUPPORTED_CLIENT = "prov_unsupported_client" + PROV_UNSUPPORTED_API = "prov_unsupported_api" + PROV_CANCELLED_SETUP = "prov_cancelled_setup" + + SRP_REGISTRATION_FINISHED = "srp_registration_finished" + SRP_REGISTRATION_FAILED = "srp_registration_failed" + SRP_REGISTRATION_TAKEN = "srp_registration_taken" def __init__(self): """ @@ -220,7 +303,14 @@ class Signaler(QtCore.QObject): self.PROV_DOWNLOAD_CA_CERT_KEY, self.PROV_CHECK_CA_FINGERPRINT_KEY, self.PROV_CHECK_API_CERTIFICATE_KEY, - self.PROV_PROV_PROBLEM_WITH_PROVIER_KEY + self.PROV_PROBLEM_WITH_PROVIDER_KEY, + self.PROV_UNSUPPORTED_CLIENT, + self.PROV_UNSUPPORTED_API, + self.PROV_CANCELLED_SETUP, + + self.SRP_REGISTRATION_FINISHED, + self.SRP_REGISTRATION_FAILED, + self.SRP_REGISTRATION_TAKEN, ] for sig in signals: @@ -243,6 +333,11 @@ class Signaler(QtCore.QObject): # will do zmq.send_multipart, and the frontend version will be # similar to this log.msg("Signaling %s :: %s" % (key, data)) + + # for some reason emitting 'None' gives a segmentation fault. + if data is None: + data = '' + try: self._signals[key].emit(data) except KeyError: @@ -274,6 +369,7 @@ class Backend(object): # Component registration self._register(Provider(self._signaler, bypass_checks)) + self._register(Register(self._signaler)) # We have a looping call on a thread executing all the # commands in queue. Right now this queue is an actual Queue @@ -303,7 +399,8 @@ class Backend(object): Stops the looping call and tries to cancel all the defers. """ log.msg("Stopping worker...") - self._lc.stop() + if self._lc.running: + self._lc.stop() while len(self._ongoing_defers) > 0: d = self._ongoing_defers.pop() d.cancel() @@ -345,17 +442,20 @@ class Backend(object): # cmd is: component, method, signalback, *args func = getattr(self._components[cmd[0]], cmd[1]) d = func(*cmd[3:]) - # A call might not have a callback signal, but if it does, - # we add it to the chain - if cmd[2] is not None: - d.addCallbacks(self._signal_back, log.err, cmd[2]) - d.addCallbacks(self._done_action, log.err, - callbackKeywords={"d": d}) - d.addErrback(log.err) - self._ongoing_defers.append(d) + if d is not None: # d may be None if a defer chain is cancelled. + # A call might not have a callback signal, but if it does, + # we add it to the chain + if cmd[2] is not None: + d.addCallbacks(self._signal_back, log.err, cmd[2]) + d.addCallbacks(self._done_action, log.err, + callbackKeywords={"d": d}) + d.addErrback(log.err) + self._ongoing_defers.append(d) except Empty: # If it's just empty we don't have anything to do. pass + except defer.CancelledError: + logger.debug("defer cancelled somewhere (CancelledError).") except Exception: # But we log the rest log.err() @@ -367,7 +467,8 @@ class Backend(object): :param d: defer to remove :type d: twisted.internet.defer.Deferred """ - self._ongoing_defers.remove(d) + if d in self._ongoing_defers: + self._ongoing_defers.remove(d) # XXX: Temporal interface until we migrate to zmq # We simulate the calls to zmq.send_multipart. Once we separate @@ -377,5 +478,12 @@ class Backend(object): def setup_provider(self, provider): self._call_queue.put(("provider", "setup_provider", None, provider)) + def cancel_setup_provider(self): + self._call_queue.put(("provider", "cancel_setup_provider", None)) + def provider_bootstrap(self, provider): self._call_queue.put(("provider", "bootstrap", None, provider)) + + def register_user(self, provider, username, password): + self._call_queue.put(("register", "register_user", None, provider, + username, password)) |