Mercurial > libervia-backend
comparison src/plugins/plugin_misc_account.py @ 1653:200efadcab76
plugin misc_account: add method asyncConnectWithXMPPCredentials
author | souliane <souliane@mailoo.org> |
---|---|
date | Mon, 23 Nov 2015 18:50:02 +0100 |
parents | 5d42e2219d7c |
children | cec204c6360c |
comparison
equal
deleted
inserted
replaced
1652:fd7f41d8cbdf | 1653:200efadcab76 |
---|---|
30 from twisted.python.procutils import which | 30 from twisted.python.procutils import which |
31 from twisted.python.failure import Failure | 31 from twisted.python.failure import Failure |
32 from twisted.mail.smtp import sendmail | 32 from twisted.mail.smtp import sendmail |
33 from os.path import join, dirname | 33 from os.path import join, dirname |
34 from email.mime.text import MIMEText | 34 from email.mime.text import MIMEText |
35 | |
36 import random | |
37 | |
35 | 38 |
36 PLUGIN_INFO = { | 39 PLUGIN_INFO = { |
37 "name": "Account Plugin", | 40 "name": "Account Plugin", |
38 "import_name": "MISC-ACCOUNT", | 41 "import_name": "MISC-ACCOUNT", |
39 "type": "MISC", | 42 "type": "MISC", |
54 default_conf = {"email_from": "NOREPLY@example.net", | 57 default_conf = {"email_from": "NOREPLY@example.net", |
55 "email_server": "localhost", | 58 "email_server": "localhost", |
56 "admin_email": "admin@example.net", | 59 "admin_email": "admin@example.net", |
57 "new_account_server": "localhost", | 60 "new_account_server": "localhost", |
58 "new_account_domain": "example.net", | 61 "new_account_domain": "example.net", |
59 "new_account_resource": "libervia", | |
60 "prosody_path": None, # prosody path (where prosodyctl will be executed from), or None to automaticaly find it | 62 "prosody_path": None, # prosody path (where prosodyctl will be executed from), or None to automaticaly find it |
61 "prosodyctl": "prosodyctl", | 63 "prosodyctl": "prosodyctl", |
62 "reserved_list": ['libervia'] # profiles which can't be used | 64 "reserved_list": ['libervia'] # profiles which can't be used |
63 } | 65 } |
64 | 66 |
100 self.deferred.errback(Failure(exceptions.InternalError)) | 102 self.deferred.errback(Failure(exceptions.InternalError)) |
101 | 103 |
102 @classmethod | 104 @classmethod |
103 def prosodyctl(cls, plugin, command, password=None, profile=None): | 105 def prosodyctl(cls, plugin, command, password=None, profile=None): |
104 """Create a new ProsodyRegisterProtocol and execute the given prosodyctl command. | 106 """Create a new ProsodyRegisterProtocol and execute the given prosodyctl command. |
107 | |
105 @param plugin: instance of MiscAccount | 108 @param plugin: instance of MiscAccount |
106 @param command: the command to execute: "adduser", "passwd" or "deluser" | 109 @param command: the command to execute: "adduser", "passwd" or "deluser" |
107 @param password: the user new password (leave to None for "deluser" command) | 110 @param password: the user new password (leave to None for "deluser" command) |
108 @param profile: the user profile | 111 @param profile: the user profile |
109 @return: a Deferred instance | 112 @return: a Deferred instance |
125 log.info(_(u"Plugin Account initialization")) | 128 log.info(_(u"Plugin Account initialization")) |
126 self.host = host | 129 self.host = host |
127 host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async=True) | 130 host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async=True) |
128 host.bridge.addMethod("getNewAccountDomain", ".plugin", in_sign='', out_sign='s', method=self.getNewAccountDomain, async=False) | 131 host.bridge.addMethod("getNewAccountDomain", ".plugin", in_sign='', out_sign='s', method=self.getNewAccountDomain, async=False) |
129 host.bridge.addMethod("getAccountDialogUI", ".plugin", in_sign='s', out_sign='s', method=self._getAccountDialogUI, async=False) | 132 host.bridge.addMethod("getAccountDialogUI", ".plugin", in_sign='s', out_sign='s', method=self._getAccountDialogUI, async=False) |
133 host.bridge.addMethod("asyncConnectWithXMPPCredentials", ".plugin", in_sign='ss', out_sign='b', method=self.asyncConnectWithXMPPCredentials, async=True) | |
130 self._prosody_path = self.getConfig('prosody_path') | 134 self._prosody_path = self.getConfig('prosody_path') |
131 if self._prosody_path is None: | 135 if self._prosody_path is None: |
132 paths = which(self.getConfig('prosodyctl')) | 136 paths = which(self.getConfig('prosodyctl')) |
133 if not paths: | 137 if not paths: |
134 log.error(_(u"Can't find %s") % (self.getConfig('prosodyctl'),)) | 138 log.error(_(u"Can't find %s") % (self.getConfig('prosodyctl'),)) |
151 self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True) | 155 self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True) |
152 | 156 |
153 def getConfig(self, name): | 157 def getConfig(self, name): |
154 return self.host.memory.getConfig(CONFIG_SECTION, name, default_conf[name]) | 158 return self.host.memory.getConfig(CONFIG_SECTION, name, default_conf[name]) |
155 | 159 |
160 def generatePassword(self): | |
161 """Generate a password with random characters. | |
162 | |
163 @return unicode | |
164 """ | |
165 random.seed() | |
166 #charset = [chr(i) for i in range(0x21,0x7F)] #XXX: this charset seems to have some issues with openfire | |
167 charset = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)] | |
168 return ''.join([random.choice(charset) for i in range(15)]) | |
169 | |
156 def _registerAccount(self, email, password, profile): | 170 def _registerAccount(self, email, password, profile): |
157 | 171 return self.registerAccount(email, password, None, profile) |
158 """ | 172 |
159 #Password Generation | 173 def registerAccount(self, email, password, jid_s, profile): |
160 #_charset = [chr(i) for i in range(0x21,0x7F)] #XXX: this charset seems to have some issues with openfire | 174 """Register a new profile and its associated XMPP account. |
161 _charset = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)] | 175 |
162 import random | 176 @param email (unicode): where to send to confirmation email to |
163 random.seed() | 177 @param password (unicode): password chosen by the user |
164 password = ''.join([random.choice(_charset) for i in range(15)]) | 178 @param jid_s (unicode): JID to re-use or to register: |
165 """ | 179 - non empty value: bind this JID to the new sat profile |
166 if not email or not password or not profile: | 180 - None or "": register a new JID on the local XMPP server |
181 @param profile | |
182 @return Deferred | |
183 """ | |
184 if not password or not profile: | |
167 raise exceptions.DataError | 185 raise exceptions.DataError |
168 | 186 |
169 if profile.lower() in self.getConfig('reserved_list'): | 187 if profile.lower() in self.getConfig('reserved_list'): |
170 return defer.fail(Failure(exceptions.ConflictError)) | 188 return defer.fail(Failure(exceptions.ConflictError)) |
171 | 189 |
172 d = self.host.memory.asyncCreateProfile(profile, password) | 190 d = self.host.memory.asyncCreateProfile(profile, password) |
173 d.addCallback(self._profileRegistered, email, password, profile) | 191 d.addCallback(lambda dummy: self.profileRegistered(email, password, jid_s, profile)) |
174 return d | 192 return d |
175 | 193 |
176 def _profileRegistered(self, result, email, password, profile): | 194 def profileRegistered(self, email, password, jid_s, profile): |
177 """Create the XMPP account and send the confirmation email. | 195 """Create the XMPP account, set the profile connection parameters and send the confirmation email. |
178 | 196 |
179 @param result: result of asyncCreateProfile | 197 @param email (unicode): where to send to confirmation email to |
180 @param email: user email | 198 @param password (unicode): password chosen by the user |
181 @param password: chosen xmpp password | 199 @param jid_s (unicode): JID to re-use or to register: |
182 @param profile: %(doc_profile)s | 200 - non empty value: bind this JID to the new sat profile |
183 @return: a deferred None value when all the processing is done | 201 - None or empty: register a new JID on the local XMPP server |
184 """ | 202 @param profile |
185 # XXX: we use "prosodyctl adduser" because "register" doesn't check conflict | 203 @return: Deferred |
186 # and just change the password if the account already exists | 204 """ |
187 d = ProsodyRegisterProtocol.prosodyctl(self, 'adduser', password, profile) | 205 # XXX: we use "prosodyctl adduser" because "register" doesn't check conflict and just change the password if the account already exists |
188 d.addCallback(self._sendEmails, profile, email, password) | 206 if jid_s: |
189 d.addCallback(lambda ignore: None) | 207 d = defer.succeed(None) |
208 else: | |
209 d = ProsodyRegisterProtocol.prosodyctl(self, 'adduser', password, profile) | |
210 jid_s = "%s@%s" % (profile, self.getConfig('new_account_domain')) | |
211 if email: | |
212 d.addCallback(lambda dummy: self.sendEmails(email, jid_s, password, profile)) | |
190 | 213 |
191 def setParams(dummy): | 214 def setParams(dummy): |
192 jid_s = "%s@%s/%s" % (profile, self.getConfig('new_account_domain'), self.getConfig('new_account_resource')) | |
193 self.host.memory.setParam("JabberID", jid_s, "Connection", profile_key=profile) | 215 self.host.memory.setParam("JabberID", jid_s, "Connection", profile_key=profile) |
194 d = self.host.memory.setParam("Password", password, "Connection", profile_key=profile) | 216 d = self.host.memory.setParam("Password", password, "Connection", profile_key=profile) |
195 d.addCallback(lambda dummy: self.host.memory.auth_sessions.profileDelUnique(profile)) | 217 return d.addCallback(lambda dummy: self.host.memory.auth_sessions.profileDelUnique(profile)) |
196 | 218 |
197 def removeProfile(failure): | 219 def removeProfile(failure): |
198 self.host.memory.asyncDeleteProfile(profile) | 220 self.host.memory.asyncDeleteProfile(profile) |
199 return failure | 221 return failure |
200 | 222 |
201 d.addCallback(setParams) | 223 d.addCallback(setParams) |
202 d.addErrback(removeProfile) | 224 d.addErrback(removeProfile) |
203 return d | 225 return d |
204 | 226 |
205 def _sendEmails(self, result, login, email, password): | 227 def sendEmails(self, email, jid_s, password, profile): |
206 # time to send the email | 228 # time to send the email |
207 | 229 |
208 _email_host = self.getConfig('email_server') | 230 _email_host = self.getConfig('email_server') |
209 _email_from = self.getConfig("email_from") | 231 _email_from = self.getConfig("email_from") |
210 domain = self.getConfig('new_account_domain') | 232 domain = self.getConfig('new_account_domain') |
211 | 233 |
234 | |
212 def email_ok(ignore): | 235 def email_ok(ignore): |
213 log.debug(u"Account creation email sent to %s" % email) | 236 log.debug(u"Account creation email sent to %s" % email) |
214 | 237 |
215 def email_ko(ignore): | 238 def email_ko(ignore): |
216 # TODO: return error code to user | 239 # TODO: return error code to user |
220 | 243 |
221 Your account on %(domain)s has been successfully created. This is a demonstration version to show you the current status of the project. It is still under development, it misses some features and it's not stable enough to be used for any serious purpose. Please keep it in mind! | 244 Your account on %(domain)s has been successfully created. This is a demonstration version to show you the current status of the project. It is still under development, it misses some features and it's not stable enough to be used for any serious purpose. Please keep it in mind! |
222 | 245 |
223 Here is your connection information: | 246 Here is your connection information: |
224 | 247 |
225 Login on libervia.org: %(login)s | 248 Login on libervia.org: %(profile)s |
226 Jabber ID (JID): %(login)s@%(domain)s | 249 Jabber ID (JID): %(jid_s)s |
227 | 250 |
228 Your password has been chosen by yourself. If you haven't read our security notice regarding the connection to https://libervia.org, please do it now: | 251 Your password has been chosen by yourself. If you haven't read our security notice regarding the connection to https://libervia.org, please do it now: |
229 | 252 |
230 http://salut-a-toi.org/faq.html#certificate | 253 http://salut-a-toi.org/faq.html#certificate |
231 | 254 |
235 | 258 |
236 Any feedback welcome. Thank you! | 259 Any feedback welcome. Thank you! |
237 | 260 |
238 Salut à Toi association | 261 Salut à Toi association |
239 http://www.salut-a-toi.org | 262 http://www.salut-a-toi.org |
240 """) % {'login': login, 'domain': domain}).encode('utf-8') | 263 """) % {'profile': profile, 'jid_s': jid_s, 'domain': domain}).encode('utf-8') |
241 msg = MIMEText(body, 'plain', 'UTF-8') | 264 msg = MIMEText(body, 'plain', 'UTF-8') |
242 msg['Subject'] = _(u'Libervia account created') | 265 msg['Subject'] = _(u'Libervia account created') |
243 msg['From'] = _email_from | 266 msg['From'] = _email_from |
244 msg['To'] = email | 267 msg['To'] = email |
245 | 268 |
246 d_user = sendmail(_email_host, _email_from, email, msg.as_string()) | 269 d_user = sendmail(_email_host, _email_from, email, msg.as_string()) |
247 d_user.addCallbacks(email_ok, email_ko) | 270 d_user.addCallbacks(email_ok, email_ko) |
248 | 271 |
249 # email to the administrator | 272 # email to the administrator |
250 body = (u"""New account created: %(login)s [%(email)s]""" % {'login': login, 'email': email}).encode('utf-8') | 273 body = (u"""New account created: %(profile)s [%(email)s]""" % {'profile': profile, 'email': email}).encode('utf-8') |
251 msg = MIMEText(body, 'plain', 'UTF-8') | 274 msg = MIMEText(body, 'plain', 'UTF-8') |
252 msg['Subject'] = _('New Libervia account created') | 275 msg['Subject'] = _('New Libervia account created') |
253 msg['From'] = _email_from | 276 msg['From'] = _email_from |
254 msg['To'] = self.getConfig('admin_email') | 277 msg['To'] = self.getConfig('admin_email') |
255 | 278 |
482 return defer.succeed({'xmlui': error_ui.toXml()}) | 505 return defer.succeed({'xmlui': error_ui.toXml()}) |
483 | 506 |
484 d.addCallbacks(deleted, errback) | 507 d.addCallbacks(deleted, errback) |
485 return d | 508 return d |
486 | 509 |
510 def asyncConnectWithXMPPCredentials(self, jid_s, password): | |
511 """Create and connect a new SàT profile using the given XMPP credentials. | |
512 | |
513 Re-use given JID and XMPP password for the profile name and profile password. | |
514 @param jid_s (unicode): JID | |
515 @param password (unicode): XMPP password | |
516 @return Deferred(bool) | |
517 @raise exceptions.PasswordError, exceptions.ConflictError | |
518 """ | |
519 try: # be sure that the profile doesn't exist yet | |
520 self.host.memory.getProfileName(jid_s) | |
521 except exceptions.ProfileUnknownError: | |
522 pass | |
523 else: | |
524 raise exceptions.ConflictError | |
525 | |
526 d = self.registerAccount(None, password, jid_s, jid_s) | |
527 d.addCallback(lambda dummy: self.host.memory.getProfileName(jid_s)) # checks if the profile has been successfuly created | |
528 d.addCallback(self.host.asyncConnect, password) | |
529 | |
530 def removeProfile(failure): # profile has been successfully created but the XMPP credentials are wrong! | |
531 log.debug("Removing previously auto-created profile: %s" % failure.getErrorMessage()) | |
532 self.host.memory.asyncDeleteProfile(jid_s) | |
533 raise failure | |
534 | |
535 d.addErrback(removeProfile) | |
536 return d |