diff src/plugins/plugin_misc_account.py @ 1041:9095263011b6

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)
author souliane <souliane@mailoo.org>
date Wed, 21 May 2014 12:07:13 +0200
parents 76ad41b708e1
children 85c110c0be86
line wrap: on
line diff
--- 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()})