# HG changeset patch # User souliane # Date 1400666833 -7200 # Node ID 9095263011b69bf4d89f0122062031f044f75a59 # Parent 76ad41b708e1708df17ed5cb5622217da1b98487 plugin misc_accout: update relative to the introduction of profile password: - check for the profile password instead of the xmpp password before a modification can be done - "Change your password" action changes both passwords (a confirmation is asked) diff -r 76ad41b708e1 -r 9095263011b6 src/plugins/plugin_misc_account.py --- a/src/plugins/plugin_misc_account.py Tue May 20 15:47:08 2014 +0200 +++ b/src/plugins/plugin_misc_account.py Wed May 21 12:07:13 2014 +0200 @@ -21,13 +21,17 @@ from sat.core.log import getLogger log = getLogger(__name__) from sat.core import exceptions +from sat.tools import xml_tools +from sat.memory.memory import Sessions +from sat.memory.crypto import PasswordHasher +from sat.core.constants import Const as C + from twisted.internet import reactor, defer, protocol -from os.path import join, dirname from twisted.python.procutils import which from twisted.python.failure import Failure +from twisted.mail.smtp import sendmail +from os.path import join, dirname from email.mime.text import MIMEText -from twisted.mail.smtp import sendmail -from sat.tools import xml_tools PLUGIN_INFO = { "name": "Account Plugin", @@ -132,15 +136,19 @@ self._prosody_path = dirname(paths[0]) log.info(_('Prosody path found: %s') % (self._prosody_path, )) + self._sessions = Sessions() + self.__account_cb_id = host.registerCallback(self._accountDialogCb, with_data=True) - self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True) + self.__change_password_id = host.registerCallback(self.__changePasswordCb, with_data=True) def deleteBlogCallback(posts, comments): return lambda data, profile: self.__deleteBlogPostsCb(posts, comments, data, profile) - self.__delete_posts_comments_id = host.registerCallback(deleteBlogCallback(True, True), with_data=True) self.__delete_posts_id = host.registerCallback(deleteBlogCallback(True, False), with_data=True) self.__delete_comments_id = host.registerCallback(deleteBlogCallback(False, True), with_data=True) + self.__delete_posts_comments_id = host.registerCallback(deleteBlogCallback(True, True), with_data=True) + + self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True) def getConfig(self, name): return self.host.memory.getConfig(CONFIG_SECTION, name) or default_conf[name] @@ -252,11 +260,11 @@ @param profile: %(doc_profile)s @return: XML of the dialog """ - form_ui = xml_tools.XMLUI("form", "tabs", title=D_("Manage your XMPP account"), submit_id=self.__account_cb_id) + form_ui = xml_tools.XMLUI("form", "tabs", title=D_("Manage your account"), submit_id=self.__account_cb_id) tab_container = form_ui.current_container tab_container.addTab("update", D_("Change your password"), container=xml_tools.PairsContainer) - form_ui.addLabel(D_("Current password")) + form_ui.addLabel(D_("Current profile password")) form_ui.addPassword("current_passwd", value="") form_ui.addLabel(D_("New password")) form_ui.addPassword("new_passwd1", value="") @@ -265,7 +273,7 @@ if 'GROUPBLOG' in self.host.plugins: tab_container.addTab("delete_posts", D_("Delete your posts"), container=xml_tools.PairsContainer) - form_ui.addLabel(D_("Current password")) + form_ui.addLabel(D_("Current profile password")) form_ui.addPassword("delete_posts_passwd", value="") form_ui.addLabel(D_("Delete all your posts and their comments")) form_ui.addBool("delete_posts_checkbox", "false") @@ -273,7 +281,7 @@ form_ui.addBool("delete_comments_checkbox", "false") tab_container.addTab("delete", D_("Delete your account"), container=xml_tools.PairsContainer) - form_ui.addLabel(D_("Current password")) + form_ui.addLabel(D_("Current profile password")) form_ui.addPassword("delete_passwd", value="") form_ui.addLabel(D_("Delete your account")) form_ui.addBool("delete_checkbox", "false") @@ -285,18 +293,25 @@ @param data @param profile """ - password = yield self.host.memory.asyncGetParamA("Password", "Connection", profile_key=profile) + sat_cipher = yield self.host.memory.asyncGetParamA(C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile) - def error_ui(): - error_ui = xml_tools.XMLUI("popup", title=D_("Error")) - error_ui.addText(D_("Passwords don't match!")) + @defer.inlineCallbacks + def verify(attempt): + auth = yield PasswordHasher.verify(attempt, sat_cipher) + defer.returnValue(auth) + + def error_ui(message=None): + if not message: + D_("The provided profile password doesn't match.") + error_ui = xml_tools.XMLUI("popup", title=D_("Attempt failure")) + error_ui.addText(message) return {'xmlui': error_ui.toXml()} # check for account deletion delete_passwd = data[xml_tools.SAT_FORM_PREFIX + 'delete_passwd'] delete_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_checkbox'] if delete_checkbox == 'true': - if password == delete_passwd: + if verify(delete_passwd): defer.returnValue(self.__deleteAccount(profile)) defer.returnValue(error_ui()) @@ -308,7 +323,7 @@ posts = delete_posts_checkbox == 'true' comments = delete_comments_checkbox == 'true' if posts or comments: - if password == delete_posts_passwd: + if verify(delete_posts_passwd): defer.returnValue(self.__deleteBlogPosts(posts, comments, profile)) defer.returnValue(error_ui()) @@ -317,20 +332,38 @@ new_passwd1 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd1'] new_passwd2 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd2'] if new_passwd1 or new_passwd2: - if password == current_passwd and new_passwd1 == new_passwd2: - data = yield self.__changePassword(new_passwd1, profile=profile) - defer.returnValue(data) + if verify(current_passwd): + if new_passwd1 == new_passwd2: + data = yield self.__changePassword(new_passwd1, profile=profile) + defer.returnValue(data) + else: + defer.returnValue(error_ui(D_("The values entered for the new password are not equal."))) defer.returnValue(error_ui()) defer.returnValue({}) def __changePassword(self, password, profile): + """Ask for a confirmation before changing the XMPP account and SàT profile passwords. + + @param password (str): the new password + @param profile (str): %(doc_profile)s + """ + session_id, dummy = self._sessions.newSession({'new_password': password}, profile) + form_ui = xml_tools.XMLUI("form", title=D_("Change your password?"), submit_id=self.__change_password_id, session_id=session_id) + form_ui.addText(D_("Note for advanced users: this will actually change both your SàT profile password AND your XMPP account password.")) + form_ui.addText(D_("Continue with changing the password?")) + return {'xmlui': form_ui.toXml()} + + def __changePasswordCb(self, data, profile): """Actually change the user XMPP account and SàT profile password - @param password: new password - @profile + @param data (dict) + @profile (str): %(doc_profile)s """ + password = self._sessions.profileGet(data['session_id'], profile)['new_password'] + def passwordChanged(result): - self.host.memory.setParam("Password", password, "Connection", profile_key=profile) + d = self.host.memory.setParam(C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=profile) + d.addCallback(lambda dummy: self.host.memory.setParam("Password", password, "Connection", profile_key=profile)) confirm_ui = xml_tools.XMLUI("popup", title=D_("Confirmation")) confirm_ui.addText(D_("Your password has been changed.")) return defer.succeed({'xmlui': confirm_ui.toXml()})