Mercurial > libervia-web
comparison libervia.tac @ 46:c3ee630914ba
Account creation
* browser side:
- login dialog has been extended to manage subscription
* server side:
- SATActionIDHandler to manage replies to action
- account is created on subscribtion form, a password is created, and 2 emails are send (one to user, one to administrator)
- access of main microblog is set to open
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 26 May 2011 16:43:30 +0200 |
parents | 7f106052326f |
children | 72c51a4839cc |
comparison
equal
deleted
inserted
replaced
45:7f106052326f | 46:c3ee630914ba |
---|---|
17 | 17 |
18 You should have received a copy of the GNU Affero General Public License | 18 You should have received a copy of the GNU Affero General Public License |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 """ | 20 """ |
21 | 21 |
22 #You need do adapt the following consts to your server | |
23 _REG_EMAIL_FROM = "NOREPLY@libervia.org" | |
24 _REG_EMAIL_SERVER = "localhost" | |
25 _REG_ADMIN_EMAIL = "goffi@goffi.org" | |
26 _NEW_ACCOUNT_SERVER = "localhost" | |
27 _NEW_ACCOUNT_DOMAIN = "tazar.int" | |
28 _NEW_ACCOUNT_RESOURCE = "libervia" | |
29 | |
22 from twisted.application import internet, service | 30 from twisted.application import internet, service |
23 from twisted.internet import glib2reactor | 31 from twisted.internet import glib2reactor |
24 glib2reactor.install() | 32 glib2reactor.install() |
25 from twisted.internet import reactor, defer | 33 from twisted.internet import reactor, defer |
26 | 34 from twisted.mail.smtp import sendmail |
27 from twisted.web import server | 35 from twisted.web import server |
28 from twisted.web import error as weberror | 36 from twisted.web import error as weberror |
29 from twisted.web.static import File | 37 from twisted.web.static import File |
30 from twisted.web.resource import Resource | 38 from twisted.web.resource import Resource |
31 from twisted.python.components import registerAdapter | 39 from twisted.python.components import registerAdapter |
32 from twisted.words.protocols.jabber.jid import JID | 40 from twisted.words.protocols.jabber.jid import JID |
33 from txjsonrpc.web import jsonrpc | 41 from txjsonrpc.web import jsonrpc |
34 from txjsonrpc import jsonrpclib | 42 from txjsonrpc import jsonrpclib |
35 from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService | 43 from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService |
44 from email.mime.text import MIMEText | |
45 from logging import debug, info, warning, error | |
36 import re | 46 import re |
37 import glob | 47 import glob |
38 import os.path | 48 import os.path |
39 import sys | 49 import sys |
40 from server_side.blog import MicroBlog | 50 from server_side.blog import MicroBlog |
73 | 83 |
74 def touch(self): | 84 def touch(self): |
75 if not self.__lock: | 85 if not self.__lock: |
76 server.Session.touch(self) | 86 server.Session.touch(self) |
77 | 87 |
88 class SATActionIDHandler(object): | |
89 """Manage SàT action id lifecycle""" | |
90 ID_LIFETIME = 30 #after this time (in seconds), id will be suppressed and action result will be ignored | |
91 | |
92 def __init__(self): | |
93 self.waiting_ids = {} | |
94 | |
95 def waitForId(self, id, callback, *args, **kwargs): | |
96 """Wait for an action result | |
97 @param id: id to wait for | |
98 @param callback: method to call when action gave a result back | |
99 @param *args: additional argument to pass to callback | |
100 @param **kwargs: idem""" | |
101 self.waiting_ids[id] = (callback, args, kwargs) | |
102 reactor.callLater(self.ID_LIFETIME, self.purgeID, id) | |
103 | |
104 def purgeID(self, id): | |
105 """Called when an id has not be handled in time""" | |
106 if id in self.waiting_ids: | |
107 warning ("action of id %s has not been managed, id is now ignored" % id) | |
108 del self.waiting_ids[id] | |
109 | |
110 def actionResultCb(self, answer_type, id, data): | |
111 """Manage the actionResult signal""" | |
112 if id in self.waiting_ids: | |
113 callback, args, kwargs = self.waiting_ids[id] | |
114 del self.waiting_ids[id] | |
115 callback(answer_type, id, data, *args, **kwargs) | |
78 | 116 |
79 class MethodHandler(jsonrpc.JSONRPC): | 117 class MethodHandler(jsonrpc.JSONRPC): |
80 | 118 |
81 def __init__(self, sat_host): | 119 def __init__(self, sat_host): |
82 jsonrpc.JSONRPC.__init__(self) | 120 jsonrpc.JSONRPC.__init__(self) |
230 - BAD REQUEST: something is wrong in the request (bad arguments, profile_key for login) | 268 - BAD REQUEST: something is wrong in the request (bad arguments, profile_key for login) |
231 - AUTH ERROR: either the profile or the password is wrong | 269 - AUTH ERROR: either the profile or the password is wrong |
232 - ALREADY WAITING: a request has already be made for this profile | 270 - ALREADY WAITING: a request has already be made for this profile |
233 - server.NOT_DONE_YET: the profile is being processed, the return value will be given by self._logged or self._logginError | 271 - server.NOT_DONE_YET: the profile is being processed, the return value will be given by self._logged or self._logginError |
234 """ | 272 """ |
273 if 'new_account' in request.args: | |
274 return self._registerNewAccount(request.args) | |
275 | |
235 try: | 276 try: |
236 _login = request.args['login'][0] | 277 _login = request.args['login'][0] |
237 if _login.startswith('@'): | 278 if _login.startswith('@'): |
238 raise Exception('No profile_key allowed') | 279 raise Exception('No profile_key allowed') |
239 _pass = request.args['password'][0] | 280 _pass = request.args['password'][0] |
253 return self._logged(_login, request, finish=False) | 294 return self._logged(_login, request, finish=False) |
254 | 295 |
255 self.profiles_waiting[_login] = request | 296 self.profiles_waiting[_login] = request |
256 self.sat_host.bridge.connect(_login) | 297 self.sat_host.bridge.connect(_login) |
257 return server.NOT_DONE_YET | 298 return server.NOT_DONE_YET |
299 | |
300 def _postAccountCreation(self, answer_type, id, data, profile): | |
301 """Called when a account has just been created, | |
302 setup stuff has microblog access""" | |
303 def _connected(ignore): | |
304 mblog_d = defer.Deferred() | |
305 self.sat_host.bridge.setMicroblogAccess("open", profile, lambda: mblog_d.callback(None), mblog_d.errback) | |
306 mblog_d.addBoth(lambda ignore: self.sat_host.bridge.disconnect(profile)) | |
307 | |
308 d = defer.Deferred() | |
309 self.sat_host.bridge.asyncConnect(profile, lambda: d.callback(None), d.errback) | |
310 d.addCallback(_connected) | |
311 | |
312 def _registerNewAccount(self, args): | |
313 """Create a new account, or return error | |
314 @param args: dict of args as given by the form | |
315 @return: "REGISTRATION" in case of success""" | |
316 try: | |
317 profile = login = args['login'][0] | |
318 email = args['email'][0] | |
319 except KeyError: | |
320 return "BAD REQUEST" | |
321 if not re.match(r'^[a-z0-9_-]+$', login, re.IGNORECASE) or \ | |
322 not re.match(r'^.+@.+\..+', email, re.IGNORECASE): | |
323 return "BAD REQUEST" | |
324 #_charset = [chr(i) for i in range(0x21,0x7F)] #XXX: this charset seems to have some issues with openfire | |
325 _charset = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)] | |
326 import random | |
327 random.seed() | |
328 password = ''.join([random.choice(_charset) for i in range(15)]) | |
329 | |
330 if login in self.sat_host.bridge.getProfilesList(): #FIXME: must use a deferred + create a new profile check method | |
331 return "ALREADY EXISTS" | |
332 | |
333 #we now create the profile | |
334 self.sat_host.bridge.createProfile(login) | |
335 #FIXME: values must be in a config file instead of hardcoded | |
336 self.sat_host.bridge.setParam("JabberID", "%s@%s/%s" % (login, _NEW_ACCOUNT_DOMAIN, _NEW_ACCOUNT_RESOURCE), "Connection", profile) | |
337 self.sat_host.bridge.setParam("Server", _NEW_ACCOUNT_SERVER, "Connection", profile) | |
338 self.sat_host.bridge.setParam("Password", password, "Connection", profile) | |
339 #and the account | |
340 action_id = self.sat_host.bridge.registerNewAccount(login, password, email, "tazar.int", 5222) | |
341 self.sat_host.action_handler.waitForId(action_id, self._postAccountCreation, profile) | |
342 | |
343 #time to send the email | |
344 | |
345 _email_host = _REG_EMAIL_SERVER | |
346 _email_from = _REG_EMAIL_FROM | |
347 | |
348 def email_ok(ignore): | |
349 print ("Account creation email sent to %s" % email) | |
350 | |
351 def email_ko(ignore): | |
352 #TODO: return error code to user | |
353 error ("Failed to send email to %s" % email) | |
354 | |
355 body = (u"""Welcome to Libervia, a Salut à Toi project part | |
356 | |
357 /!\\ WARNING, THIS IS ONLY A TECHNICAL DEMO, DON'T USE THIS ACCOUNT FOR ANY SERIOUS PURPOSE /!\\ | |
358 | |
359 Here are your connection informations: | |
360 login: %(login)s | |
361 password: %(password)s | |
362 | |
363 Any feedback welcome | |
364 | |
365 Cheers | |
366 Goffi""" % { 'login': login, 'password': password }).encode('utf-8') | |
367 msg = MIMEText(body, 'plain', 'UTF-8') | |
368 msg['Subject'] = 'Libervia account created' | |
369 msg['From'] = _email_from | |
370 msg['To'] = email | |
371 | |
372 d = sendmail(_email_host, _email_from, email, msg.as_string()) | |
373 d.addCallbacks(email_ok, email_ko) | |
374 | |
375 #email to the administrator | |
376 | |
377 body = (u"""New account created: %(login)s [%(email)s]""" % { 'login': login, 'email': email }).encode('utf-8') | |
378 msg = MIMEText(body, 'plain', 'UTF-8') | |
379 msg['Subject'] = 'Libervia new account created' | |
380 msg['From'] = _email_from | |
381 msg['To'] = _REG_ADMIN_EMAIL | |
382 | |
383 d = sendmail(_email_host, _email_from, email, msg.as_string()) | |
384 d.addCallbacks(email_ok, email_ko) | |
385 print "rturn REGISTRATION" | |
386 return "REGISTRATION" | |
258 | 387 |
259 def __cleanWaiting(self, login): | 388 def __cleanWaiting(self, login): |
260 """Remove login from waiting queue""" | 389 """Remove login from waiting queue""" |
261 try: | 390 try: |
262 del self.profiles_waiting[login] | 391 del self.profiles_waiting[login] |
403 self.signal_handler = SignalHandler(self) | 532 self.signal_handler = SignalHandler(self) |
404 _register = Register(self) | 533 _register = Register(self) |
405 self.signal_handler.plugRegister(_register) | 534 self.signal_handler.plugRegister(_register) |
406 self.sessions = {} #key = session value = user | 535 self.sessions = {} #key = session value = user |
407 self.prof_connected = set() #Profiles connected | 536 self.prof_connected = set() #Profiles connected |
537 self.action_handler = SATActionIDHandler() | |
408 ## bridge ## | 538 ## bridge ## |
409 try: | 539 try: |
410 self.bridge=DBusBridgeFrontend() | 540 self.bridge=DBusBridgeFrontend() |
411 except BridgeExceptionNoService: | 541 except BridgeExceptionNoService: |
412 print(u"Can't connect to SàT backend, are you sure it's launched ?") | 542 print(u"Can't connect to SàT backend, are you sure it's launched ?") |
413 sys.exit(1) | 543 sys.exit(1) |
414 self.bridge.register("connected", self.signal_handler.connected) | 544 self.bridge.register("connected", self.signal_handler.connected) |
415 self.bridge.register("connectionError", self.signal_handler.connectionError) | 545 self.bridge.register("connectionError", self.signal_handler.connectionError) |
546 self.bridge.register("actionResult", self.action_handler.actionResultCb, "request") | |
416 for signal_name in ['presenceUpdate', 'personalEvent', 'newMessage', 'roomJoined', 'roomUserJoined', 'roomUserLeft', 'tarotGameStarted', 'tarotGameNew', | 547 for signal_name in ['presenceUpdate', 'personalEvent', 'newMessage', 'roomJoined', 'roomUserJoined', 'roomUserLeft', 'tarotGameStarted', 'tarotGameNew', |
417 'tarotGameChooseContrat', 'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore']: | 548 'tarotGameChooseContrat', 'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore']: |
418 self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name)) | 549 self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name)) |
419 root.putChild('json_signal_api', self.signal_handler) | 550 root.putChild('json_signal_api', self.signal_handler) |
420 root.putChild('json_api', MethodHandler(self)) | 551 root.putChild('json_api', MethodHandler(self)) |