diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/bitmask/backend.py | 199 | ||||
| -rw-r--r-- | src/leap/bitmask/crypto/srpauth.py | 139 | ||||
| -rw-r--r-- | src/leap/bitmask/crypto/srpregister.py | 2 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 121 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 150 | 
5 files changed, 466 insertions, 145 deletions
| diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index 3c9991be..d78a9a6a 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -29,6 +29,7 @@ from twisted.python import log  import zope.interface  from leap.bitmask.config.providerconfig import ProviderConfig +from leap.bitmask.crypto.srpauth import SRPAuth  from leap.bitmask.crypto.srpregister import SRPRegister  from leap.bitmask.provider import get_provider_path  from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper @@ -149,6 +150,7 @@ class Provider(object):          self._provider_bootstrapper = ProviderBootstrapper(signaler,                                                             bypass_checks)          self._download_provider_defer = None +        self._provider_config = ProviderConfig()      def setup_provider(self, provider):          """ @@ -186,7 +188,7 @@ class Provider(object):          """          d = None -        config = ProviderConfig() +        config = self._provider_config          if get_provider_config(config, provider):              d = self._provider_bootstrapper.run_provider_setup_checks(                  self._provider_config, @@ -247,6 +249,113 @@ class Register(object):              logger.error("Could not load provider configuration.") +class Authenticate(object): +    """ +    Interfaces with setup and bootstrapping operations for a provider +    """ + +    zope.interface.implements(ILEAPComponent) + +    def __init__(self, signaler=None): +        """ +        Constructor for the Authenticate component + +        :param signaler: Object in charge of handling communication +                         back to the frontend +        :type signaler: Signaler +        """ +        object.__init__(self) +        self.key = "authenticate" +        self._signaler = signaler +        self._srp_auth = None + +    def login(self, domain, username, password): +        """ +        Executes the whole authentication process for a user + +        :param domain: the domain where we need to authenticate. +        :type domain: unicode +        :param username: username for this session +        :type username: str +        :param password: password for this user +        :type password: str + +        :returns: the defer for the operation running in a thread. +        :rtype: twisted.internet.defer.Deferred +        """ +        config = ProviderConfig() +        if get_provider_config(config, domain): +            self._srp_auth = SRPAuth(config, self._signaler) +            self._login_defer = self._srp_auth.authenticate(username, password) +            return self._login_defer +        else: +            if self._signaler is not None: +                self._signaler.signal(self._signaler.srp_auth_error) +            logger.error("Could not load provider configuration.") + +    def cancel_login(self): +        """ +        Cancel the ongoing login defer (if any). +        """ +        d = self._login_defer +        if d is not None: +            d.cancel() + +    def change_password(self, current_password, new_password): +        """ +        Changes the user's password. + +        :param current_password: the current password of the user. +        :type current_password: str +        :param new_password: the new password for the user. +        :type new_password: str + +        :returns: a defer to interact with. +        :rtype: twisted.internet.defer.Deferred +        """ +        if not self._is_logged_in(): +            if self._signaler is not None: +                self._signaler.signal(self._signaler.SRP_NOT_LOGGED_IN_ERROR) +            return + +        return self._srp_auth.change_password(current_password, new_password) + +    def logout(self): +        """ +        Logs out the current session. +        Expects a session_id to exists, might raise AssertionError +        """ +        if not self._is_logged_in(): +            if self._signaler is not None: +                self._signaler.signal(self._signaler.SRP_NOT_LOGGED_IN_ERROR) +            return + +        self._srp_auth.logout() + +    def _is_logged_in(self): +        """ +        Return whether the user is logged in or not. + +        :rtype: bool +        """ +        return self._srp_auth.is_authenticated() + +    def get_logged_in_status(self): +        """ +        Signals if the user is currently logged in or not. +        """ +        if self._signaler is None: +            return + +        signal = None +        if self._is_logged_in(): +            signal = self._signaler.SRP_STATUS_LOGGED_IN +        else: +            signal = self._signaler.SRP_STATUS_NOT_LOGGED_IN + +        self._signaler.signal(signal) + +  class EIP(object):      """      Interfaces with setup and launch of EIP @@ -338,6 +447,21 @@ class Signaler(QtCore.QObject):      eip_cancelled_setup = QtCore.Signal(object) +    # Signals for SRPAuth +    srp_auth_ok = QtCore.Signal(object) +    srp_auth_error = QtCore.Signal(object) +    srp_auth_server_error = QtCore.Signal(object) +    srp_auth_connection_error = QtCore.Signal(object) +    srp_auth_bad_user_or_password = QtCore.Signal(object) +    srp_logout_ok = QtCore.Signal(object) +    srp_logout_error = QtCore.Signal(object) +    srp_password_change_ok = QtCore.Signal(object) +    srp_password_change_error = QtCore.Signal(object) +    srp_password_change_badpw = QtCore.Signal(object) +    srp_not_logged_in_error = QtCore.Signal(object) +    srp_status_logged_in = QtCore.Signal(object) +    srp_status_not_logged_in = QtCore.Signal(object) +      ####################      # These will exist both in the backend AND the front end.      # The frontend might choose to not "interpret" all the signals @@ -357,6 +481,19 @@ class Signaler(QtCore.QObject):      SRP_REGISTRATION_FINISHED = "srp_registration_finished"      SRP_REGISTRATION_FAILED = "srp_registration_failed"      SRP_REGISTRATION_TAKEN = "srp_registration_taken" +    SRP_AUTH_OK = "srp_auth_ok" +    SRP_AUTH_ERROR = "srp_auth_error" +    SRP_AUTH_SERVER_ERROR = "srp_auth_server_error" +    SRP_AUTH_CONNECTION_ERROR = "srp_auth_connection_error" +    SRP_AUTH_BAD_USER_OR_PASSWORD = "srp_auth_bad_user_or_password" +    SRP_LOGOUT_OK = "srp_logout_ok" +    SRP_LOGOUT_ERROR = "srp_logout_error" +    SRP_PASSWORD_CHANGE_OK = "srp_password_change_ok" +    SRP_PASSWORD_CHANGE_ERROR = "srp_password_change_error" +    SRP_PASSWORD_CHANGE_BADPW = "srp_password_change_badpw" +    SRP_NOT_LOGGED_IN_ERROR = "srp_not_logged_in_error" +    SRP_STATUS_LOGGED_IN = "srp_status_logged_in" +    SRP_STATUS_NOT_LOGGED_IN = "srp_status_not_logged_in"      # TODO change the name of "download_config" signal to      # something less confusing (config_ready maybe) @@ -396,6 +533,20 @@ class Signaler(QtCore.QObject):              self.EIP_DOWNLOAD_CONFIG,              self.EIP_DOWNLOAD_CLIENT_CERTIFICATE,              self.EIP_CANCELLED_SETUP, + +            self.SRP_AUTH_OK, +            self.SRP_AUTH_ERROR, +            self.SRP_AUTH_SERVER_ERROR, +            self.SRP_AUTH_CONNECTION_ERROR, +            self.SRP_AUTH_BAD_USER_OR_PASSWORD, +            self.SRP_LOGOUT_OK, +            self.SRP_LOGOUT_ERROR, +            self.SRP_PASSWORD_CHANGE_OK, +            self.SRP_PASSWORD_CHANGE_ERROR, +            self.SRP_PASSWORD_CHANGE_BADPW, +            self.SRP_NOT_LOGGED_IN_ERROR, +            self.SRP_STATUS_LOGGED_IN, +            self.SRP_STATUS_NOT_LOGGED_IN,          ]          for sig in signals: @@ -455,6 +606,7 @@ class Backend(object):          # Component registration          self._register(Provider(self._signaler, bypass_checks))          self._register(Register(self._signaler)) +        self._register(Authenticate(self._signaler))          self._register(EIP(self._signaler))          # We have a looping call on a thread executing all the @@ -579,3 +731,48 @@ class Backend(object):      def cancel_setup_eip(self):          self._call_queue.put(("eip", "cancel_setup_eip", None)) + +    def login(self, provider, username, password): +        self._call_queue.put(("authenticate", "login", None, provider, +                              username, password)) + +    def logout(self): +        self._call_queue.put(("authenticate", "logout", None)) + +    def cancel_login(self): +        self._call_queue.put(("authenticate", "cancel_login", None)) + +    def change_password(self, current_password, new_password): +        self._call_queue.put(("authenticate", "change_password", None, +                              current_password, new_password)) + +    def get_logged_in_status(self): +        self._call_queue.put(("authenticate", "get_logged_in_status", None)) + +    ########################################################################### +    # XXX HACK: this section is meant to be a place to hold methods and +    # variables needed in the meantime while we migrate all to the backend. + +    def srpauth_get_username(self): +        srp_auth = self._components["authenticate"]._srp_auth +        if srp_auth is not None: +            return srp_auth.get_username() + +    def srpauth_get_session_id(self): +        srp_auth = self._components["authenticate"]._srp_auth +        if srp_auth is not None: +            return srp_auth.get_session_id() + +    def srpauth_get_uuid(self): +        srp_auth = self._components["authenticate"]._srp_auth +        if srp_auth is not None: +            return srp_auth.get_uuid() + +    def srpauth_get_token(self): +        srp_auth = self._components["authenticate"]._srp_auth +        if srp_auth is not None: +            return srp_auth.get_token() + +    def get_provider_config(self): +        provider_config = self._components["provider"]._provider_config +        return provider_config diff --git a/src/leap/bitmask/crypto/srpauth.py b/src/leap/bitmask/crypto/srpauth.py index 7cf7e55a..26a2d09a 100644 --- a/src/leap/bitmask/crypto/srpauth.py +++ b/src/leap/bitmask/crypto/srpauth.py @@ -30,6 +30,7 @@ from requests.adapters import HTTPAdapter  from PySide import QtCore  from twisted.internet import threads +from twisted.internet.defer import CancelledError  from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.util import request_helpers as reqhelper @@ -135,12 +136,15 @@ class SRPAuth(QtCore.QObject):          USER_SALT_KEY = 'user[password_salt]'          AUTHORIZATION_KEY = "Authorization" -        def __init__(self, provider_config): +        def __init__(self, provider_config, signaler=None):              """              Constructor for SRPAuth implementation -            :param server: Server to which we will authenticate -            :type server: str +            :param provider_config: ProviderConfig needed to authenticate. +            :type provider_config: ProviderConfig +            :param signaler: Signaler object used to receive notifications +                            from the backend +            :type signaler: Signaler              """              QtCore.QObject.__init__(self) @@ -148,6 +152,7 @@ class SRPAuth(QtCore.QObject):                          "We need a provider config to authenticate")              self._provider_config = provider_config +            self._signaler = signaler              self._settings = LeapSettings()              # **************************************************** # @@ -448,7 +453,7 @@ class SRPAuth(QtCore.QObject):          def _threader(self, cb, res, *args, **kwargs):              return threads.deferToThread(cb, res, *args, **kwargs) -        def change_password(self, current_password, new_password): +        def _change_password(self, current_password, new_password):              """              Changes the password for the currently logged user if the current              password match. @@ -499,6 +504,43 @@ class SRPAuth(QtCore.QObject):              self._password = new_password +        def change_password(self, current_password, new_password): +            """ +            Changes the password for the currently logged user if the current +            password match. +            It requires to be authenticated. + +            :param current_password: the current password for the logged user. +            :type current_password: str +            :param new_password: the new password for the user +            :type new_password: str +            """ +            d = threads.deferToThread( +                self._change_password, current_password, new_password) +            d.addCallback(self._change_password_ok) +            d.addErrback(self._change_password_error) + +        def _change_password_ok(self, _): +            """ +            Password change callback. +            """ +            if self._signaler is not None: +                self._signaler.signal(self._signaler.SRP_PASSWORD_CHANGE_OK) + +        def _change_password_error(self, failure): +            """ +            Password change errback. +            """ +            logger.debug( +                "Error changing password. Failure: {0}".format(failure)) +            if self._signaler is None: +                return + +            if failure.check(SRPAuthBadUserOrPassword): +                self._signaler.signal(self._signaler.SRP_PASSWORD_CHANGE_BADPW) +            else: +                self._signaler.signal(self._signaler.SRP_PASSWORD_CHANGE_ERROR) +          def authenticate(self, username, password):              """              Executes the whole authentication process for a user @@ -539,8 +581,49 @@ class SRPAuth(QtCore.QObject):              d.addCallback(partial(self._threader,                                    self._verify_session)) +            d.addCallback(self._authenticate_ok) +            d.addErrback(self._authenticate_error)              return d +        def _authenticate_ok(self, _): +            """ +            Callback that notifies that the authentication was successful. + +            :param _: IGNORED, output from the previous callback (None) +            :type _: IGNORED +            """ +            logger.debug("Successful login!") +            self._signaler.signal(self._signaler.SRP_AUTH_OK) + +        def _authenticate_error(self, failure): +            """ +            Error handler for the srpauth.authenticate method. + +            :param failure: failure object that Twisted generates +            :type failure: twisted.python.failure.Failure +            """ +            logger.error("Error logging in, {0!r}".format(failure)) + +            signal = None +            if failure.check(CancelledError): +                logger.debug("Defer cancelled.") +                failure.trap(Exception) +                return + +            if self._signaler is None: +                return + +            if failure.check(SRPAuthBadUserOrPassword): +                signal = self._signaler.SRP_AUTH_BAD_USER_OR_PASSWORD +            elif failure.check(SRPAuthConnectionError): +                signal = self._signaler.SRP_AUTH_CONNECTION_ERROR +            elif failure.check(SRPAuthenticationError): +                signal = self._signaler.SRP_AUTH_SERVER_ERROR +            else: +                signal = self._signaler.SRP_AUTH_ERROR + +            self._signaler.signal(signal) +          def logout(self):              """              Logs out the current session. @@ -565,6 +648,8 @@ class SRPAuth(QtCore.QObject):              except Exception as e:                  logger.warning("Something went wrong with the logout: %r" %                                 (e,)) +                if self._signaler is not None: +                    self._signaler.signal(self._signaler.SRP_LOGOUT_ERROR)                  raise              else:                  self.set_session_id(None) @@ -573,6 +658,8 @@ class SRPAuth(QtCore.QObject):                  # Also reset the session                  self._session = self._fetcher.session()                  logger.debug("Successfully logged out.") +                if self._signaler is not None: +                    self._signaler.signal(self._signaler.SRP_LOGOUT_OK)          def set_session_id(self, session_id):              QtCore.QMutexLocker(self._session_id_lock) @@ -604,20 +691,22 @@ class SRPAuth(QtCore.QObject):      __instance = None -    authentication_finished = QtCore.Signal() -    logout_ok = QtCore.Signal() -    logout_error = QtCore.Signal() - -    def __init__(self, provider_config): +    def __init__(self, provider_config, signaler=None):          """ -        Creates a singleton instance if needed +        Create a singleton instance if needed + +        :param provider_config: ProviderConfig needed to authenticate. +        :type provider_config: ProviderConfig +        :param signaler: Signaler object used to send notifications +                         from the backend +        :type signaler: Signaler          """          QtCore.QObject.__init__(self)          # Check whether we already have an instance          if SRPAuth.__instance is None:              # Create and remember instance -            SRPAuth.__instance = SRPAuth.__impl(provider_config) +            SRPAuth.__instance = SRPAuth.__impl(provider_config, signaler)          # Store instance reference as the only member in the handle          self.__dict__['_SRPAuth__instance'] = SRPAuth.__instance @@ -642,9 +731,20 @@ class SRPAuth(QtCore.QObject):          """          username = username.lower()          d = self.__instance.authenticate(username, password) -        d.addCallback(self._gui_notify)          return d +    def is_authenticated(self): +        """ +        Return whether the user is authenticated or not. + +        :rtype: bool +        """ +        user = self.__instance._srp_user +        if user is not None: +            return self.__instance.authenticated() + +        return False +      def change_password(self, current_password, new_password):          """          Changes the user's password. @@ -657,8 +757,7 @@ class SRPAuth(QtCore.QObject):          :returns: a defer to interact with.          :rtype: twisted.internet.defer.Deferred          """ -        d = threads.deferToThread( -            self.__instance.change_password, current_password, new_password) +        d = self.__instance.change_password(current_password, new_password)          return d      def get_username(self): @@ -672,16 +771,6 @@ class SRPAuth(QtCore.QObject):              return None          return self.__instance._username -    def _gui_notify(self, _): -        """ -        Callback that notifies the UI with the proper signal. - -        :param _: IGNORED, output from the previous callback (None) -        :type _: IGNORED -        """ -        logger.debug("Successful login!") -        self.authentication_finished.emit() -      def get_session_id(self):          return self.__instance.get_session_id() @@ -699,9 +788,7 @@ class SRPAuth(QtCore.QObject):          try:              self.__instance.logout()              logger.debug("Logout success") -            self.logout_ok.emit()              return True          except Exception as e:              logger.debug("Logout error: {0!r}".format(e)) -            self.logout_error.emit()          return False diff --git a/src/leap/bitmask/crypto/srpregister.py b/src/leap/bitmask/crypto/srpregister.py index 4c52db42..f03dc469 100644 --- a/src/leap/bitmask/crypto/srpregister.py +++ b/src/leap/bitmask/crypto/srpregister.py @@ -46,8 +46,6 @@ class SRPRegister(QtCore.QObject):      STATUS_TAKEN = 422      STATUS_ERROR = -999  # Custom error status -    registration_finished = QtCore.Signal(bool, object) -      def __init__(self, signaler=None,                   provider_config=None, register_path="users"):          """ diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 61aff5f9..492ea125 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -20,13 +20,13 @@ Main window for Bitmask.  import logging  import socket +from functools import partial  from threading import Condition  from datetime import datetime  from PySide import QtCore, QtGui  from zope.proxy import ProxyBase, setProxiedObject  from twisted.internet import reactor, threads -from twisted.internet.defer import CancelledError  from leap.bitmask import __version__ as VERSION  from leap.bitmask import __version_hash__ as VERSION_HASH @@ -34,9 +34,6 @@ from leap.bitmask.config import flags  from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.config.providerconfig import ProviderConfig -from leap.bitmask.crypto import srpauth -from leap.bitmask.crypto.srpauth import SRPAuth -  from leap.bitmask.gui.loggerwindow import LoggerWindow  from leap.bitmask.gui.advanced_key_management import AdvancedKeyManagement  from leap.bitmask.gui.login import LoginWidget @@ -213,8 +210,8 @@ class MainWindow(QtGui.QMainWindow):          # than once          # XXX HACK!! But we need it as long as we are using          # provider_config in here -        self._provider_config = ( -            self._backend._components["provider"]._provider_config) +        self._provider_config = self._backend.get_provider_config() +          # Used for automatic start of EIP          self._provisional_provider_config = ProviderConfig()          self._eip_config = eipconfig.EIPConfig() @@ -342,7 +339,6 @@ class MainWindow(QtGui.QMainWindow):          self._soledad = ProxyBase(None)          self._keymanager = ProxyBase(None) -        self._login_defer = None          self._soledad_defer = None          self._mail_conductor = mail_conductor.MailConductor( @@ -377,6 +373,18 @@ class MainWindow(QtGui.QMainWindow):              # so this has to be done after eip_machine is started              self._finish_init() +    def _not_logged_in_error(self): +        """ +        Handle the 'not logged in' backend error if we try to do an operation +        that requires to be logged in. +        """ +        logger.critical("You are trying to do an operation that requires " +                        "log in first.") +        QtGui.QMessageBox.critical( +            self, self.tr("Application error"), +            self.tr("You are trying to do an operation " +                    "that requires logging in first.")) +      def _backend_connect(self):          """          Helper to connect to backend signals @@ -401,6 +409,34 @@ class MainWindow(QtGui.QMainWindow):          sig.eip_download_config.connect(self._eip_intermediate_stage)          sig.eip_download_client_certificate.connect(self._finish_eip_bootstrap) +        # Authentication related signals +        sig.srp_auth_ok.connect(self._authentication_finished) + +        auth_error = partial( +            self._authentication_error, +            self.tr("Unknown error.")) +        sig.srp_auth_error.connect(auth_error) + +        auth_server_error = partial( +            self._authentication_error, +            self.tr("There was a server problem with authentication.")) +        sig.srp_auth_server_error.connect(auth_server_error) + +        auth_connection_error = partial( +            self._authentication_error, +            self.tr("Could not establish a connection.")) +        sig.srp_auth_connection_error.connect(auth_connection_error) + +        auth_bad_user_or_password = partial( +            self._authentication_error, +            self.tr("Invalid username or password.")) +        sig.srp_auth_bad_user_or_password.connect(auth_bad_user_or_password) + +        sig.srp_logout_ok.connect(self._logout_ok) +        sig.srp_logout_error.connect(self._logout_error) + +        sig.srp_not_logged_in_error.connect(self._not_logged_in_error) +      def _backend_disconnect(self):          """          Helper to disconnect from backend signals. @@ -538,7 +574,7 @@ class MainWindow(QtGui.QMainWindow):          Displays the preferences window.          """          preferences = PreferencesWindow( -            self, self._srp_auth, self._provider_config, self._soledad, +            self, self._backend, self._provider_config, self._soledad,              self._login_widget.get_selected_provider())          self.soledad_ready.connect(preferences.set_soledad_ready) @@ -1050,39 +1086,20 @@ class MainWindow(QtGui.QMainWindow):              if self._login_widget.start_login():                  self._download_provider_config() -    def _login_errback(self, failure): -        """ -        Error handler for the srpauth.authenticate method. - -        :param failure: failure object that Twisted generates -        :type failure: twisted.python.failure.Failure +    def _authentication_error(self, msg):          """ -        # NOTE: this behavior needs to be managed through the signaler, -        # as we are doing with the prov_cancelled_setup signal. -        # After we move srpauth to the backend, we need to update this. -        logger.error("Error logging in, {0!r}".format(failure)) +        SLOT +        TRIGGERS: +            Signaler.srp_auth_error +            Signaler.srp_auth_server_error +            Signaler.srp_auth_connection_error +            Signaler.srp_auth_bad_user_or_password -        if failure.check(CancelledError): -            logger.debug("Defer cancelled.") -            failure.trap(Exception) -            self._set_login_cancelled() -            return -        elif failure.check(srpauth.SRPAuthBadUserOrPassword): -            msg = self.tr("Invalid username or password.") -        elif failure.check(srpauth.SRPAuthBadStatusCode, -                           srpauth.SRPAuthenticationError, -                           srpauth.SRPAuthVerificationFailed, -                           srpauth.SRPAuthNoSessionId, -                           srpauth.SRPAuthNoSalt, srpauth.SRPAuthNoB, -                           srpauth.SRPAuthBadDataFromServer, -                           srpauth.SRPAuthJSONDecodeError): -            msg = self.tr("There was a server problem with authentication.") -        elif failure.check(srpauth.SRPAuthConnectionError): -            msg = self.tr("Could not establish a connection.") -        else: -            # this shouldn't happen, but just in case. -            msg = self.tr("Unknown error: {0!r}".format(failure.value)) +        Handle the authentication errors. +        :param msg: the message to show to the user. +        :type msg: unicode +        """          self._login_widget.set_status(msg)          self._login_widget.set_enabled(True) @@ -1101,12 +1118,9 @@ class MainWindow(QtGui.QMainWindow):          """          Cancel the running defers to avoid app blocking.          """ +        # XXX: Should we stop all the backend defers?          self._backend.cancel_setup_provider() - -        if self._login_defer is not None: -            logger.debug("Cancelling login defer.") -            self._login_defer.cancel() -            self._login_defer = None +        self._backend.cancel_login()          if self._soledad_defer is not None:              logger.debug("Cancelling soledad defer.") @@ -1142,15 +1156,8 @@ class MainWindow(QtGui.QMainWindow):              self._hide_unsupported_services() -            if self._srp_auth is None: -                self._srp_auth = SRPAuth(self._provider_config) -                self._srp_auth.authentication_finished.connect( -                    self._authentication_finished) -                self._srp_auth.logout_ok.connect(self._logout_ok) -                self._srp_auth.logout_error.connect(self._logout_error) - -            self._login_defer = self._srp_auth.authenticate(username, password) -            self._login_defer.addErrback(self._login_errback) +            domain = self._provider_config.get_domain() +            self._backend.login(domain, username, password)          else:              self._login_widget.set_status(                  "Unable to login: Problem with provider") @@ -1172,7 +1179,6 @@ class MainWindow(QtGui.QMainWindow):          domain = self._provider_config.get_domain()          full_user_id = make_address(user, domain)          self._mail_conductor.userid = full_user_id -        self._login_defer = None          self._start_eip_bootstrap()          # if soledad/mail is enabled: @@ -1916,7 +1922,7 @@ class MainWindow(QtGui.QMainWindow):          # XXX: If other defers are doing authenticated stuff, this          # might conflict with those. CHECK! -        threads.deferToThread(self._srp_auth.logout) +        self._backend.logout()          self.logout.emit()      def _logout_error(self): @@ -2017,11 +2023,8 @@ class MainWindow(QtGui.QMainWindow):          self._stop_imap_service() -        if self._srp_auth is not None: -            if self._srp_auth.get_session_id() is not None or \ -               self._srp_auth.get_token() is not None: -                # XXX this can timeout after loong time: See #3368 -                self._srp_auth.logout() +        if self._logged_user is not None: +            self._backend.logout()          if self._soledad_bootstrapper.soledad is not None:              logger.debug("Closing soledad...") diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index b2cc2236..f6bd1ed3 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -29,7 +29,6 @@ from leap.bitmask.provider import get_provider_path  from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.gui.ui_preferences import Ui_Preferences  from leap.soledad.client import NoStorageSecret -from leap.bitmask.crypto.srpauth import SRPAuthBadUserOrPassword  from leap.bitmask.util.password import basic_password_checks  from leap.bitmask.services import get_supported  from leap.bitmask.config.providerconfig import ProviderConfig @@ -44,12 +43,12 @@ class PreferencesWindow(QtGui.QDialog):      """      preferences_saved = QtCore.Signal() -    def __init__(self, parent, srp_auth, provider_config, soledad, domain): +    def __init__(self, parent, backend, provider_config, soledad, domain):          """          :param parent: parent object of the PreferencesWindow.          :parent type: QWidget -        :param srp_auth: SRPAuth object configured in the main app. -        :type srp_auth: SRPAuth +        :param backend: Backend being used +        :type backend: Backend          :param provider_config: ProviderConfig object.          :type provider_config: ProviderConfig          :param soledad: Soledad instance @@ -60,9 +59,13 @@ class PreferencesWindow(QtGui.QDialog):          QtGui.QDialog.__init__(self, parent)          self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") -        self._srp_auth = srp_auth +        self._backend = backend          self._settings = LeapSettings()          self._soledad = soledad +        self._provider_config = provider_config +        self._domain = domain + +        self._backend_connect()          # Load UI          self.ui = Ui_Preferences() @@ -82,40 +85,57 @@ class PreferencesWindow(QtGui.QDialog):          else:              self._add_configured_providers() -        pw_enabled = False - -        # check if the user is logged in -        if srp_auth is not None and srp_auth.get_token() is not None: -            # check if provider has 'mx' ... -            if provider_config.provides_mx(): -                enabled_services = self._settings.get_enabled_services(domain) -                mx_name = get_service_display_name(MX_SERVICE) - -                # ... and if the user have it enabled -                if MX_SERVICE not in enabled_services: -                    msg = self.tr("You need to enable {0} in order to change " -                                  "the password.".format(mx_name)) -                    self._set_password_change_status(msg, error=True) -                else: -                    if sameProxiedObjects(self._soledad, None): -                        msg = self.tr( -                            "You need to wait until {0} is ready in " -                            "order to change the password.".format(mx_name)) -                        self._set_password_change_status(msg) -                    else: -                        # Soledad is bootstrapped -                        pw_enabled = True -            else: -                pw_enabled = True -        else: -            msg = self.tr( -                "In order to change your password you need to be logged in.") -            self._set_password_change_status(msg) +        self._backend.get_logged_in_status()          self._select_provider_by_name(domain) +    def _is_logged_in(self): +        """ +        SLOT +        TRIGGERS: +            Signaler.srp_status_logged_in + +        Actions to perform is the user is logged in. +        """ +        settings = self._settings +        pw_enabled = True + +        # check if provider has 'mx' ... +        # TODO: we should move this to the backend. +        if self._provider_config.provides_mx(): +            enabled_services = settings.get_enabled_services(self._domain) +            mx_name = get_service_display_name(MX_SERVICE) + +            # ... and if the user have it enabled +            if MX_SERVICE not in enabled_services: +                msg = self.tr("You need to enable {0} in order to change " +                              "the password.".format(mx_name)) +                self._set_password_change_status(msg, error=True) +                pw_enabled = False +            else: +                # check if Soledad is bootstrapped +                if sameProxiedObjects(self._soledad, None): +                    msg = self.tr( +                        "You need to wait until {0} is ready in " +                        "order to change the password.".format(mx_name)) +                    self._set_password_change_status(msg) +                    pw_enabled = False +          self.ui.gbPasswordChange.setEnabled(pw_enabled) +    def _not_logged_in(self): +        """ +        SLOT +        TRIGGERS: +            Signaler.srp_status_not_logged_in + +        Actions to perform if the user is not logged in. +        """ +        msg = self.tr( +            "In order to change your password you need to be logged in.") +        self._set_password_change_status(msg) +        self.ui.gbPasswordChange.setEnabled(False) +      def set_soledad_ready(self):          """          SLOT @@ -185,19 +205,17 @@ class PreferencesWindow(QtGui.QDialog):              return          self._set_changing_password(True) -        d = self._srp_auth.change_password(current_password, new_password) -        d.addCallback(partial(self._change_password_success, new_password)) -        d.addErrback(self._change_password_problem) +        self._backend.change_password(current_password, new_password) -    def _change_password_success(self, new_password, _): +    def _change_password_ok(self):          """ -        Callback used to display a successfully performed action. +        SLOT +        TRIGGERS: +          self._backend.signaler.srp_password_change_ok -        :param new_password: the new password for the user. -        :type new_password: str. -        :param _: the returned data from self._srp_auth.change_password -                  Ignored +        Callback used to display a successfully changed password.          """ +        new_password = self.ui.leNewPassword.text()          logger.debug("SRP password changed successfully.")          try:              self._soledad.change_passphrase(new_password) @@ -211,24 +229,21 @@ class PreferencesWindow(QtGui.QDialog):          self._clear_password_inputs()          self._set_changing_password(False) -    def _change_password_problem(self, failure): -        """ -        Errback called if there was a problem with the deferred. -        Also is used to display an error message. - -        :param failure: the cause of the method failed. -        :type failure: twisted.python.Failure +    def _change_password_problem(self, msg):          """ -        logger.error("Error changing password: %s", (failure, )) -        problem = self.tr("There was a problem changing the password.") - -        if failure.check(SRPAuthBadUserOrPassword): -            problem = self.tr("You did not enter a correct current password.") +        SLOT +        TRIGGERS: +          self._backend.signaler.srp_password_change_error +          self._backend.signaler.srp_password_change_badpw -        self._set_password_change_status(problem, error=True) +        Callback used to display an error on changing password. +        :param msg: the message to show to the user. +        :type msg: unicode +        """ +        logger.error("Error changing password") +        self._set_password_change_status(msg, error=True)          self._set_changing_password(False) -        failure.trap(Exception)      def _clear_password_inputs(self):          """ @@ -387,3 +402,24 @@ class PreferencesWindow(QtGui.QDialog):              provider_config = None          return provider_config + +    def _backend_connect(self): +        """ +        Helper to connect to backend signals +        """ +        sig = self._backend.signaler + +        sig.srp_status_logged_in.connect(self._is_logged_in) +        sig.srp_status_not_logged_in.connect(self._not_logged_in) + +        sig.srp_password_change_ok.connect(self._change_password_ok) + +        pwd_change_error = partial( +            self._change_password_problem, +            self.tr("There was a problem changing the password.")) +        sig.srp_password_change_error.connect(pwd_change_error) + +        pwd_change_badpw = partial( +            self._change_password_problem, +            self.tr("You did not enter a correct current password.")) +        sig.srp_password_change_badpw.connect(pwd_change_badpw) | 
