# HG changeset patch # User Goffi # Date 1448639869 -3600 # Node ID 52af44e745b59eafd7a171a584aba71b6791a3d3 # Parent 9a7a27c4461172e2d9c1f7fa1d56107a0e797e98 core (stdui[ui_profile_manager]): refactored profile authentication: the workflow is greatly simplified by the use of the new startSession mechanisme and deferedUI diff -r 9a7a27c44611 -r 52af44e745b5 src/stdui/ui_profile_manager.py --- a/src/stdui/ui_profile_manager.py Fri Nov 27 16:54:11 2015 +0100 +++ b/src/stdui/ui_profile_manager.py Fri Nov 27 16:57:49 2015 +0100 @@ -20,10 +20,11 @@ from sat.core.i18n import D_ from sat.core.constants import Const as C +from sat.core.log import getLogger +log = getLogger(__name__) +from sat.core import exceptions from sat.tools import xml_tools -from sat.memory.crypto import PasswordHasher from sat.memory.memory import ProfileSessions -from twisted.internet import defer from twisted.words.protocols.jabber import jid @@ -38,73 +39,50 @@ host.registerCallback(self._changeXMPPPassword, force_id=C.CHANGE_XMPP_PASSWD_ID, with_data=True) self.__new_xmpp_passwd_id = host.registerCallback(self._changeXMPPPasswordCb, with_data=True) - def _authenticateProfile(self, data, profile): - """Get the data/dialog for connecting a profile + def _startSessionEb(self, fail, first, profile): + """Errback method for startSession during profile authentication - @param data (dict) - @param profile: %(doc_profile)s - @return: deferred dict + @param first(bool): if True, this is the first try and we have tryied empty password + in this case we ask for a password to the user. + @param profile(unicode, None): %(doc_profile)s + must only be used if first is True """ - def gotProfileCipher(profile_cipher): - if self.host.memory.auth_sessions.profileGetUnique(profile): - # case 1: profile already authenticated - return {'validated': C.boolConst(True)} - self.profile_ciphers[profile] = profile_cipher - if 'profile_password' in data: - # case 2: password is provided by the caller - return self._verifyPassword(data, profile) + if first: + # first call, we ask for the password + form_ui = xml_tools.XMLUI("form", title=D_('Profile password for {}').format(profile), submit_id='') + form_ui.addPassword('profile_password', value='') + d = xml_tools.deferredUI(self.host, form_ui, chained=True) + d.addCallback(self._authenticateProfile, profile) + return {'xmlui': form_ui.toXml()} - def check_empty_password(verify_password_result): - validated = C.bool(verify_password_result['validated']) - if validated: - # case 3: there's no password for this profile - return verify_password_result - - # case 4: prompt the user for a password - def xmlui_cb(data_, profile): - return self._verifyPassword(data_, profile) - - callback_id = self.host.registerCallback(xmlui_cb, with_data=True, one_shot=True) - form_ui = xml_tools.XMLUI("form", title=D_('Profile password for {}').format(profile), submit_id=callback_id) - form_ui.addPassword('profile_password', value='') - return {'xmlui': form_ui.toXml()} + assert profile is None - check_empty_data = {'profile_password': ''} - d = self._verifyPassword(check_empty_data, profile) - return d.addCallback(check_empty_password) - - d = self.host.memory.asyncGetStringParamA(C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile) - d.addCallback(gotProfileCipher) - d.addErrback(self.getParamError) - return d - - def getParamError(self, failure): - _dialog = xml_tools.XMLUI('popup', title=D_('Error')) - _dialog.addText(D_("Can't get profile parameter.")) - return {'xmlui': _dialog.toXml(), 'validated': C.boolConst(False)} - - @defer.inlineCallbacks - def _verifyPassword(self, data, profile): - """Verify the given password + if fail.check(exceptions.PasswordError): + dialog = xml_tools.XMLUI('popup', title=D_('Connection error')) + dialog.addText(D_("The provided profile password doesn't match.")) + else: + log.error(u"Unexpected exceptions: {}".format(fail)) + dialog = xml_tools.XMLUI('popup', title=D_('Internal error')) + dialog.addText(D_(u"Internal error: {}".format(fail))) + return {'xmlui': dialog.toXml(), 'validated': C.BOOL_FALSE} - @param data (dict) - @param profile: %(doc_profile)s - @return: deferred dict - """ - assert profile in self.profile_ciphers - + def _authenticateProfile(self, data, profile): + if self.host.memory.isSessionStarted(profile): + return {'validated': C.BOOL_TRUE} try: - profile_password = data[xml_tools.formEscape('profile_password')] + password = data[xml_tools.formEscape('profile_password')] except KeyError: - profile_password = data['profile_password'] # not received from a user input - verified = yield PasswordHasher.verify(profile_password, self.profile_ciphers[profile]) - if not verified: - _dialog = xml_tools.XMLUI('popup', title=D_('Connection error')) - _dialog.addText(D_("The provided profile password doesn't match.")) - defer.returnValue({'xmlui': _dialog.toXml(), 'validated': C.boolConst(False)}) - - yield self.host.memory.newAuthSession(profile_password, profile) - defer.returnValue({'validated': C.boolConst(True)}) + # first request, we try empty password + password = '' + first = True + eb_profile = profile + else: + first = False + eb_profile = None + d = self.host.memory.startSession(password, profile) + d.addCallback(lambda dummy: {'validated': C.BOOL_TRUE}) + d.addErrback(self._startSessionEb, first, eb_profile) + return d def _changeXMPPPassword(self, data, profile): session_data = self._sessions.profileGetUnique(profile)