diff options
-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) |