diff 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
line wrap: on
line diff
--- a/src/plugins/plugin_misc_account.py	Mon Nov 23 18:51:15 2015 +0100
+++ b/src/plugins/plugin_misc_account.py	Mon Nov 23 18:50:02 2015 +0100
@@ -33,6 +33,9 @@
 from os.path import join, dirname
 from email.mime.text import MIMEText
 
+import random
+
+
 PLUGIN_INFO = {
     "name": "Account Plugin",
     "import_name": "MISC-ACCOUNT",
@@ -56,7 +59,6 @@
                 "admin_email": "admin@example.net",
                 "new_account_server": "localhost",
                 "new_account_domain": "example.net",
-                "new_account_resource": "libervia",
                 "prosody_path": None, # prosody path (where prosodyctl will be executed from), or None to automaticaly find it
                 "prosodyctl": "prosodyctl",
                 "reserved_list": ['libervia'] # profiles which can't be used
@@ -102,6 +104,7 @@
     @classmethod
     def prosodyctl(cls, plugin, command, password=None, profile=None):
         """Create a new ProsodyRegisterProtocol and execute the given prosodyctl command.
+
         @param plugin: instance of MiscAccount
         @param command: the command to execute: "adduser", "passwd" or "deluser"
         @param password: the user new password (leave to None for "deluser" command)
@@ -127,6 +130,7 @@
         host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async=True)
         host.bridge.addMethod("getNewAccountDomain", ".plugin", in_sign='', out_sign='s', method=self.getNewAccountDomain, async=False)
         host.bridge.addMethod("getAccountDialogUI", ".plugin", in_sign='s', out_sign='s', method=self._getAccountDialogUI, async=False)
+        host.bridge.addMethod("asyncConnectWithXMPPCredentials", ".plugin", in_sign='ss', out_sign='b', method=self.asyncConnectWithXMPPCredentials, async=True)
         self._prosody_path = self.getConfig('prosody_path')
         if self._prosody_path is None:
             paths = which(self.getConfig('prosodyctl'))
@@ -153,46 +157,64 @@
     def getConfig(self, name):
         return self.host.memory.getConfig(CONFIG_SECTION, name, default_conf[name])
 
-    def _registerAccount(self, email, password, profile):
-
+    def generatePassword(self):
+        """Generate a password with random characters.
+    
+        @return unicode
         """
-        #Password Generation
-        #_charset = [chr(i) for i in range(0x21,0x7F)] #XXX: this charset seems to have some issues with openfire
-        _charset = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)]
-        import random
         random.seed()
-        password = ''.join([random.choice(_charset) for i in range(15)])
+        #charset = [chr(i) for i in range(0x21,0x7F)] #XXX: this charset seems to have some issues with openfire
+        charset = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)]
+        return ''.join([random.choice(charset) for i in range(15)])
+
+    def _registerAccount(self, email, password, profile):
+        return self.registerAccount(email, password, None, profile)
+
+    def registerAccount(self, email, password, jid_s, profile):
+        """Register a new profile and its associated XMPP account.
+
+        @param email (unicode): where to send to confirmation email to
+        @param password (unicode): password chosen by the user
+        @param jid_s (unicode): JID to re-use or to register:
+            - non empty value: bind this JID to the new sat profile
+            - None or "": register a new JID on the local XMPP server
+        @param profile
+        @return Deferred 
         """
-        if not email or not password or not profile:
+        if not password or not profile:
             raise exceptions.DataError
 
         if profile.lower() in self.getConfig('reserved_list'):
             return defer.fail(Failure(exceptions.ConflictError))
 
         d = self.host.memory.asyncCreateProfile(profile, password)
-        d.addCallback(self._profileRegistered, email, password, profile)
+        d.addCallback(lambda dummy: self.profileRegistered(email, password, jid_s, profile))
         return d
 
-    def _profileRegistered(self, result, email, password, profile):
-        """Create the XMPP account and send the confirmation email.
+    def profileRegistered(self, email, password, jid_s, profile):
+        """Create the XMPP account, set the profile connection parameters and send the confirmation email.
 
-        @param result: result of asyncCreateProfile
-        @param email: user email
-        @param password: chosen xmpp password
-        @param profile: %(doc_profile)s
-        @return: a deferred None value when all the processing is done
+        @param email (unicode): where to send to confirmation email to
+        @param password (unicode): password chosen by the user
+        @param jid_s (unicode): JID to re-use or to register:
+            - non empty value: bind this JID to the new sat profile
+            - None or empty: register a new JID on the local XMPP server
+        @param profile
+        @return: Deferred
         """
-        # XXX: we use "prosodyctl adduser" because "register" doesn't check conflict
-        #      and just change the password if the account already exists
-        d = ProsodyRegisterProtocol.prosodyctl(self, 'adduser', password, profile)
-        d.addCallback(self._sendEmails, profile, email, password)
-        d.addCallback(lambda ignore: None)
+        # XXX: we use "prosodyctl adduser" because "register" doesn't check conflict and just change the password if the account already exists
+        if jid_s:
+            d = defer.succeed(None)
+        else:
+            d = ProsodyRegisterProtocol.prosodyctl(self, 'adduser', password, profile)
+            jid_s = "%s@%s" % (profile, self.getConfig('new_account_domain'))
+        if email:
+            d.addCallback(lambda dummy: self.sendEmails(email, jid_s, password, profile))
 
         def setParams(dummy):
-            jid_s = "%s@%s/%s" % (profile, self.getConfig('new_account_domain'), self.getConfig('new_account_resource'))
             self.host.memory.setParam("JabberID", jid_s, "Connection", profile_key=profile)
             d = self.host.memory.setParam("Password", password, "Connection", profile_key=profile)
-            d.addCallback(lambda dummy: self.host.memory.auth_sessions.profileDelUnique(profile))
+            return d.addCallback(lambda dummy: self.host.memory.auth_sessions.profileDelUnique(profile))
 
         def removeProfile(failure):
             self.host.memory.asyncDeleteProfile(profile)
@@ -202,13 +224,14 @@
         d.addErrback(removeProfile)
         return d
 
-    def _sendEmails(self, result, login, email, password):
+    def sendEmails(self, email, jid_s, password, profile):
         # time to send the email
 
         _email_host = self.getConfig('email_server')
         _email_from = self.getConfig("email_from")
         domain = self.getConfig('new_account_domain')
 
+
         def email_ok(ignore):
             log.debug(u"Account creation email sent to %s" % email)
 
@@ -222,8 +245,8 @@
 
 Here is your connection information:
 
-Login on libervia.org: %(login)s
-Jabber ID (JID): %(login)s@%(domain)s
+Login on libervia.org: %(profile)s
+Jabber ID (JID): %(jid_s)s
 
 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:
 
@@ -237,7 +260,7 @@
 
 Salut à Toi association
 http://www.salut-a-toi.org
-""") % {'login': login, 'domain': domain}).encode('utf-8')
+""") % {'profile': profile, 'jid_s': jid_s, 'domain': domain}).encode('utf-8')
         msg = MIMEText(body, 'plain', 'UTF-8')
         msg['Subject'] = _(u'Libervia account created')
         msg['From'] = _email_from
