comparison sat/plugins/plugin_misc_account.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 378188abe941
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
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
22 log = getLogger(__name__) 23 log = getLogger(__name__)
23 from sat.core import exceptions 24 from sat.core import exceptions
24 from sat.tools import xml_tools 25 from sat.tools import xml_tools
25 from sat.memory.memory import Sessions 26 from sat.memory.memory import Sessions
26 from sat.memory.crypto import PasswordHasher 27 from sat.memory.crypto import PasswordHasher
29 from twisted.internet import defer 30 from twisted.internet import defer
30 from twisted.python.failure import Failure 31 from twisted.python.failure import Failure
31 from twisted.words.protocols.jabber import jid 32 from twisted.words.protocols.jabber import jid
32 from sat.tools import email as sat_email 33 from sat.tools import email as sat_email
33 34
34 # FIXME: this plugin code is old and need a cleaning 35 #  FIXME: this plugin code is old and need a cleaning
35 # TODO: account deletion/password change need testing 36 # TODO: account deletion/password change need testing
36 37
37 38
38 PLUGIN_INFO = { 39 PLUGIN_INFO = {
39 C.PI_NAME: "Account Plugin", 40 C.PI_NAME: "Account Plugin",
40 C.PI_IMPORT_NAME: "MISC-ACCOUNT", 41 C.PI_IMPORT_NAME: "MISC-ACCOUNT",
41 C.PI_TYPE: "MISC", 42 C.PI_TYPE: "MISC",
42 C.PI_PROTOCOLS: [], 43 C.PI_PROTOCOLS: [],
43 C.PI_DEPENDENCIES: ["XEP-0077"], 44 C.PI_DEPENDENCIES: ["XEP-0077"],
44 C.PI_RECOMMENDATIONS: ['GROUPBLOG'], 45 C.PI_RECOMMENDATIONS: ["GROUPBLOG"],
45 C.PI_MAIN: "MiscAccount", 46 C.PI_MAIN: "MiscAccount",
46 C.PI_HANDLER: "no", 47 C.PI_HANDLER: "no",
47 C.PI_DESCRIPTION: _(u"""SàT account creation""") 48 C.PI_DESCRIPTION: _(u"""SàT account creation"""),
48 } 49 }
49 50
50 CONFIG_SECTION = "plugin account" 51 CONFIG_SECTION = "plugin account"
51 52
52 # You need do adapt the following consts to your server 53 # You need do adapt the following consts to your server
53 # all theses values (key=option name, value=default) can (and should) be overriden in sat.conf 54 # all theses values (key=option name, value=default) can (and should) be overriden in sat.conf
54 # in section CONFIG_SECTION 55 # in section CONFIG_SECTION
55 56
56 default_conf = {"email_from": "NOREPLY@example.net", 57 default_conf = {
57 "email_server": "localhost", 58 "email_from": "NOREPLY@example.net",
58 "email_sender_domain": "", 59 "email_server": "localhost",
59 "email_port": 25, 60 "email_sender_domain": "",
60 "email_username": "", 61 "email_port": 25,
61 "email_password": "", 62 "email_username": "",
62 "email_starttls": "false", 63 "email_password": "",
63 "email_auth": "false", 64 "email_starttls": "false",
64 "email_admins_list": [], 65 "email_auth": "false",
65 "admin_email": "", 66 "email_admins_list": [],
66 "new_account_server": "localhost", 67 "admin_email": "",
67 "new_account_domain": "", # use xmpp_domain if not found 68 "new_account_server": "localhost",
68 "reserved_list": ['libervia'] # profiles which can't be used 69 "new_account_domain": "", #  use xmpp_domain if not found
69 } 70 "reserved_list": ["libervia"], # profiles which can't be used
70 71 }
71 WELCOME_MSG = D_(u"""Welcome to Libervia, the web interface of Salut à Toi. 72
73 WELCOME_MSG = D_(
74 u"""Welcome to Libervia, the web interface of Salut à Toi.
72 75
73 Your account on {domain} has been successfully created. 76 Your account on {domain} has been successfully created.
74 This is a demonstration version to show you the current status of the project. 77 This is a demonstration version to show you the current status of the project.
75 It is still under development, please keep it in mind! 78 It is still under development, please keep it in mind!
76 79
86 89
87 Any feedback welcome. Thank you! 90 Any feedback welcome. Thank you!
88 91
89 Salut à Toi association 92 Salut à Toi association
90 https://www.salut-a-toi.org 93 https://www.salut-a-toi.org
91 """) 94 """
95 )
92 96
93 DEFAULT_DOMAIN = u"example.net" 97 DEFAULT_DOMAIN = u"example.net"
94 98
95 99
96 class MiscAccount(object): 100 class MiscAccount(object):
97 """Account plugin: create a SàT + XMPP account, used by Libervia""" 101 """Account plugin: create a SàT + XMPP account, used by Libervia"""
102
98 # XXX: This plugin was initialy a Q&D one used for the demo. 103 # XXX: This plugin was initialy a Q&D one used for the demo.
99 # TODO: cleaning, separate email handling, more configuration/tests, fixes 104 # TODO: cleaning, separate email handling, more configuration/tests, fixes
100
101 105
102 def __init__(self, host): 106 def __init__(self, host):
103 log.info(_(u"Plugin Account initialization")) 107 log.info(_(u"Plugin Account initialization"))
104 self.host = host 108 self.host = host
105 host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async=True) 109 host.bridge.addMethod(
106 host.bridge.addMethod("getNewAccountDomain", ".plugin", in_sign='', out_sign='s', method=self.getNewAccountDomain, async=False) 110 "registerSatAccount",
107 host.bridge.addMethod("getAccountDialogUI", ".plugin", in_sign='s', out_sign='s', method=self._getAccountDialogUI, async=False) 111 ".plugin",
108 host.bridge.addMethod("asyncConnectWithXMPPCredentials", ".plugin", in_sign='ss', out_sign='b', method=self.asyncConnectWithXMPPCredentials, async=True) 112 in_sign="sss",
113 out_sign="",
114 method=self._registerAccount,
115 async=True,
116 )
117 host.bridge.addMethod(
118 "getNewAccountDomain",
119 ".plugin",
120 in_sign="",
121 out_sign="s",
122 method=self.getNewAccountDomain,
123 async=False,
124 )
125 host.bridge.addMethod(
126 "getAccountDialogUI",
127 ".plugin",
128 in_sign="s",
129 out_sign="s",
130 method=self._getAccountDialogUI,
131 async=False,
132 )
133 host.bridge.addMethod(
134 "asyncConnectWithXMPPCredentials",
135 ".plugin",
136 in_sign="ss",
137 out_sign="b",
138 method=self.asyncConnectWithXMPPCredentials,
139 async=True,
140 )
109 141
110 self.fixEmailAdmins() 142 self.fixEmailAdmins()
111 self._sessions = Sessions() 143 self._sessions = Sessions()
112 144
113 self.__account_cb_id = host.registerCallback(self._accountDialogCb, with_data=True) 145 self.__account_cb_id = host.registerCallback(
114 self.__change_password_id = host.registerCallback(self.__changePasswordCb, with_data=True) 146 self._accountDialogCb, with_data=True
147 )
148 self.__change_password_id = host.registerCallback(
149 self.__changePasswordCb, with_data=True
150 )
115 151
116 def deleteBlogCallback(posts, comments): 152 def deleteBlogCallback(posts, comments):
117 return lambda data, profile: self.__deleteBlogPostsCb(posts, comments, data, profile) 153 return lambda data, profile: self.__deleteBlogPostsCb(
118 154 posts, comments, data, profile
119 self.__delete_posts_id = host.registerCallback(deleteBlogCallback(True, False), with_data=True) 155 )
120 self.__delete_comments_id = host.registerCallback(deleteBlogCallback(False, True), with_data=True) 156
121 self.__delete_posts_comments_id = host.registerCallback(deleteBlogCallback(True, True), with_data=True) 157 self.__delete_posts_id = host.registerCallback(
122 158 deleteBlogCallback(True, False), with_data=True
123 self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True) 159 )
124 160 self.__delete_comments_id = host.registerCallback(
161 deleteBlogCallback(False, True), with_data=True
162 )
163 self.__delete_posts_comments_id = host.registerCallback(
164 deleteBlogCallback(True, True), with_data=True
165 )
166
167 self.__delete_account_id = host.registerCallback(
168 self.__deleteAccountCb, with_data=True
169 )
125 170
126 # FIXME: remove this after some time, when the deprecated parameter is really abandoned 171 # FIXME: remove this after some time, when the deprecated parameter is really abandoned
127 def fixEmailAdmins(self): 172 def fixEmailAdmins(self):
128 """Handle deprecated config option "admin_email" to fix the admin emails list""" 173 """Handle deprecated config option "admin_email" to fix the admin emails list"""
129 admin_email = self.getConfig('admin_email') 174 admin_email = self.getConfig("admin_email")
130 if not admin_email: 175 if not admin_email:
131 return 176 return
132 log.warning(u"admin_email parameter is deprecated, please use email_admins_list instead") 177 log.warning(
178 u"admin_email parameter is deprecated, please use email_admins_list instead"
179 )
133 param_name = "email_admins_list" 180 param_name = "email_admins_list"
134 try: 181 try:
135 section = "" 182 section = ""
136 value = self.host.memory.getConfig(section, param_name, Exception) 183 value = self.host.memory.getConfig(section, param_name, Exception)
137 except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): 184 except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
138 section = CONFIG_SECTION 185 section = CONFIG_SECTION
139 value = self.host.memory.getConfig(section, param_name, default_conf[param_name]) 186 value = self.host.memory.getConfig(
187 section, param_name, default_conf[param_name]
188 )
140 189
141 value = set(value) 190 value = set(value)
142 value.add(admin_email) 191 value.add(admin_email)
143 self.host.memory.config.set(section, param_name, ",".join(value)) 192 self.host.memory.config.set(section, param_name, ",".join(value))
144 193
191 @return Deferred 240 @return Deferred
192 """ 241 """
193 if not password or not profile: 242 if not password or not profile:
194 raise exceptions.DataError 243 raise exceptions.DataError
195 244
196 if profile.lower() in self.getConfig('reserved_list'): 245 if profile.lower() in self.getConfig("reserved_list"):
197 return defer.fail(Failure(exceptions.ConflictError)) 246 return defer.fail(Failure(exceptions.ConflictError))
198 247
199 d = self.host.memory.createProfile(profile, password) 248 d = self.host.memory.createProfile(profile, password)
200 d.addCallback(lambda dummy: self.profileCreated(password, jid_s, profile)) 249 d.addCallback(lambda dummy: self.profileCreated(password, jid_s, profile))
201 return d 250 return d
214 d = defer.succeed(None) 263 d = defer.succeed(None)
215 jid_ = jid.JID(jid_s) 264 jid_ = jid.JID(jid_s)
216 else: 265 else:
217 jid_s = profile + u"@" + self.getNewAccountDomain() 266 jid_s = profile + u"@" + self.getNewAccountDomain()
218 jid_ = jid.JID(jid_s) 267 jid_ = jid.JID(jid_s)
219 d = self.host.plugins['XEP-0077'].registerNewAccount(jid_, password) 268 d = self.host.plugins["XEP-0077"].registerNewAccount(jid_, password)
220 269
221 def setParams(dummy): 270 def setParams(dummy):
222 self.host.memory.setParam("JabberID", jid_s, "Connection", profile_key=profile) 271 self.host.memory.setParam(
223 d = self.host.memory.setParam("Password", password, "Connection", profile_key=profile) 272 "JabberID", jid_s, "Connection", profile_key=profile
273 )
274 d = self.host.memory.setParam(
275 "Password", password, "Connection", profile_key=profile
276 )
224 return d 277 return d
225 278
226 def removeProfile(failure): 279 def removeProfile(failure):
227 self.host.memory.asyncDeleteProfile(profile) 280 self.host.memory.asyncDeleteProfile(profile)
228 return failure 281 return failure
233 d.addErrback(removeProfile) 286 d.addErrback(removeProfile)
234 return d 287 return d
235 288
236 def _sendEmailEb(self, failure_, email): 289 def _sendEmailEb(self, failure_, email):
237 # TODO: return error code to user 290 # TODO: return error code to user
238 log.error(_(u"Failed to send account creation confirmation to {email}: {msg}").format( 291 log.error(
239 email = email, 292 _(u"Failed to send account creation confirmation to {email}: {msg}").format(
240 msg = failure_)) 293 email=email, msg=failure_
294 )
295 )
241 296
242 def sendEmails(self, email, profile): 297 def sendEmails(self, email, profile):
243 # time to send the email 298 # time to send the email
244 299
245 domain = self.getNewAccountDomain() 300 domain = self.getNewAccountDomain()
246 301
247 # email to the administrators 302 # email to the administrators
248 admins_emails = self.getConfig('email_admins_list') 303 admins_emails = self.getConfig("email_admins_list")
249 if not admins_emails: 304 if not admins_emails:
250 log.warning(u"No known admin email, we can't send email to administrator(s).\nPlease fill email_admins_list parameter") 305 log.warning(
306 u"No known admin email, we can't send email to administrator(s).\nPlease fill email_admins_list parameter"
307 )
251 d_admin = defer.fail(exceptions.DataError("no admin email")) 308 d_admin = defer.fail(exceptions.DataError("no admin email"))
252 else: 309 else:
253 subject = _(u'New Libervia account created') 310 subject = _(u"New Libervia account created")
254 body = (u"""New account created: {profile} [{email}]""".format( 311 body = u"""New account created: {profile} [{email}]""".format(
255 profile = profile, 312 profile=profile,
256 # there is no email when an existing XMPP account is used 313 # there is no email when an existing XMPP account is used
257 email = email or u"<no email>")) 314 email=email or u"<no email>",
315 )
258 d_admin = sat_email.sendEmail(self.host, admins_emails, subject, body) 316 d_admin = sat_email.sendEmail(self.host, admins_emails, subject, body)
259 317
260 admins_emails_txt = u', '.join([u'<' + addr + u'>' for addr in admins_emails]) 318 admins_emails_txt = u", ".join([u"<" + addr + u">" for addr in admins_emails])
261 d_admin.addCallbacks(lambda dummy: log.debug(u"Account creation notification sent to admin(s) {}".format(admins_emails_txt)), 319 d_admin.addCallbacks(
262 lambda dummy: log.error(u"Failed to send account creation notification to admin {}".format(admins_emails_txt))) 320 lambda dummy: log.debug(
321 u"Account creation notification sent to admin(s) {}".format(
322 admins_emails_txt
323 )
324 ),
325 lambda dummy: log.error(
326 u"Failed to send account creation notification to admin {}".format(
327 admins_emails_txt
328 )
329 ),
330 )
263 if not email: 331 if not email:
264 # TODO: if use register with an existing account, an XMPP message should be sent 332 # TODO: if use register with an existing account, an XMPP message should be sent
265 return d_admin 333 return d_admin
266 334
267 jid_s = self.host.memory.getParamA(u"JabberID", u"Connection", profile_key=profile) 335 jid_s = self.host.memory.getParamA(
268 subject = _(u'Your Libervia account has been created') 336 u"JabberID", u"Connection", profile_key=profile
269 body = (_(WELCOME_MSG).format(profile=profile, jid=jid_s, domain=domain)) 337 )
338 subject = _(u"Your Libervia account has been created")
339 body = _(WELCOME_MSG).format(profile=profile, jid=jid_s, domain=domain)
270 340
271 # XXX: this will not fail when the email address doesn't exist 341 # XXX: this will not fail when the email address doesn't exist
272 # FIXME: check email reception to validate email given by the user 342 # FIXME: check email reception to validate email given by the user
273 # FIXME: delete the profile if the email could not been sent? 343 # FIXME: delete the profile if the email could not been sent?
274 d_user = sat_email.sendEmail(self.host, [email], subject, body) 344 d_user = sat_email.sendEmail(self.host, [email], subject, body)
275 d_user.addCallbacks(lambda dummy: log.debug(u"Account creation confirmation sent to <{}>".format(email)), 345 d_user.addCallbacks(
276 self._sendEmailEb) 346 lambda dummy: log.debug(
347 u"Account creation confirmation sent to <{}>".format(email)
348 ),
349 self._sendEmailEb,
350 )
277 return defer.DeferredList([d_user, d_admin]) 351 return defer.DeferredList([d_user, d_admin])
278 352
279 def getNewAccountDomain(self): 353 def getNewAccountDomain(self):
280 """get the domain that will be set to new account""" 354 """get the domain that will be set to new account"""
281 355
282 domain = self.getConfig('new_account_domain') or self.getConfig('xmpp_domain', None) 356 domain = self.getConfig("new_account_domain") or self.getConfig(
357 "xmpp_domain", None
358 )
283 if not domain: 359 if not domain:
284 log.warning(_(u'xmpp_domain needs to be set in sat.conf. Using "{default}" meanwhile').format(default=DEFAULT_DOMAIN)) 360 log.warning(
361 _(
362 u'xmpp_domain needs to be set in sat.conf. Using "{default}" meanwhile'
363 ).format(default=DEFAULT_DOMAIN)
364 )
285 return DEFAULT_DOMAIN 365 return DEFAULT_DOMAIN
286 return domain 366 return domain
287 367
288 def _getAccountDialogUI(self, profile): 368 def _getAccountDialogUI(self, profile):
289 """Get the main dialog to manage your account 369 """Get the main dialog to manage your account
290 @param menu_data 370 @param menu_data
291 @param profile: %(doc_profile)s 371 @param profile: %(doc_profile)s
292 @return: XML of the dialog 372 @return: XML of the dialog
293 """ 373 """
294 form_ui = xml_tools.XMLUI("form", "tabs", title=D_("Manage your account"), submit_id=self.__account_cb_id) 374 form_ui = xml_tools.XMLUI(
375 "form",
376 "tabs",
377 title=D_("Manage your account"),
378 submit_id=self.__account_cb_id,
379 )
295 tab_container = form_ui.current_container 380 tab_container = form_ui.current_container
296 381
297 tab_container.addTab("update", D_("Change your password"), container=xml_tools.PairsContainer) 382 tab_container.addTab(
383 "update", D_("Change your password"), container=xml_tools.PairsContainer
384 )
298 form_ui.addLabel(D_("Current profile password")) 385 form_ui.addLabel(D_("Current profile password"))
299 form_ui.addPassword("current_passwd", value="") 386 form_ui.addPassword("current_passwd", value="")
300 form_ui.addLabel(D_("New password")) 387 form_ui.addLabel(D_("New password"))
301 form_ui.addPassword("new_passwd1", value="") 388 form_ui.addPassword("new_passwd1", value="")
302 form_ui.addLabel(D_("New password (again)")) 389 form_ui.addLabel(D_("New password (again)"))
326 def _accountDialogCb(self, data, profile): 413 def _accountDialogCb(self, data, profile):
327 """Called when the user submits the main account dialog 414 """Called when the user submits the main account dialog
328 @param data 415 @param data
329 @param profile 416 @param profile
330 """ 417 """
331 sat_cipher = yield self.host.memory.asyncGetParamA(C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile) 418 sat_cipher = yield self.host.memory.asyncGetParamA(
419 C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile
420 )
332 421
333 @defer.inlineCallbacks 422 @defer.inlineCallbacks
334 def verify(attempt): 423 def verify(attempt):
335 auth = yield PasswordHasher.verify(attempt, sat_cipher) 424 auth = yield PasswordHasher.verify(attempt, sat_cipher)
336 defer.returnValue(auth) 425 defer.returnValue(auth)
338 def error_ui(message=None): 427 def error_ui(message=None):
339 if not message: 428 if not message:
340 message = D_("The provided profile password doesn't match.") 429 message = D_("The provided profile password doesn't match.")
341 error_ui = xml_tools.XMLUI("popup", title=D_("Attempt failure")) 430 error_ui = xml_tools.XMLUI("popup", title=D_("Attempt failure"))
342 error_ui.addText(message) 431 error_ui.addText(message)
343 return {'xmlui': error_ui.toXml()} 432 return {"xmlui": error_ui.toXml()}
344 433
345 # check for account deletion 434 # check for account deletion
346 # FIXME: uncomment and fix these features 435 # FIXME: uncomment and fix these features
347 """ 436 """
348 delete_passwd = data[xml_tools.SAT_FORM_PREFIX + 'delete_passwd'] 437 delete_passwd = data[xml_tools.SAT_FORM_PREFIX + 'delete_passwd']
368 defer.returnValue(self.__deleteBlogPosts(posts, comments, profile)) 457 defer.returnValue(self.__deleteBlogPosts(posts, comments, profile))
369 defer.returnValue(error_ui()) 458 defer.returnValue(error_ui())
370 """ 459 """
371 460
372 # check for password modification 461 # check for password modification
373 current_passwd = data[xml_tools.SAT_FORM_PREFIX + 'current_passwd'] 462 current_passwd = data[xml_tools.SAT_FORM_PREFIX + "current_passwd"]
374 new_passwd1 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd1'] 463 new_passwd1 = data[xml_tools.SAT_FORM_PREFIX + "new_passwd1"]
375 new_passwd2 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd2'] 464 new_passwd2 = data[xml_tools.SAT_FORM_PREFIX + "new_passwd2"]
376 if new_passwd1 or new_passwd2: 465 if new_passwd1 or new_passwd2:
377 verified = yield verify(current_passwd) 466 verified = yield verify(current_passwd)
378 assert isinstance(verified, bool) 467 assert isinstance(verified, bool)
379 if verified: 468 if verified:
380 if new_passwd1 == new_passwd2: 469 if new_passwd1 == new_passwd2:
381 data = yield self.__changePassword(new_passwd1, profile=profile) 470 data = yield self.__changePassword(new_passwd1, profile=profile)
382 defer.returnValue(data) 471 defer.returnValue(data)
383 else: 472 else:
384 defer.returnValue(error_ui(D_("The values entered for the new password are not equal."))) 473 defer.returnValue(
474 error_ui(
475 D_("The values entered for the new password are not equal.")
476 )
477 )
385 defer.returnValue(error_ui()) 478 defer.returnValue(error_ui())
386 479
387 defer.returnValue({}) 480 defer.returnValue({})
388 481
389 def __changePassword(self, password, profile): 482 def __changePassword(self, password, profile):
390 """Ask for a confirmation before changing the XMPP account and SàT profile passwords. 483 """Ask for a confirmation before changing the XMPP account and SàT profile passwords.
391 484
392 @param password (str): the new password 485 @param password (str): the new password
393 @param profile (str): %(doc_profile)s 486 @param profile (str): %(doc_profile)s
394 """ 487 """
395 session_id, dummy = self._sessions.newSession({'new_password': password}, profile=profile) 488 session_id, dummy = self._sessions.newSession(
396 form_ui = xml_tools.XMLUI("form", title=D_("Change your password?"), submit_id=self.__change_password_id, session_id=session_id) 489 {"new_password": password}, profile=profile
397 form_ui.addText(D_("Note for advanced users: this will actually change both your SàT profile password AND your XMPP account password.")) 490 )
491 form_ui = xml_tools.XMLUI(
492 "form",
493 title=D_("Change your password?"),
494 submit_id=self.__change_password_id,
495 session_id=session_id,
496 )
497 form_ui.addText(
498 D_(
499 "Note for advanced users: this will actually change both your SàT profile password AND your XMPP account password."
500 )
501 )
398 form_ui.addText(D_("Continue with changing the password?")) 502 form_ui.addText(D_("Continue with changing the password?"))
399 return {'xmlui': form_ui.toXml()} 503 return {"xmlui": form_ui.toXml()}
400 504
401 def __changePasswordCb(self, data, profile): 505 def __changePasswordCb(self, data, profile):
402 """Actually change the user XMPP account and SàT profile password 506 """Actually change the user XMPP account and SàT profile password
403 @param data (dict) 507 @param data (dict)
404 @profile (str): %(doc_profile)s 508 @profile (str): %(doc_profile)s
405 """ 509 """
406 client = self.host.getClient(profile) 510 client = self.host.getClient(profile)
407 password = self._sessions.profileGet(data['session_id'], profile)['new_password'] 511 password = self._sessions.profileGet(data["session_id"], profile)["new_password"]
408 del self._sessions[data['session_id']] 512 del self._sessions[data["session_id"]]
409 513
410 def passwordChanged(dummy): 514 def passwordChanged(dummy):
411 d = self.host.memory.setParam(C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=profile) 515 d = self.host.memory.setParam(
412 d.addCallback(lambda dummy: self.host.memory.setParam("Password", password, "Connection", profile_key=profile)) 516 C.PROFILE_PASS_PATH[1],
517 password,
518 C.PROFILE_PASS_PATH[0],
519 profile_key=profile,
520 )
521 d.addCallback(
522 lambda dummy: self.host.memory.setParam(
523 "Password", password, "Connection", profile_key=profile
524 )
525 )
413 confirm_ui = xml_tools.XMLUI("popup", title=D_("Confirmation")) 526 confirm_ui = xml_tools.XMLUI("popup", title=D_("Confirmation"))
414 confirm_ui.addText(D_("Your password has been changed.")) 527 confirm_ui.addText(D_("Your password has been changed."))
415 return defer.succeed({'xmlui': confirm_ui.toXml()}) 528 return defer.succeed({"xmlui": confirm_ui.toXml()})
416 529
417 def errback(failure): 530 def errback(failure):
418 error_ui = xml_tools.XMLUI("popup", title=D_("Error")) 531 error_ui = xml_tools.XMLUI("popup", title=D_("Error"))
419 error_ui.addText(D_("Your password could not be changed: %s") % failure.getErrorMessage()) 532 error_ui.addText(
420 return defer.succeed({'xmlui': error_ui.toXml()}) 533 D_("Your password could not be changed: %s") % failure.getErrorMessage()
421 534 )
422 d = self.host.plugins['XEP-0077'].changePassword(client, password) 535 return defer.succeed({"xmlui": error_ui.toXml()})
536
537 d = self.host.plugins["XEP-0077"].changePassword(client, password)
423 d.addCallbacks(passwordChanged, errback) 538 d.addCallbacks(passwordChanged, errback)
424 return d 539 return d
425 540
426 def __deleteAccount(self, profile): 541 def __deleteAccount(self, profile):
427 """Ask for a confirmation before deleting the XMPP account and SàT profile 542 """Ask for a confirmation before deleting the XMPP account and SàT profile
428 @param profile 543 @param profile
429 """ 544 """
430 form_ui = xml_tools.XMLUI("form", title=D_("Delete your account?"), submit_id=self.__delete_account_id) 545 form_ui = xml_tools.XMLUI(
431 form_ui.addText(D_("If you confirm this dialog, you will be disconnected and then your XMPP account AND your SàT profile will both be DELETED.")) 546 "form", title=D_("Delete your account?"), submit_id=self.__delete_account_id
432 target = D_('contact list, messages history, blog posts and comments' if 'GROUPBLOG' in self.host.plugins else D_('contact list and messages history')) 547 )
433 form_ui.addText(D_("All your data stored on %(server)s, including your %(target)s will be erased.") % {'server': self.getNewAccountDomain(), 'target': target}) 548 form_ui.addText(
434 form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?")) 549 D_(
435 return {'xmlui': form_ui.toXml()} 550 "If you confirm this dialog, you will be disconnected and then your XMPP account AND your SàT profile will both be DELETED."
551 )
552 )
553 target = D_(
554 "contact list, messages history, blog posts and comments"
555 if "GROUPBLOG" in self.host.plugins
556 else D_("contact list and messages history")
557 )
558 form_ui.addText(
559 D_(
560 "All your data stored on %(server)s, including your %(target)s will be erased."
561 )
562 % {"server": self.getNewAccountDomain(), "target": target}
563 )
564 form_ui.addText(
565 D_(
566 "There is no other confirmation dialog, this is the very last one! Are you sure?"
567 )
568 )
569 return {"xmlui": form_ui.toXml()}
436 570
437 def __deleteAccountCb(self, data, profile): 571 def __deleteAccountCb(self, data, profile):
438 """Actually delete the XMPP account and SàT profile 572 """Actually delete the XMPP account and SàT profile
439 573
440 @param data 574 @param data
441 @param profile 575 @param profile
442 """ 576 """
443 client = self.host.getClient(profile) 577 client = self.host.getClient(profile)
578
444 def userDeleted(dummy): 579 def userDeleted(dummy):
445 580
446 # FIXME: client should be disconnected at this point, so 2 next loop should be removed (to be confirmed) 581 # FIXME: client should be disconnected at this point, so 2 next loop should be removed (to be confirmed)
447 for jid_ in client.roster._jids: # empty roster 582 for jid_ in client.roster._jids: # empty roster
448 client.presence.unsubscribe(jid_) 583 client.presence.unsubscribe(jid_)
449 584
450 for jid_ in self.host.memory.getWaitingSub(profile): # delete waiting subscriptions 585 for jid_ in self.host.memory.getWaitingSub(
586 profile
587 ): # delete waiting subscriptions
451 self.host.memory.delWaitingSub(jid_) 588 self.host.memory.delWaitingSub(jid_)
452 589
453 delete_profile = lambda: self.host.memory.asyncDeleteProfile(profile, force=True) 590 delete_profile = lambda: self.host.memory.asyncDeleteProfile(
454 if 'GROUPBLOG' in self.host.plugins: 591 profile, force=True
455 d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogsAndComments(profile_key=profile) 592 )
593 if "GROUPBLOG" in self.host.plugins:
594 d = self.host.plugins["GROUPBLOG"].deleteAllGroupBlogsAndComments(
595 profile_key=profile
596 )
456 d.addCallback(lambda dummy: delete_profile()) 597 d.addCallback(lambda dummy: delete_profile())
457 else: 598 else:
458 delete_profile() 599 delete_profile()
459 600
460 return defer.succeed({}) 601 return defer.succeed({})
461 602
462 def errback(failure): 603 def errback(failure):
463 error_ui = xml_tools.XMLUI("popup", title=D_("Error")) 604 error_ui = xml_tools.XMLUI("popup", title=D_("Error"))
464 error_ui.addText(D_("Your XMPP account could not be deleted: %s") % failure.getErrorMessage()) 605 error_ui.addText(
465 return defer.succeed({'xmlui': error_ui.toXml()}) 606 D_("Your XMPP account could not be deleted: %s")
466 607 % failure.getErrorMessage()
467 d = self.host.plugins['XEP-0077'].unregister(client, jid.JID(client.jid.host)) 608 )
609 return defer.succeed({"xmlui": error_ui.toXml()})
610
611 d = self.host.plugins["XEP-0077"].unregister(client, jid.JID(client.jid.host))
468 d.addCallbacks(userDeleted, errback) 612 d.addCallbacks(userDeleted, errback)
469 return d 613 return d
470 614
471 def __deleteBlogPosts(self, posts, comments, profile): 615 def __deleteBlogPosts(self, posts, comments, profile):
472 """Ask for a confirmation before deleting the blog posts 616 """Ask for a confirmation before deleting the blog posts
475 @param data 619 @param data
476 @param profile 620 @param profile
477 """ 621 """
478 if posts: 622 if posts:
479 if comments: # delete everything 623 if comments: # delete everything
480 form_ui = xml_tools.XMLUI("form", title=D_("Delete all your (micro-)blog posts and comments?"), submit_id=self.__delete_posts_comments_id) 624 form_ui = xml_tools.XMLUI(
481 form_ui.addText(D_("If you confirm this dialog, all the (micro-)blog data you submitted will be erased.")) 625 "form",
482 form_ui.addText(D_("These are the public and private posts and comments you sent to any group.")) 626 title=D_("Delete all your (micro-)blog posts and comments?"),
483 form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?")) 627 submit_id=self.__delete_posts_comments_id,
628 )
629 form_ui.addText(
630 D_(
631 "If you confirm this dialog, all the (micro-)blog data you submitted will be erased."
632 )
633 )
634 form_ui.addText(
635 D_(
636 "These are the public and private posts and comments you sent to any group."
637 )
638 )
639 form_ui.addText(
640 D_(
641 "There is no other confirmation dialog, this is the very last one! Are you sure?"
642 )
643 )
484 else: # delete only the posts 644 else: # delete only the posts
485 form_ui = xml_tools.XMLUI("form", title=D_("Delete all your (micro-)blog posts?"), submit_id=self.__delete_posts_id) 645 form_ui = xml_tools.XMLUI(
486 form_ui.addText(D_("If you confirm this dialog, all the public and private posts you sent to any group will be erased.")) 646 "form",
487 form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?")) 647 title=D_("Delete all your (micro-)blog posts?"),
648 submit_id=self.__delete_posts_id,
649 )
650 form_ui.addText(
651 D_(
652 "If you confirm this dialog, all the public and private posts you sent to any group will be erased."
653 )
654 )
655 form_ui.addText(
656 D_(
657 "There is no other confirmation dialog, this is the very last one! Are you sure?"
658 )
659 )
488 elif comments: # delete only the comments 660 elif comments: # delete only the comments
489 form_ui = xml_tools.XMLUI("form", title=D_("Delete all your (micro-)blog comments?"), submit_id=self.__delete_comments_id) 661 form_ui = xml_tools.XMLUI(
490 form_ui.addText(D_("If you confirm this dialog, all the public and private comments you made on other people's posts will be erased.")) 662 "form",
491 form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?")) 663 title=D_("Delete all your (micro-)blog comments?"),
492 664 submit_id=self.__delete_comments_id,
493 return {'xmlui': form_ui.toXml()} 665 )
666 form_ui.addText(
667 D_(
668 "If you confirm this dialog, all the public and private comments you made on other people's posts will be erased."
669 )
670 )
671 form_ui.addText(
672 D_(
673 "There is no other confirmation dialog, this is the very last one! Are you sure?"
674 )
675 )
676
677 return {"xmlui": form_ui.toXml()}
494 678
495 def __deleteBlogPostsCb(self, posts, comments, data, profile): 679 def __deleteBlogPostsCb(self, posts, comments, data, profile):
496 """Actually delete the XMPP account and SàT profile 680 """Actually delete the XMPP account and SàT profile
497 @param posts: delete all posts of the user (and their comments) 681 @param posts: delete all posts of the user (and their comments)
498 @param comments: delete all the comments of the user on other's posts 682 @param comments: delete all the comments of the user on other's posts
499 @param profile 683 @param profile
500 """ 684 """
501 if posts: 685 if posts:
502 if comments: 686 if comments:
503 target = D_('blog posts and comments') 687 target = D_("blog posts and comments")
504 d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogsAndComments(profile_key=profile) 688 d = self.host.plugins["GROUPBLOG"].deleteAllGroupBlogsAndComments(
689 profile_key=profile
690 )
505 else: 691 else:
506 target = D_('blog posts') 692 target = D_("blog posts")
507 d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogs(profile_key=profile) 693 d = self.host.plugins["GROUPBLOG"].deleteAllGroupBlogs(
694 profile_key=profile
695 )
508 elif comments: 696 elif comments:
509 target = D_('comments') 697 target = D_("comments")
510 d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogsComments(profile_key=profile) 698 d = self.host.plugins["GROUPBLOG"].deleteAllGroupBlogsComments(
699 profile_key=profile
700 )
511 701
512 def deleted(result): 702 def deleted(result):
513 ui = xml_tools.XMLUI("popup", title=D_("Deletion confirmation")) 703 ui = xml_tools.XMLUI("popup", title=D_("Deletion confirmation"))
514 # TODO: change the message when delete/retract notifications are done with XEP-0060 704 # TODO: change the message when delete/retract notifications are done with XEP-0060
515 ui.addText(D_("Your %(target)s have been deleted.") % {'target': target}) 705 ui.addText(D_("Your %(target)s have been deleted.") % {"target": target})
516 ui.addText(D_("Known issue of the demo version: you need to refresh the page to make the deleted posts actually disappear.")) 706 ui.addText(
517 return defer.succeed({'xmlui': ui.toXml()}) 707 D_(
708 "Known issue of the demo version: you need to refresh the page to make the deleted posts actually disappear."
709 )
710 )
711 return defer.succeed({"xmlui": ui.toXml()})
518 712
519 def errback(failure): 713 def errback(failure):
520 error_ui = xml_tools.XMLUI("popup", title=D_("Error")) 714 error_ui = xml_tools.XMLUI("popup", title=D_("Error"))
521 error_ui.addText(D_("Your %(target)s could not be deleted: %(message)s") % {'target': target, 'message': failure.getErrorMessage()}) 715 error_ui.addText(
522 return defer.succeed({'xmlui': error_ui.toXml()}) 716 D_("Your %(target)s could not be deleted: %(message)s")
717 % {"target": target, "message": failure.getErrorMessage()}
718 )
719 return defer.succeed({"xmlui": error_ui.toXml()})
523 720
524 d.addCallbacks(deleted, errback) 721 d.addCallbacks(deleted, errback)
525 return d 722 return d
526 723
527 def asyncConnectWithXMPPCredentials(self, jid_s, password): 724 def asyncConnectWithXMPPCredentials(self, jid_s, password):
539 pass 736 pass
540 else: 737 else:
541 raise exceptions.ConflictError 738 raise exceptions.ConflictError
542 739
543 d = self.createProfile(password, jid_s, jid_s) 740 d = self.createProfile(password, jid_s, jid_s)
544 d.addCallback(lambda dummy: self.host.memory.getProfileName(jid_s)) # checks if the profile has been successfuly created 741 d.addCallback(
742 lambda dummy: self.host.memory.getProfileName(jid_s)
743 ) # checks if the profile has been successfuly created
545 d.addCallback(self.host.connect, password, {}, 0) 744 d.addCallback(self.host.connect, password, {}, 0)
546
547 745
548 def connected(result): 746 def connected(result):
549 self.sendEmails(None, profile=jid_s) 747 self.sendEmails(None, profile=jid_s)
550 return result 748 return result
551 749
552 def removeProfile(failure): # profile has been successfully created but the XMPP credentials are wrong! 750 def removeProfile(
553 log.debug("Removing previously auto-created profile: %s" % failure.getErrorMessage()) 751 failure
752 ): # profile has been successfully created but the XMPP credentials are wrong!
753 log.debug(
754 "Removing previously auto-created profile: %s" % failure.getErrorMessage()
755 )
554 self.host.memory.asyncDeleteProfile(jid_s) 756 self.host.memory.asyncDeleteProfile(jid_s)
555 raise failure 757 raise failure
556 758
557 # FIXME: we don't catch the case where the JID host is not an XMPP server, and the user 759 # FIXME: we don't catch the case where the JID host is not an XMPP server, and the user
558 # has to wait until the DBUS timeout ; as a consequence, emails are sent to the admins 760 # has to wait until the DBUS timeout ; as a consequence, emails are sent to the admins