changeset 2176:61128d260eef

plugin account: removed dependency to Prosody/prosodyctl and properly use in-band registration instead
author Goffi <goffi@goffi.org>
date Thu, 09 Mar 2017 00:06:13 +0100
parents 75002ac33801
children 09cfec4d8d19
files src/core/sat_main.py src/plugins/plugin_misc_account.py
diffstat 2 files changed, 23 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/sat_main.py	Thu Mar 09 00:06:13 2017 +0100
+++ b/src/core/sat_main.py	Thu Mar 09 00:06:13 2017 +0100
@@ -439,7 +439,7 @@
         @return: client or None if it doesn't exist
         @raise exceptions.ProfileKeyUnknown: the profile or profile key doesn't exist
         @raise exceptions.NotFound: client is not available
-            This happen if profile has not been use yet
+            This happen if profile has not been used yet
         """
         profile = self.memory.getProfileName(profile_key)
         if not profile:
--- a/src/plugins/plugin_misc_account.py	Thu Mar 09 00:06:13 2017 +0100
+++ b/src/plugins/plugin_misc_account.py	Thu Mar 09 00:06:13 2017 +0100
@@ -26,22 +26,24 @@
 from sat.memory.crypto import PasswordHasher
 from sat.core.constants import Const as C
 import ConfigParser
-from twisted.internet import reactor, defer, protocol
-from twisted.python.procutils import which
+from twisted.internet import defer
 from twisted.python.failure import Failure
+from twisted.words.protocols.jabber import jid
 from twisted.mail.smtp import sendmail
-from os.path import join, dirname
 from email.mime.text import MIMEText
 
 import random
 
+# FIXME: this plugin code is old and need a cleaning
+# TODO: account deletion/password change need testing
+
 
 PLUGIN_INFO = {
     C.PI_NAME: "Account Plugin",
     C.PI_IMPORT_NAME: "MISC-ACCOUNT",
     C.PI_TYPE: "MISC",
     C.PI_PROTOCOLS: [],
-    C.PI_DEPENDENCIES: [],
+    C.PI_DEPENDENCIES: ["XEP-0077"],
     C.PI_RECOMMENDATIONS: ['GROUPBLOG'],
     C.PI_MAIN: "MiscAccount",
     C.PI_HANDLER: "no",
@@ -66,70 +68,15 @@
                 "admin_email": "",
                 "new_account_server": "localhost",
                 "new_account_domain": "example.net",
-                "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
                }
 
 
-class PasswordsMatchingError(Exception):
-    pass
-
-
-class ProsodyRegisterProtocol(protocol.ProcessProtocol):
-    """ Try to register an account with prosody """
-
-    def __init__(self, password=None, deferred=None):
-        """
-        @param password: new user password
-        @param deferred
-        """
-        self.password = password
-        self.deferred = deferred
-        self.data = ''
-
-    def connectionMade(self):
-        if self.password is None:
-            return
-        self.transport.write("%s\n%s" % ((self.password.encode('utf-8'),) * 2))
-        self.transport.closeStdin()
-
-    def outReceived(self, data):
-        self.data += data
-
-    def errReceived(self, data):
-        self.data += data
+class MiscAccount(object):
+    """Account plugin: create a SàT + XMPP account, used by Libervia"""
+    # XXX: This plugin was initialy a Q&D one used for the demo.
+    # TODO: cleaning, separate email handling, more configuration/tests, fixes
 
-    def processEnded(self, reason):
-        if (reason.value.exitCode == 0):
-            log.info(_('Prosody command succeed'))
-            self.deferred.callback(None)
-        else:
-            log.error(_(u"Can't complete Prosody command (error code: %(code)d): %(message)s") % {'code': reason.value.exitCode, 'message': self.data})
-            self.deferred.errback(Failure(exceptions.InternalError))
-
-    @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)
-        @param profile: the user profile
-        @return: a Deferred instance
-        """
-        d = defer.Deferred()
-        prosody_reg = ProsodyRegisterProtocol(password, d)
-        prosody_exe = join(plugin._prosody_path, plugin.getConfig('prosodyctl'))
-        # TODO delete account which are not on the same host
-        reactor.spawnProcess(prosody_reg, prosody_exe, [prosody_exe, command, "%s@%s" % (profile, plugin.getConfig('new_account_domain'))], path=plugin._prosody_path)
-        return d
-
-
-class MiscAccount(object):
-    """Account plugin: create a SàT + Prosody account, used by Libervia"""
-    #XXX: This plugin is a Q&D one used for the demo. Something more generic (and not
-    #     only focused on Prosody) is planed
 
     def __init__(self, host):
         log.info(_(u"Plugin Account initialization"))
