Give something better than a 500 when OpenID discovery fails.
[zzz-floof.git] / floof / controllers / account.py
1 import elixir
2 import logging
3 from openid.consumer.consumer import Consumer
4 from openid.extensions.sreg import SRegRequest, SRegResponse
5 from openid.store.filestore import FileOpenIDStore
6 from openid.yadis.discover import DiscoveryFailure
7 from sqlalchemy.orm.exc import NoResultFound
8
9 from pylons import request, response, session, tmpl_context as c, url
10 from pylons.controllers.util import abort, redirect, redirect_to
11 from routes import url_for, request_config
12
13 from floof.lib.base import BaseController, render
14 import floof.lib.helpers as h
15 from floof.model.users import IdentityURL, User
16
17 log = logging.getLogger(__name__)
18
19 class AccountController(BaseController):
20
21 openid_store = FileOpenIDStore('/var/tmp')
22
23 def login(self):
24 c.bogus_identity_url = request.params.get('bogus_identity_url', None)
25 return render('/account/login.mako')
26
27 def login_begin(self):
28 """Step one of logging in with OpenID; we redirect to the provider"""
29
30 identity_url = request.params['identity_url']
31 cons = Consumer(session=session, store=self.openid_store)
32 try:
33 auth_request = cons.begin(identity_url)
34 except DiscoveryFailure:
35 redirect_to(controller='account', action='login',
36 bogus_identity_url=identity_url)
37
38 sreg_req = SRegRequest(optional=['nickname', 'email', 'dob', 'gender',
39 'country', 'language', 'timezone'])
40 auth_request.addExtension(sreg_req)
41
42 host = request.headers['host']
43 protocol = request_config().protocol
44 return_url = url_for(host=host, controller='account', action='login_finish')
45 new_url = auth_request.redirectURL(return_to=return_url,
46 realm=protocol + '://' + host)
47 redirect(new_url)
48
49 def login_finish(self):
50 """Step two of logging in; the OpenID provider redirects back here."""
51
52 cons = Consumer(session=session, store=self.openid_store)
53 host = request.headers['host']
54 return_url = url_for(host=host, controller='account', action='login_finish')
55 res = cons.complete(request.params, return_url)
56
57 if res.status != 'success':
58 return 'Error! %s' % res.message
59
60 try:
61 # Grab an existing user record, if one exists
62 q = User.query.filter(User.identity_urls.any(url=res.identity_url))
63 user = q.one()
64 except NoResultFound:
65 # Unrecognized URL. Redirect to a registration page to ask for a
66 # nickname, etc.
67 session['register:identity_url'] = res.identity_url
68
69 # Try to pull a name out of the SReg response
70 sreg_res = SRegResponse.fromSuccessResponse(res)
71 if sreg_res and 'nickname' in sreg_res:
72 session['register:nickname'] = sreg_res['nickname']
73
74 session.save()
75 redirect(url('register'))
76
77 # Remember who's logged in, and we're good to go
78 session['user_id'] = user.id
79 session.save()
80
81 # XXX send me where I came from
82 redirect('/')
83
84 def logout(self):
85 """Log user out."""
86
87 if 'user_id' in session:
88 del session['user_id']
89 session.save()
90
91 # XXX success message
92 # XXX send me where I came from
93 redirect('/')
94
95 def register(self):
96 """Logging in with an unrecognized identity URL redirects here."""
97
98 c.identity_url = session['register:identity_url']
99 c.nickname = session.get('register:nickname', None)
100
101 return render('/account/register.mako')
102
103 def register_finish(self):
104 """Complete a new-user registration. Create the user and log in."""
105
106 identity_url = session['register:identity_url']
107 username = request.params.get('username', None)
108
109 # XXX how do we return errors in some useful way?
110
111 if not username:
112 return 'Please enter a username.'
113
114 if User.query.filter_by(name=username).count():
115 return 'That username is taken.'
116
117 # Create db records
118 user = User(name=username)
119 user.identity_urls.append(IdentityURL(url=identity_url))
120 elixir.session.commit()
121
122 # Log in
123 session['user_id'] = user.id
124 session.save()
125
126 # XXX how do we do success messages in some useful way?
127 # XXX send me where I came from
128 redirect('/')