comparison src/plugins/plugin_misc_account.py @ 730:32bbabe809da

plugin account: configuration constants can be overriden in sat.conf (section "plugin account") + better deferred management in email sending
author Goffi <goffi@goffi.org>
date Fri, 13 Dec 2013 17:15:57 +0100
parents 712e3782af12
children ffc3ddcdaf48
comparison
equal deleted inserted replaced
729:8f50a0079769 730:32bbabe809da
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT plugin for parrot mode (experimental) 4 # SAT plugin for account creation (experimental)
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009, 2010, 2011, 2012, 2013 Jérôme Poisson (goffi@goffi.org)
6 6
7 # This program is free software: you can redistribute it and/or modify 7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by 8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or 9 # the Free Software Foundation, either version 3 of the License, or
35 "main": "MiscAccount", 35 "main": "MiscAccount",
36 "handler": "no", 36 "handler": "no",
37 "description": _(u"""SàT account creation""") 37 "description": _(u"""SàT account creation""")
38 } 38 }
39 39
40 #You need do adapt the following consts to your server 40 CONFIG_SECTION = "plugin account"
41 _REG_EMAIL_FROM = "NOREPLY@example.net" 41
42 _REG_EMAIL_SERVER = "localhost" 42 # You need do adapt the following consts to your server
43 _REG_ADMIN_EMAIL = "admin@example.net" 43 # all theses values (key=option name, value=default) can (and should) be overriden in sat.conf
44 _NEW_ACCOUNT_SERVER = "localhost" 44 # in section CONFIG_SECTION
45 _NEW_ACCOUNT_DOMAIN = "example.net" 45
46 _NEW_ACCOUNT_RESOURCE = "libervia" 46 default_conf = {"email_from": "NOREPLY@example.net",
47 _PROSODY_PATH = None # prosody path (where prosodyctl will be executed from), or None to automaticaly find it 47 "email_server": "localhost",
48 _PROSODYCTL = "prosodyctl" 48 "admin_email": "admin@example.net",
49 49 "new_account_server": "localhost",
50 RESERVED = ['libervia'] 50 "new_account_domain": "example.net",
51 "new_account_resource": "libervia",
52 "prosody_path": None, # prosody path (where prosodyctl will be executed from), or None to automaticaly find it
53 "prosodyctl": "prosodyctl",
54 "reserved_list": ['libervia'] # profiles which can't be used
55 }
51 56
52 57
53 class ProsodyRegisterProtocol(protocol.ProcessProtocol): 58 class ProsodyRegisterProtocol(protocol.ProcessProtocol):
54 """ Try to register an account with prosody """ 59 """ Try to register an account with prosody """
55 60
79 84
80 class MiscAccount(object): 85 class MiscAccount(object):
81 """Account plugin: create a SàT + Prosody account, used by Libervia""" 86 """Account plugin: create a SàT + Prosody account, used by Libervia"""
82 #XXX: This plugin is a Q&D one used for the demo. Something more generic (and not 87 #XXX: This plugin is a Q&D one used for the demo. Something more generic (and not
83 # only focused on Prosody) is planed 88 # only focused on Prosody) is planed
84 _prosody_path = _PROSODY_PATH or ''
85 89
86 def __init__(self, host): 90 def __init__(self, host):
87 info(_(u"Plugin Account initialization")) 91 info(_(u"Plugin Account initialization"))
88 self.host = host 92 self.host = host
89 host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async=True) 93 host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async=True)
90 host.bridge.addMethod("getNewAccountDomain", ".plugin", in_sign='', out_sign='s', method=self._getNewAccountDomain, async=False) 94 host.bridge.addMethod("getNewAccountDomain", ".plugin", in_sign='', out_sign='s', method=self._getNewAccountDomain, async=False)
91 if not self._prosody_path: 95 self._prosody_path = self.getConfig('prosody_path')
92 paths = which(_PROSODYCTL) 96 if self._prosody_path is None:
97 paths = which(self.getConfig('prosodyctl'))
93 if not paths: 98 if not paths:
94 error(_("Can't find %s") % (_PROSODYCTL, )) 99 error(_("Can't find %s") % (self.getConfig('prosodyctl'), ))
95 else: 100 else:
96 self._prosody_path = dirname(paths[0]) 101 self._prosody_path = dirname(paths[0])
97 info(_('Prosody path found: %s') % (self._prosody_path, )) 102 info(_('Prosody path found: %s') % (self._prosody_path, ))
103
104 def getConfig(self, name):
105 return self.host.memory.getConfig(CONFIG_SECTION, name) or default_conf[name]
98 106
99 def _registerAccount(self, email, password, profile): 107 def _registerAccount(self, email, password, profile):
100 108
101 """ 109 """
102 #Password Generation 110 #Password Generation
107 password = ''.join([random.choice(_charset) for i in range(15)]) 115 password = ''.join([random.choice(_charset) for i in range(15)])
108 """ 116 """
109 if not email or not password or not profile: 117 if not email or not password or not profile:
110 raise exceptions.DataError 118 raise exceptions.DataError
111 119
112 if profile.lower() in RESERVED: 120 if profile.lower() in self.getConfig('reserved_list'):
113 return defer.fail(Failure(u'CONFLICT')) 121 return defer.fail(Failure(u'CONFLICT'))
114 122
115 d = self.host.memory.asyncCreateProfile(profile) 123 d = self.host.memory.asyncCreateProfile(profile)
116 d.addCallback(self._profileRegistered, email, password, profile) 124 d.addCallback(self._profileRegistered, email, password, profile)
117 return d 125 return d
118 126
119 def _profileRegistered(self, result, email, password, profile): 127 def _profileRegistered(self, result, email, password, profile):
120 128
121 #FIXME: values must be in a config file instead of hardcoded 129 #FIXME: values must be in a config file instead of hardcoded
122 self.host.memory.setParam("JabberID", "%s@%s/%s" % (profile, _NEW_ACCOUNT_DOMAIN, _NEW_ACCOUNT_RESOURCE), 130 self.host.memory.setParam("JabberID", "%s@%s/%s" % (profile, self.getConfig('new_account_domain'), self.getConfig('new_account_resource')),
123 "Connection", profile_key=profile) 131 "Connection", profile_key=profile)
124 self.host.memory.setParam("Server", _NEW_ACCOUNT_SERVER, 132 self.host.memory.setParam("Server", self.getConfig('new_account_server'),
125 "Connection", profile_key=profile) 133 "Connection", profile_key=profile)
126 self.host.memory.setParam("Password", password, 134 self.host.memory.setParam("Password", password,
127 "Connection", profile_key=profile) 135 "Connection", profile_key=profile)
128 #and the account 136 #and the account
129 137
130 #XXX: we use "prosodyctl adduser" because "register" doesn't check conflict 138 #XXX: we use "prosodyctl adduser" because "register" doesn't check conflict
131 # and just change the password if the account already exists 139 # and just change the password if the account already exists
132 d = defer.Deferred() 140 d = defer.Deferred()
133 prosody_reg = ProsodyRegisterProtocol(password, d) 141 prosody_reg = ProsodyRegisterProtocol(password, d)
134 prosody_exe = join(self._prosody_path, _PROSODYCTL) 142 prosody_exe = join(self._prosody_path, self.getConfig('prosodyctl'))
135 reactor.spawnProcess(prosody_reg, prosody_exe, [prosody_exe, 'adduser', "%s@%s" % (profile, _NEW_ACCOUNT_DOMAIN)], path=self._prosody_path) 143 reactor.spawnProcess(prosody_reg, prosody_exe, [prosody_exe, 'adduser', "%s@%s" % (profile, self.getConfig('new_account_domain'))], path=self._prosody_path)
136 144
137 d.addCallback(self._accountCreated, profile, email, password) 145 d.addCallback(self._sendEmails, profile, email, password)
146 d.addCallback(lambda ignore: None)
138 return d 147 return d
139 148
140 def _accountCreated(self, result, login, email, password): 149 def _sendEmails(self, result, login, email, password):
141 #time to send the email 150 #time to send the email
142 151
143 _email_host = _REG_EMAIL_SERVER 152 _email_host = self.getConfig('email_server')
144 _email_from = _REG_EMAIL_FROM 153 _email_from = self.getConfig("email_from")
145 154
146 def email_ok(ignore): 155 def email_ok(ignore):
147 print ("Account creation email sent to %s" % email) 156 print ("Account creation email sent to %s" % email)
148 157
149 def email_ko(ignore): 158 def email_ko(ignore):
166 follow SàT news: http://www.goffi.org 175 follow SàT news: http://www.goffi.org
167 176
168 Any feedback welcome 177 Any feedback welcome
169 178
170 Cheers 179 Cheers
171 Goffi""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, _NEW_ACCOUNT_DOMAIN)}).encode('utf-8') 180 Goffi""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, self.getConfig('new_account_domain'))}).encode('utf-8')
172 msg = MIMEText(body, 'plain', 'UTF-8') 181 msg = MIMEText(body, 'plain', 'UTF-8')
173 msg['Subject'] = 'Libervia account created' 182 msg['Subject'] = 'Libervia account created'
174 msg['From'] = _email_from 183 msg['From'] = _email_from
175 msg['To'] = email 184 msg['To'] = email
176 185
177 d = sendmail(_email_host, _email_from, email, msg.as_string()) 186 d_user = sendmail(_email_host, _email_from, email, msg.as_string())
178 d.addCallbacks(email_ok, email_ko) 187 d_user.addCallbacks(email_ok, email_ko)
188
179 #email to the administrator 189 #email to the administrator
180
181 body = (u"""New account created: %(login)s [%(email)s]""" % {'login': login, 'email': email}).encode('utf-8') 190 body = (u"""New account created: %(login)s [%(email)s]""" % {'login': login, 'email': email}).encode('utf-8')
182 msg = MIMEText(body, 'plain', 'UTF-8') 191 msg = MIMEText(body, 'plain', 'UTF-8')
183 msg['Subject'] = 'Libervia new account created' 192 msg['Subject'] = 'Libervia new account created'
184 msg['From'] = _email_from 193 msg['From'] = _email_from
185 msg['To'] = _REG_ADMIN_EMAIL 194 msg['To'] = self.getConfig('admin_email')
186 195
187 d = sendmail(_email_host, _email_from, _REG_ADMIN_EMAIL, msg.as_string()) 196 d_admin = sendmail(_email_host, _email_from, self.getConfig('admin_email'), msg.as_string())
188 d.addCallbacks(email_ok, email_ko) 197 d_admin.addCallbacks(email_ok, email_ko)
198 return defer.DeferredList([d_user, d_admin])
189 199
190 def _getNewAccountDomain(self): 200 def _getNewAccountDomain(self):
191 """@return: the domain that will be set to new account""" 201 """@return: the domain that will be set to new account"""
192 return _NEW_ACCOUNT_DOMAIN 202 return self.getConfig('new_account_domain')
193 203