@@ -138,14 +85,6 @@
         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'))
-            if not paths:
-                log.error(_(u"Can't find %s") % (self.getConfig('prosodyctl'),))
-            else:
-                self._prosody_path = dirname(paths[0])
-                log.info(_(u'Prosody path found: %s') % (self._prosody_path,))
 
         self.fixEmailAdmins()
         self._sessions = Sessions()
@@ -256,12 +195,13 @@
         @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
         if jid_s:
             d = defer.succeed(None)
+            jid_ = jid.JID(jid_s)
         else:
-            d = ProsodyRegisterProtocol.prosodyctl(self, 'adduser', password, profile)
-            jid_s = "%s@%s" % (profile, self.getConfig('new_account_domain'))
+            jid_s = profile + u"@" + self.getConfig('new_account_domain')
+            jid_ = jid.JID(jid_s)
+            d = self.host.plugins['XEP-0077'].registerNewAccount(jid_, password)
 
         def setParams(dummy):
             self.host.memory.setParam("JabberID", jid_s, "Connection", profile_key=profile)
@@ -481,10 +421,11 @@
         @param data (dict)
         @profile (str): %(doc_profile)s
         """
+        client = self.host.getClient(profile)
         password = self._sessions.profileGet(data['session_id'], profile)['new_password']
         del self._sessions[data['session_id']]
 
-        def passwordChanged(result):
+        def passwordChanged(dummy):
             d = self.host.memory.setParam(C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=profile)
             d.addCallback(lambda dummy: self.host.memory.setParam("Password", password, "Connection", profile_key=profile))
             confirm_ui = xml_tools.XMLUI("popup", title=D_("Confirmation"))
@@ -496,7 +437,7 @@
             error_ui.addText(D_("Your password could not be changed: %s") % failure.getErrorMessage())
             return defer.succeed({'xmlui': error_ui.toXml()})
 
-        d = ProsodyRegisterProtocol.prosodyctl(self, 'passwd', password, profile=profile)
+        d = self.host.plugins['XEP-0077'].changePassword(client, password)
         d.addCallbacks(passwordChanged, errback)
         return d
 
@@ -513,12 +454,14 @@
 
     def __deleteAccountCb(self, data, profile):
         """Actually delete the XMPP account and SàT profile
+
         @param data
         @param profile
         """
-        def userDeleted(result):
-            client = self.host.profiles[profile]
+        client = self.host.getClient(profile)
+        def userDeleted(dummy):
 
+            # FIXME: client should be disconnected at this point, so 2 next loop should be removed (to be confirmed)
             for jid_ in client.roster._jids:  # empty roster
                 client.presence.unsubscribe(jid_)
 
@@ -539,7 +482,7 @@
             error_ui.addText(D_("Your XMPP account could not be deleted: %s") % failure.getErrorMessage())
             return defer.succeed({'xmlui': error_ui.toXml()})
 
-        d = ProsodyRegisterProtocol.prosodyctl(self, 'deluser', profile=profile)
+        d = self.host.plugins['XEP-0077'].removeRegistration(client, jid.JID(client.jid.host))
         d.addCallbacks(userDeleted, errback)
         return d