Handle a cancel response from OpenID.
[zzz-spline-users.git] / splinext / users / controllers / accounts.py
1 import logging
2 from openid.consumer.consumer import Consumer, SUCCESS, CANCEL
3 from openid.extensions.sreg import SRegRequest, SRegResponse
4 from openid.store.filestore import FileOpenIDStore
5 from openid.yadis.discover import DiscoveryFailure
6 from sqlalchemy.orm.exc import NoResultFound
7
8 from pylons import config, request, response, session, tmpl_context as c, url
9 from pylons.controllers.util import abort, redirect_to
10 from routes import request_config
11
12 from spline.model import meta
13 from spline.lib import helpers as h
14 from spline.lib.base import BaseController, render
15 from splinext.users import model as users_model
16
17 log = logging.getLogger(__name__)
18
19 class AccountsController(BaseController):
20
21 openid_store = FileOpenIDStore('/var/tmp')
22
23 def _bail(self, reason):
24 # Used for bailing on a login attempt; reshows the login page
25 c.error = reason
26 c.attempted_openid = request.params.get('openid_identifier', '')
27 return render('/users/login.mako')
28
29
30 def login(self):
31 c.error = None
32 c.attempted_openid = None
33 return render('/users/login.mako')
34
35 def login_begin(self):
36 """Step one of logging in with OpenID; we redirect to the provider"""
37
38 cons = Consumer(session=session, store=self.openid_store)
39
40 try:
41 openid_url = request.params['openid_identifier']
42 except KeyError:
43 return self._bail("Gotta enter an OpenID to log in.")
44
45 try:
46 auth_request = cons.begin(openid_url)
47 except DiscoveryFailure:
48 return self._bail(
49 "Can't connect to '{0}'. You sure it's an OpenID?"
50 .format(openid_url)
51 )
52
53 sreg_req = SRegRequest(optional=['nickname', 'email', 'dob', 'gender',
54 'country', 'language', 'timezone'])
55 auth_request.addExtension(sreg_req)
56
57 host = request.headers['host']
58 protocol = request_config().protocol
59 return_url = url(host=host, controller='accounts', action='login_finish')
60 new_url = auth_request.redirectURL(return_to=return_url,
61 realm=protocol + '://' + host)
62 redirect_to(new_url)
63
64 def login_finish(self):
65 """Step two of logging in; the OpenID provider redirects back here."""
66
67 cons = Consumer(session=session, store=self.openid_store)
68 host = request.headers['host']
69 return_url = url(host=host, controller='accounts', action='login_finish')
70 res = cons.complete(request.params, return_url)
71
72 if res.status == CANCEL:
73 # I guess.. just.. back to the homepage?
74 h.flash(u"""Login canceled.""", icon='user-silhouette')
75 redirect_to(url('/'))
76 elif res.status != SUCCESS:
77 return 'Error! %s' % res.message
78
79 try:
80 # Grab an existing user record, if one exists
81 q = meta.Session.query(users_model.User) \
82 .filter(users_model.User.openids.any(openid=res.identity_url))
83 user = q.one()
84 except NoResultFound:
85 # Try to pull a name out of the SReg response
86 sreg_res = SRegResponse.fromSuccessResponse(res)
87 try:
88 username = sreg_res['nickname']
89 except (KeyError, TypeError):
90 # KeyError if sreg has no nickname; TypeError if sreg is None
91 username = 'Anonymous'
92
93 # Create db records
94 user = users_model.User(name=username)
95 meta.Session.add(user)
96
97 openid = users_model.OpenID(openid=res.identity_url)
98 user.openids.append(openid)
99
100 meta.Session.commit()
101
102 # Remember who's logged in, and we're good to go
103 session['user_id'] = user.id
104 session.save()
105
106 h.flash(u"""Hello, {0}!""".format(user.name),
107 icon='user')
108
109 redirect_to('/', _code=303)
110
111 def logout(self):
112 """Logs the user out."""
113
114 if 'user_id' in session:
115 del session['user_id']
116 session.save()
117
118 h.flash(u"""Logged out.""",
119 icon='user-silhouette')
120
121 redirect_to('/', _code=303)