comparison 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
comparison
equal deleted inserted replaced
1040:76ad41b708e1 1041:9095263011b6
19 19
20 from sat.core.i18n import _, D_ 20 from sat.core.i18n import _, D_
21 from sat.core.log import getLogger 21 from sat.core.log import getLogger
22 log = getLogger(__name__) 22 log = getLogger(__name__)
23 from sat.core import exceptions 23 from sat.core import exceptions
24 from sat.tools import xml_tools
25 from sat.memory.memory import Sessions
26 from sat.memory.crypto import PasswordHasher
27 from sat.core.constants import Const as C
28
24 from twisted.internet import reactor, defer, protocol 29 from twisted.internet import reactor, defer, protocol
25 from os.path import join, dirname
26 from twisted.python.procutils import which 30 from twisted.python.procutils import which
27 from twisted.python.failure import Failure 31 from twisted.python.failure import Failure
32 from twisted.mail.smtp import sendmail
33 from os.path import join, dirname
28 from email.mime.text import MIMEText 34 from email.mime.text import MIMEText
29 from twisted.mail.smtp import sendmail
30 from sat.tools import xml_tools
31 35
32 PLUGIN_INFO = { 36 PLUGIN_INFO = {
33 "name": "Account Plugin", 37 "name": "Account Plugin",
34 "import_name": "MISC-ACCOUNT", 38 "import_name": "MISC-ACCOUNT",
35 "type": "MISC", 39 "type": "MISC",
130 log.error(_("Can't find %s") % (self.getConfig('prosodyctl'), )) 134 log.error(_("Can't find %s") % (self.getConfig('prosodyctl'), ))
131 else: 135 else:
132 self._prosody_path = dirname(paths[0]) 136 self._prosody_path = dirname(paths[0])
133 log.info(_('Prosody path found: %s') % (self._prosody_path, )) 137 log.info(_('Prosody path found: %s') % (self._prosody_path, ))
134 138
139 self._sessions = Sessions()
140
135 self.__account_cb_id = host.registerCallback(self._accountDialogCb, with_data=True) 141 self.__account_cb_id = host.registerCallback(self._accountDialogCb, with_data=True)
136 self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True) 142 self.__change_password_id = host.registerCallback(self.__changePasswordCb, with_data=True)
137 143
138 def deleteBlogCallback(posts, comments): 144 def deleteBlogCallback(posts, comments):
139 return lambda data, profile: self.__deleteBlogPostsCb(posts, comments, data, profile) 145 return lambda data, profile: self.__deleteBlogPostsCb(posts, comments, data, profile)
140 146
141 self.__delete_posts_comments_id = host.registerCallback(deleteBlogCallback(True, True), with_data=True)
142 self.__delete_posts_id = host.registerCallback(deleteBlogCallback(True, False), with_data=True) 147 self.__delete_posts_id = host.registerCallback(deleteBlogCallback(True, False), with_data=True)
143 self.__delete_comments_id = host.registerCallback(deleteBlogCallback(False, True), with_data=True) 148 self.__delete_comments_id = host.registerCallback(deleteBlogCallback(False, True), with_data=True)
149 self.__delete_posts_comments_id = host.registerCallback(deleteBlogCallback(True, True), with_data=True)
150
151 self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True)
144 152
145 def getConfig(self, name): 153 def getConfig(self, name):
146 return self.host.memory.getConfig(CONFIG_SECTION, name) or default_conf[name] 154 return self.host.memory.getConfig(CONFIG_SECTION, name) or default_conf[name]
147 155
148 def _registerAccount(self, email, password, profile): 156 def _registerAccount(self, email, password, profile):
250 """Get the main dialog to manage your account 258 """Get the main dialog to manage your account
251 @param menu_data 259 @param menu_data
252 @param profile: %(doc_profile)s 260 @param profile: %(doc_profile)s
253 @return: XML of the dialog 261 @return: XML of the dialog
254 """ 262 """
255 form_ui = xml_tools.XMLUI("form", "tabs", title=D_("Manage your XMPP account"), submit_id=self.__account_cb_id) 263 form_ui = xml_tools.XMLUI("form", "tabs", title=D_("Manage your account"), submit_id=self.__account_cb_id)
256 tab_container = form_ui.current_container 264 tab_container = form_ui.current_container
257 265
258 tab_container.addTab("update", D_("Change your password"), container=xml_tools.PairsContainer) 266 tab_container.addTab("update", D_("Change your password"), container=xml_tools.PairsContainer)
259 form_ui.addLabel(D_("Current password")) 267 form_ui.addLabel(D_("Current profile password"))
260 form_ui.addPassword("current_passwd", value="") 268 form_ui.addPassword("current_passwd", value="")
261 form_ui.addLabel(D_("New password")) 269 form_ui.addLabel(D_("New password"))
262 form_ui.addPassword("new_passwd1", value="") 270 form_ui.addPassword("new_passwd1", value="")
263 form_ui.addLabel(D_("New password (again)")) 271 form_ui.addLabel(D_("New password (again)"))
264 form_ui.addPassword("new_passwd2", value="") 272 form_ui.addPassword("new_passwd2", value="")
265 273
266 if 'GROUPBLOG' in self.host.plugins: 274 if 'GROUPBLOG' in self.host.plugins:
267 tab_container.addTab("delete_posts", D_("Delete your posts"), container=xml_tools.PairsContainer) 275 tab_container.addTab("delete_posts", D_("Delete your posts"), container=xml_tools.PairsContainer)
268 form_ui.addLabel(D_("Current password")) 276 form_ui.addLabel(D_("Current profile password"))
269 form_ui.addPassword("delete_posts_passwd", value="") 277 form_ui.addPassword("delete_posts_passwd", value="")
270 form_ui.addLabel(D_("Delete all your posts and their comments")) 278 form_ui.addLabel(D_("Delete all your posts and their comments"))
271 form_ui.addBool("delete_posts_checkbox", "false") 279 form_ui.addBool("delete_posts_checkbox", "false")
272 form_ui.addLabel(D_("Delete all your comments on other's posts")) 280 form_ui.addLabel(D_("Delete all your comments on other's posts"))
273 form_ui.addBool("delete_comments_checkbox", "false") 281 form_ui.addBool("delete_comments_checkbox", "false")
274 282
275 tab_container.addTab("delete", D_("Delete your account"), container=xml_tools.PairsContainer) 283 tab_container.addTab("delete", D_("Delete your account"), container=xml_tools.PairsContainer)
276 form_ui.addLabel(D_("Current password")) 284 form_ui.addLabel(D_("Current profile password"))
277 form_ui.addPassword("delete_passwd", value="") 285 form_ui.addPassword("delete_passwd", value="")
278 form_ui.addLabel(D_("Delete your account")) 286 form_ui.addLabel(D_("Delete your account"))
279 form_ui.addBool("delete_checkbox", "false") 287 form_ui.addBool("delete_checkbox", "false")
280 return form_ui.toXml() 288 return form_ui.toXml()
281 289
283 def _accountDialogCb(self, data, profile): 291 def _accountDialogCb(self, data, profile):
284 """Called when the user submits the main account dialog 292 """Called when the user submits the main account dialog
285 @param data 293 @param data
286 @param profile 294 @param profile
287 """ 295 """
288 password = yield self.host.memory.asyncGetParamA("Password", "Connection", profile_key=profile) 296 sat_cipher = yield self.host.memory.asyncGetParamA(C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile)
289 297
290 def error_ui(): 298 @defer.inlineCallbacks
291 error_ui = xml_tools.XMLUI("popup", title=D_("Error")) 299 def verify(attempt):
292 error_ui.addText(D_("Passwords don't match!")) 300 auth = yield PasswordHasher.verify(attempt, sat_cipher)
301 defer.returnValue(auth)
302
303 def error_ui(message=None):
304 if not message:
305 D_("The provided profile password doesn't match.")
306 error_ui = xml_tools.XMLUI("popup", title=D_("Attempt failure"))
307 error_ui.addText(message)
293 return {'xmlui': error_ui.toXml()} 308 return {'xmlui': error_ui.toXml()}
294 309
295 # check for account deletion 310 # check for account deletion
296 delete_passwd = data[xml_tools.SAT_FORM_PREFIX + 'delete_passwd'] 311 delete_passwd = data[xml_tools.SAT_FORM_PREFIX + 'delete_passwd']
297 delete_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_checkbox'] 312 delete_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_checkbox']
298 if delete_checkbox == 'true': 313 if delete_checkbox == 'true':
299 if password == delete_passwd: 314 if verify(delete_passwd):
300 defer.returnValue(self.__deleteAccount(profile)) 315 defer.returnValue(self.__deleteAccount(profile))
301 defer.returnValue(error_ui()) 316 defer.returnValue(error_ui())
302 317
303 # check for blog posts deletion 318 # check for blog posts deletion
304 if 'GROUPBLOG' in self.host.plugins: 319 if 'GROUPBLOG' in self.host.plugins:
306 delete_posts_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_posts_checkbox'] 321 delete_posts_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_posts_checkbox']
307 delete_comments_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_comments_checkbox'] 322 delete_comments_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_comments_checkbox']
308 posts = delete_posts_checkbox == 'true' 323 posts = delete_posts_checkbox == 'true'
309 comments = delete_comments_checkbox == 'true' 324 comments = delete_comments_checkbox == 'true'
310 if posts or comments: 325 if posts or comments:
311 if password == delete_posts_passwd: 326 if verify(delete_posts_passwd):
312 defer.returnValue(self.__deleteBlogPosts(posts, comments, profile)) 327 defer.returnValue(self.__deleteBlogPosts(posts, comments, profile))
313 defer.returnValue(error_ui()) 328 defer.returnValue(error_ui())
314 329
315 # check for password modification 330 # check for password modification
316 current_passwd = data[xml_tools.SAT_FORM_PREFIX + 'current_passwd'] 331 current_passwd = data[xml_tools.SAT_FORM_PREFIX + 'current_passwd']
317 new_passwd1 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd1'] 332 new_passwd1 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd1']
318 new_passwd2 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd2'] 333 new_passwd2 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd2']
319 if new_passwd1 or new_passwd2: 334 if new_passwd1 or new_passwd2:
320 if password == current_passwd and new_passwd1 == new_passwd2: 335 if verify(current_passwd):
321 data = yield self.__changePassword(new_passwd1, profile=profile) 336 if new_passwd1 == new_passwd2:
322 defer.returnValue(data) 337 data = yield self.__changePassword(new_passwd1, profile=profile)
338 defer.returnValue(data)
339 else:
340 defer.returnValue(error_ui(D_("The values entered for the new password are not equal.")))
323 defer.returnValue(error_ui()) 341 defer.returnValue(error_ui())
324 342
325 defer.returnValue({}) 343 defer.returnValue({})
326 344
327 def __changePassword(self, password, profile): 345 def __changePassword(self, password, profile):
346 """Ask for a confirmation before changing the XMPP account and SàT profile passwords.
347
348 @param password (str): the new password
349 @param profile (str): %(doc_profile)s
350 """
351 session_id, dummy = self._sessions.newSession({'new_password': password}, profile)
352 form_ui = xml_tools.XMLUI("form", title=D_("Change your password?"), submit_id=self.__change_password_id, session_id=session_id)
353 form_ui.addText(D_("Note for advanced users: this will actually change both your SàT profile password AND your XMPP account password."))
354 form_ui.addText(D_("Continue with changing the password?"))
355 return {'xmlui': form_ui.toXml()}
356
357 def __changePasswordCb(self, data, profile):
328 """Actually change the user XMPP account and SàT profile password 358 """Actually change the user XMPP account and SàT profile password
329 @param password: new password 359 @param data (dict)
330 @profile 360 @profile (str): %(doc_profile)s
331 """ 361 """
362 password = self._sessions.profileGet(data['session_id'], profile)['new_password']
363
332 def passwordChanged(result): 364 def passwordChanged(result):
333 self.host.memory.setParam("Password", password, "Connection", profile_key=profile) 365 d = self.host.memory.setParam(C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=profile)
366 d.addCallback(lambda dummy: self.host.memory.setParam("Password", password, "Connection", profile_key=profile))
334 confirm_ui = xml_tools.XMLUI("popup", title=D_("Confirmation")) 367 confirm_ui = xml_tools.XMLUI("popup", title=D_("Confirmation"))
335 confirm_ui.addText(D_("Your password has been changed.")) 368 confirm_ui.addText(D_("Your password has been changed."))
336 return defer.succeed({'xmlui': confirm_ui.toXml()}) 369 return defer.succeed({'xmlui': confirm_ui.toXml()})
337 370
338 def errback(failure): 371 def errback(failure):