plugin misc_account: add method asyncConnectWithXMPPCredentials

This commit is contained in:
souliane 2015-11-23 18:50:02 +01:00
commit 04bb8beaae

View file

@ -33,6 +33,9 @@ from twisted.mail.smtp import sendmail
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 @@ default_conf = {"email_from": "NOREPLY@example.net",
"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 @@ class ProsodyRegisterProtocol(protocol.ProcessProtocol):
@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 @@ class MiscAccount(object):
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 @@ class MiscAccount(object):
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 @@ class MiscAccount(object):
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 @@ Your account on %(domain)s has been successfully created. This is a demonstratio
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 @@ Any feedback welcome. Thank you!
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 @@ http://www.salut-a-toi.org
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 @@ http://www.salut-a-toi.org
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