comparison sat/plugins/plugin_misc_account.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 420897488080
children 82eee2c383d9
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT plugin for account creation (experimental) 4 # SAT plugin for account creation (experimental)
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org)
6 6
24 from sat.core import exceptions 24 from sat.core import exceptions
25 from sat.tools import xml_tools 25 from sat.tools import xml_tools
26 from sat.memory.memory import Sessions 26 from sat.memory.memory import Sessions
27 from sat.memory.crypto import PasswordHasher 27 from sat.memory.crypto import PasswordHasher
28 from sat.core.constants import Const as C 28 from sat.core.constants import Const as C
29 import ConfigParser 29 import configparser
30 from twisted.internet import defer 30 from twisted.internet import defer
31 from twisted.python.failure import Failure 31 from twisted.python.failure import Failure
32 from twisted.words.protocols.jabber import jid 32 from twisted.words.protocols.jabber import jid
33 from sat.tools.common import email as sat_email 33 from sat.tools.common import email as sat_email
34 34
43 C.PI_PROTOCOLS: [], 43 C.PI_PROTOCOLS: [],
44 C.PI_DEPENDENCIES: ["XEP-0077"], 44 C.PI_DEPENDENCIES: ["XEP-0077"],
45 C.PI_RECOMMENDATIONS: ["GROUPBLOG"], 45 C.PI_RECOMMENDATIONS: ["GROUPBLOG"],
46 C.PI_MAIN: "MiscAccount", 46 C.PI_MAIN: "MiscAccount",
47 C.PI_HANDLER: "no", 47 C.PI_HANDLER: "no",
48 C.PI_DESCRIPTION: _(u"""SàT account creation"""), 48 C.PI_DESCRIPTION: _("""SàT account creation"""),
49 } 49 }
50 50
51 CONFIG_SECTION = "plugin account" 51 CONFIG_SECTION = "plugin account"
52 52
53 # You need do adapt the following consts to your server 53 # You need do adapt the following consts to your server
69 "new_account_domain": "", #  use xmpp_domain if not found 69 "new_account_domain": "", #  use xmpp_domain if not found
70 "reserved_list": ["libervia"], # profiles which can't be used 70 "reserved_list": ["libervia"], # profiles which can't be used
71 } 71 }
72 72
73 WELCOME_MSG = D_( 73 WELCOME_MSG = D_(
74 u"""Welcome to Libervia, the web interface of Salut à Toi. 74 """Welcome to Libervia, the web interface of Salut à Toi.
75 75
76 Your account on {domain} has been successfully created. 76 Your account on {domain} has been successfully created.
77 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.
78 It is still under development, please keep it in mind! 78 It is still under development, please keep it in mind!
79 79
92 Salut à Toi association 92 Salut à Toi association
93 https://www.salut-a-toi.org 93 https://www.salut-a-toi.org
94 """ 94 """
95 ) 95 )
96 96
97 DEFAULT_DOMAIN = u"example.net" 97 DEFAULT_DOMAIN = "example.net"
98 98
99 99
100 class MiscAccount(object): 100 class MiscAccount(object):
101 """Account plugin: create a SàT + XMPP account, used by Libervia""" 101 """Account plugin: create a SàT + XMPP account, used by Libervia"""
102 102
103 # 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.
104 # TODO: cleaning, separate email handling, more configuration/tests, fixes 104 # TODO: cleaning, separate email handling, more configuration/tests, fixes
105 105
106 def __init__(self, host): 106 def __init__(self, host):
107 log.info(_(u"Plugin Account initialization")) 107 log.info(_("Plugin Account initialization"))
108 self.host = host 108 self.host = host
109 host.bridge.addMethod( 109 host.bridge.addMethod(
110 "registerSatAccount", 110 "registerSatAccount",
111 ".plugin", 111 ".plugin",
112 in_sign="sss", 112 in_sign="sss",
113 out_sign="", 113 out_sign="",
114 method=self._registerAccount, 114 method=self._registerAccount,
115 async=True, 115 async_=True,
116 ) 116 )
117 host.bridge.addMethod( 117 host.bridge.addMethod(
118 "getNewAccountDomain", 118 "getNewAccountDomain",
119 ".plugin", 119 ".plugin",
120 in_sign="", 120 in_sign="",
121 out_sign="s", 121 out_sign="s",
122 method=self.getNewAccountDomain, 122 method=self.getNewAccountDomain,
123 async=False, 123 async_=False,
124 ) 124 )
125 host.bridge.addMethod( 125 host.bridge.addMethod(
126 "getAccountDialogUI", 126 "getAccountDialogUI",
127 ".plugin", 127 ".plugin",
128 in_sign="s", 128 in_sign="s",
129 out_sign="s", 129 out_sign="s",
130 method=self._getAccountDialogUI, 130 method=self._getAccountDialogUI,
131 async=False, 131 async_=False,
132 ) 132 )
133 host.bridge.addMethod( 133 host.bridge.addMethod(
134 "asyncConnectWithXMPPCredentials", 134 "asyncConnectWithXMPPCredentials",
135 ".plugin", 135 ".plugin",
136 in_sign="ss", 136 in_sign="ss",
137 out_sign="b", 137 out_sign="b",
138 method=self.asyncConnectWithXMPPCredentials, 138 method=self.asyncConnectWithXMPPCredentials,
139 async=True, 139 async_=True,
140 ) 140 )
141 141
142 self.fixEmailAdmins() 142 self.fixEmailAdmins()
143 self._sessions = Sessions() 143 self._sessions = Sessions()
144 144
173 """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"""
174 admin_email = self.getConfig("admin_email") 174 admin_email = self.getConfig("admin_email")
175 if not admin_email: 175 if not admin_email:
176 return 176 return
177 log.warning( 177 log.warning(
178 u"admin_email parameter is deprecated, please use email_admins_list instead" 178 "admin_email parameter is deprecated, please use email_admins_list instead"
179 ) 179 )
180 param_name = "email_admins_list" 180 param_name = "email_admins_list"
181 try: 181 try:
182 section = "" 182 section = ""
183 value = self.host.memory.getConfig(section, param_name, Exception) 183 value = self.host.memory.getConfig(section, param_name, Exception)
184 except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): 184 except (configparser.NoOptionError, configparser.NoSectionError):
185 section = CONFIG_SECTION 185 section = CONFIG_SECTION
186 value = self.host.memory.getConfig( 186 value = self.host.memory.getConfig(
187 section, param_name, default_conf[param_name] 187 section, param_name, default_conf[param_name]
188 ) 188 )
189 189
196 # XXX: email_ parameters were first in [plugin account] section 196 # XXX: email_ parameters were first in [plugin account] section
197 # but as it make more sense to have them in common with other plugins, 197 # but as it make more sense to have them in common with other plugins,
198 # they can now be in [DEFAULT] section 198 # they can now be in [DEFAULT] section
199 try: 199 try:
200 value = self.host.memory.getConfig(None, name, Exception) 200 value = self.host.memory.getConfig(None, name, Exception)
201 except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): 201 except (configparser.NoOptionError, configparser.NoSectionError):
202 pass 202 pass
203 else: 203 else:
204 return value 204 return value
205 205
206 if section == CONFIG_SECTION: 206 if section == CONFIG_SECTION:
261 """ 261 """
262 if jid_s: 262 if jid_s:
263 d = defer.succeed(None) 263 d = defer.succeed(None)
264 jid_ = jid.JID(jid_s) 264 jid_ = jid.JID(jid_s)
265 else: 265 else:
266 jid_s = profile + u"@" + self.getNewAccountDomain() 266 jid_s = profile + "@" + self.getNewAccountDomain()
267 jid_ = jid.JID(jid_s) 267 jid_ = jid.JID(jid_s)
268 d = self.host.plugins["XEP-0077"].registerNewAccount(jid_, password) 268 d = self.host.plugins["XEP-0077"].registerNewAccount(jid_, password)
269 269
270 def setParams(__): 270 def setParams(__):
271 self.host.memory.setParam( 271 self.host.memory.setParam(
287 return d 287 return d
288 288
289 def _sendEmailEb(self, failure_, email): 289 def _sendEmailEb(self, failure_, email):
290 # TODO: return error code to user 290 # TODO: return error code to user
291 log.error( 291 log.error(
292 _(u"Failed to send account creation confirmation to {email}: {msg}").format( 292 _("Failed to send account creation confirmation to {email}: {msg}").format(
293 email=email, msg=failure_ 293 email=email, msg=failure_
294 ) 294 )
295 ) 295 )
296 296
297 def sendEmails(self, email, profile): 297 def sendEmails(self, email, profile):
301 301
302 # email to the administrators 302 # email to the administrators
303 admins_emails = self.getConfig("email_admins_list") 303 admins_emails = self.getConfig("email_admins_list")
304 if not admins_emails: 304 if not admins_emails:
305 log.warning( 305 log.warning(
306 u"No known admin email, we can't send email to administrator(s).\nPlease fill email_admins_list parameter" 306 "No known admin email, we can't send email to administrator(s).\nPlease fill email_admins_list parameter"
307 ) 307 )
308 d_admin = defer.fail(exceptions.DataError("no admin email")) 308 d_admin = defer.fail(exceptions.DataError("no admin email"))
309 else: 309 else:
310 subject = _(u"New Libervia account created") 310 subject = _("New Libervia account created")
311 body = u"""New account created: {profile} [{email}]""".format( 311 body = """New account created: {profile} [{email}]""".format(
312 profile=profile, 312 profile=profile,
313 # there is no email when an existing XMPP account is used 313 # there is no email when an existing XMPP account is used
314 email=email or u"<no email>", 314 email=email or "<no email>",
315 ) 315 )
316 d_admin = sat_email.sendEmail(self.host, admins_emails, subject, body) 316 d_admin = sat_email.sendEmail(self.host, admins_emails, subject, body)
317 317
318 admins_emails_txt = u", ".join([u"<" + addr + u">" for addr in admins_emails]) 318 admins_emails_txt = ", ".join(["<" + addr + ">" for addr in admins_emails])
319 d_admin.addCallbacks( 319 d_admin.addCallbacks(
320 lambda __: log.debug( 320 lambda __: log.debug(
321 u"Account creation notification sent to admin(s) {}".format( 321 "Account creation notification sent to admin(s) {}".format(
322 admins_emails_txt 322 admins_emails_txt
323 ) 323 )
324 ), 324 ),
325 lambda __: log.error( 325 lambda __: log.error(
326 u"Failed to send account creation notification to admin {}".format( 326 "Failed to send account creation notification to admin {}".format(
327 admins_emails_txt 327 admins_emails_txt
328 ) 328 )
329 ), 329 ),
330 ) 330 )
331 if not email: 331 if not email:
332 # 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
333 return d_admin 333 return d_admin
334 334
335 jid_s = self.host.memory.getParamA( 335 jid_s = self.host.memory.getParamA(
336 u"JabberID", u"Connection", profile_key=profile 336 "JabberID", "Connection", profile_key=profile
337 ) 337 )
338 subject = _(u"Your Libervia account has been created") 338 subject = _("Your Libervia account has been created")
339 body = _(WELCOME_MSG).format(profile=profile, jid=jid_s, domain=domain) 339 body = _(WELCOME_MSG).format(profile=profile, jid=jid_s, domain=domain)
340 340
341 # 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
342 # FIXME: check email reception to validate email given by the user 342 # FIXME: check email reception to validate email given by the user
343 # FIXME: delete the profile if the email could not been sent? 343 # FIXME: delete the profile if the email could not been sent?
344 d_user = sat_email.sendEmail(self.host, [email], subject, body) 344 d_user = sat_email.sendEmail(self.host, [email], subject, body)
345 d_user.addCallbacks( 345 d_user.addCallbacks(
346 lambda __: log.debug( 346 lambda __: log.debug(
347 u"Account creation confirmation sent to <{}>".format(email) 347 "Account creation confirmation sent to <{}>".format(email)
348 ), 348 ),
349 self._sendEmailEb, 349 self._sendEmailEb,
350 ) 350 )
351 return defer.DeferredList([d_user, d_admin]) 351 return defer.DeferredList([d_user, d_admin])
352 352
357 "xmpp_domain", None 357 "xmpp_domain", None
358 ) 358 )
359 if not domain: 359 if not domain:
360 log.warning( 360 log.warning(
361 _( 361 _(
362 u'xmpp_domain needs to be set in sat.conf. Using "{default}" meanwhile' 362 'xmpp_domain needs to be set in sat.conf. Using "{default}" meanwhile'
363 ).format(default=DEFAULT_DOMAIN) 363 ).format(default=DEFAULT_DOMAIN)
364 ) 364 )
365 return DEFAULT_DOMAIN 365 return DEFAULT_DOMAIN
366 return domain 366 return domain
367 367