@@ -247,7 +270,7 @@
         d_user.addCallbacks(email_ok, email_ko)
 
         # email to the administrator
-        body = (u"""New account created: %(login)s [%(email)s]""" % {'login': login, 'email': email}).encode('utf-8')
+        body = (u"""New account created: %(profile)s [%(email)s]""" % {'profile': profile, 'email': email}).encode('utf-8')
         msg = MIMEText(body, 'plain', 'UTF-8')
         msg['Subject'] = _('New Libervia account created')
         msg['From'] = _email_from
@@ -484,3 +507,30 @@
         d.addCallbacks(deleted, errback)
         return d
 
+    def asyncConnectWithXMPPCredentials(self, jid_s, password):
+        """Create and connect a new SàT profile using the given XMPP credentials.
+        
+        Re-use given JID and XMPP password for the profile name and profile password.
+        @param jid_s (unicode): JID
+        @param password (unicode): XMPP password
+        @return Deferred(bool)
+        @raise exceptions.PasswordError, exceptions.ConflictError 
+        """
+        try:  # be sure that the profile doesn't exist yet
+            self.host.memory.getProfileName(jid_s)
+        except exceptions.ProfileUnknownError:
+            pass
+        else:
+            raise exceptions.ConflictError
+
+        d = self.registerAccount(None, password, jid_s, jid_s)
+        d.addCallback(lambda dummy: self.host.memory.getProfileName(jid_s))  # checks if the profile has been successfuly created
+        d.addCallback(self.host.asyncConnect, password)
+
+        def removeProfile(failure):  # profile has been successfully created but the XMPP credentials are wrong!
+            log.debug("Removing previously auto-created profile: %s" % failure.getErrorMessage())
+            self.host.memory.asyncDeleteProfile(jid_s)
+            raise failure            
+
+        d.addErrback(removeProfile)
+        return d