1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 from openid
.consumer
import consumer
17 from openid
.consumer
.discover
import DiscoveryFailure
18 from openid
.extensions
.sreg
import SRegRequest
, SRegResponse
20 from mediagoblin
import mg_globals
, messages
21 from mediagoblin
.db
.models
import User
22 from mediagoblin
.decorators
import (auth_enabled
, allow_registration
,
24 from mediagoblin
.tools
.response
import redirect
, render_to_response
25 from mediagoblin
.tools
.translate
import pass_to_ugettext
as _
26 from mediagoblin
.plugins
.openid
import forms
as auth_forms
27 from mediagoblin
.plugins
.openid
.models
import OpenIDUserURL
28 from mediagoblin
.plugins
.openid
.store
import SQLAlchemyOpenIDStore
29 from mediagoblin
.auth
.tools
import register_user
32 def _start_verification(request
, form
, return_to
, sreg
=True):
34 Start OpenID Verification.
36 Returns False if verification fails, otherwise, will return either a
37 redirect or render_to_response object
39 openid_url
= form
.openid
.data
40 c
= consumer
.Consumer(request
.session
, SQLAlchemyOpenIDStore())
42 # Try to discover provider
44 auth_request
= c
.begin(openid_url
)
45 except DiscoveryFailure
:
46 # Discovery failed, return to login page
47 form
.openid
.errors
.append(
48 _('Sorry, the OpenID server could not be found'))
52 host
= 'http://' + request
.host
55 # Ask provider for email and nickname
56 auth_request
.addExtension(SRegRequest(required
=['email', 'nickname']))
58 # Do we even need this?
59 if auth_request
is None:
60 form
.openid
.errors
.append(
61 _('No OpenID service was found for %s' % openid_url
))
63 elif auth_request
.shouldSendRedirect():
64 # Begin the authentication process as a HTTP redirect
65 redirect_url
= auth_request
.redirectURL(
69 request
, location
=redirect_url
)
72 # Send request as POST
73 form_html
= auth_request
.htmlMarkup(
74 host
, host
+ return_to
,
76 form_tag_attrs
={'id': 'openid_message'})
78 # Beware: this renders a template whose content is a form
79 # and some javascript to submit it upon page load. Non-JS
80 # users will have to click the form submit button to
81 # initiate OpenID authentication.
82 return render_to_response(
84 'mediagoblin/plugins/openid/request_form.html',
90 def _finish_verification(request
):
92 Complete OpenID Verification Process.
94 If the verification failed, will return false, otherwise, will return
97 c
= consumer
.Consumer(request
.session
, SQLAlchemyOpenIDStore())
99 # Check the response from the provider
100 response
= c
.complete(request
.args
, request
.base_url
)
101 if response
.status
== consumer
.FAILURE
:
102 messages
.add_message(
105 _('Verification of %s failed: %s' %
106 (response
.getDisplayIdentifier(), response
.message
)))
108 elif response
.status
== consumer
.SUCCESS
:
109 # Verification was successfull
112 elif response
.status
== consumer
.CANCEL
:
113 # Verification canceled
114 messages
.add_message(
117 _('Verification cancelled'))
122 def _response_email(response
):
123 """ Gets the email from the OpenID providers response"""
124 sreg_response
= SRegResponse
.fromSuccessResponse(response
)
125 if sreg_response
and 'email' in sreg_response
:
126 return sreg_response
.data
['email']
130 def _response_nickname(response
):
131 """ Gets the nickname from the OpenID providers response"""
132 sreg_response
= SRegResponse
.fromSuccessResponse(response
)
133 if sreg_response
and 'nickname' in sreg_response
:
134 return sreg_response
.data
['nickname']
140 """OpenID Login View"""
141 login_form
= auth_forms
.LoginForm(request
.form
)
142 allow_registration
= mg_globals
.app_config
["allow_registration"]
144 # Can't store next in request.GET because of redirects to OpenID provider
145 # Store it in the session
146 next
= request
.GET
.get('next')
147 request
.session
['next'] = next
151 if request
.method
== 'POST' and login_form
.validate():
152 return_to
= request
.urlgen(
153 'mediagoblin.plugins.openid.finish_login')
155 success
= _start_verification(request
, login_form
, return_to
)
162 return render_to_response(
164 'mediagoblin/plugins/openid/login.html',
165 {'login_form': login_form
,
166 'next': request
.session
.get('next'),
167 'login_failed': login_failed
,
168 'post_url': request
.urlgen('mediagoblin.plugins.openid.login'),
169 'allow_registration': allow_registration
})
173 def finish_login(request
):
174 """Complete OpenID Login Process"""
175 response
= _finish_verification(request
)
178 # Verification failed, redirect to login page.
179 return redirect(request
, 'mediagoblin.plugins.openid.login')
181 # Verification was successfull
182 query
= OpenIDUserURL
.query
.filter_by(
183 openid_url
=response
.identity_url
,
185 user
= query
.user
if query
else None
188 # Set up login in session
189 request
.session
['user_id'] = unicode(user
.id)
190 request
.session
.save()
192 if request
.session
.get('next'):
193 return redirect(request
, location
=request
.session
.pop('next'))
195 return redirect(request
, "index")
197 # No user, need to register
198 if not mg_globals
.app
.auth
:
199 messages
.add_message(
202 _('Sorry, authentication is disabled on this instance.'))
203 return redirect(request
, 'index')
205 # Get email and nickname from response
206 email
= _response_email(response
)
207 username
= _response_nickname(response
)
209 register_form
= auth_forms
.RegistrationForm(request
.form
,
210 openid
=response
.identity_url
,
213 return render_to_response(
215 'mediagoblin/auth/register.html',
216 {'register_form': register_form
,
217 'post_url': request
.urlgen('mediagoblin.plugins.openid.register')})
222 def register(request
):
223 """OpenID Registration View"""
224 if request
.method
== 'GET':
225 # Need to connect to openid provider before registering a user to
226 # get the users openid url. If method is 'GET', then this page was
227 # acessed without logging in first.
228 return redirect(request
, 'mediagoblin.plugins.openid.login')
230 register_form
= auth_forms
.RegistrationForm(request
.form
)
232 if register_form
.validate():
233 user
= register_user(request
, register_form
)
236 # redirect the user to their homepage... there will be a
237 # message waiting for them to verify their email
239 request
, 'mediagoblin.user_pages.user_home',
242 return render_to_response(
244 'mediagoblin/auth/register.html',
245 {'register_form': register_form
,
246 'post_url': request
.urlgen('mediagoblin.plugins.openid.register')})
249 @require_active_login
250 def start_edit(request
):
251 """Starts the process of adding an openid url to a users account"""
252 form
= auth_forms
.LoginForm(request
.form
)
254 if request
.method
== 'POST' and form
.validate():
255 query
= OpenIDUserURL
.query
.filter_by(
256 openid_url
=form
.openid
.data
258 user
= query
.user
if query
else None
261 return_to
= request
.urlgen('mediagoblin.plugins.openid.finish_edit')
262 success
= _start_verification(request
, form
, return_to
, False)
267 form
.openid
.errors
.append(
268 _('Sorry, an account is already registered to that OpenID.'))
270 return render_to_response(
272 'mediagoblin/plugins/openid/add.html',
274 'post_url': request
.urlgen('mediagoblin.plugins.openid.edit')})
277 @require_active_login
278 def finish_edit(request
):
279 """Finishes the process of adding an openid url to a user"""
280 response
= _finish_verification(request
)
283 # Verification failed, redirect to add openid page.
284 return redirect(request
, 'mediagoblin.plugins.openid.edit')
286 # Verification was successfull
287 query
= OpenIDUserURL
.query
.filter_by(
288 openid_url
=response
.identity_url
,
290 user_exists
= query
.user
if query
else None
293 # user exists with that openid url, redirect back to edit page
294 messages
.add_message(
297 _('Sorry, an account is already registered to that OpenID.'))
298 return redirect(request
, 'mediagoblin.plugins.openid.edit')
301 # Save openid to user
302 user
= User
.query
.filter_by(
303 id=request
.session
['user_id']
306 new_entry
= OpenIDUserURL()
307 new_entry
.openid_url
= response
.identity_url
308 new_entry
.user_id
= user
.id
311 messages
.add_message(
314 _('Your OpenID url was saved successfully.'))
316 return redirect(request
, 'mediagoblin.edit.account')
319 @require_active_login
320 def delete_openid(request
):
321 """View to remove an openid from a users account"""
322 form
= auth_forms
.LoginForm(request
.form
)
324 if request
.method
== 'POST' and form
.validate():
325 # Check if a user has this openid
326 query
= OpenIDUserURL
.query
.filter_by(
327 openid_url
=form
.openid
.data
329 user
= query
.first().user
if query
.first() else None
331 if user
and user
.id == int(request
.session
['user_id']):
332 count
= len(user
.openid_urls
)
333 if not count
> 1 and not user
.pw_hash
:
334 # Make sure the user has a pw or another OpenID
335 messages
.add_message(
338 _("You can't delete your only OpenID URL unless you"
339 " have a password set"))
341 # There is a user, but not the same user who is logged in
342 form
.openid
.errors
.append(
343 _('That OpenID is not registered to this account.'))
345 if not form
.errors
and not request
.session
.get('messages'):
346 # Okay to continue with deleting openid
347 return_to
= request
.urlgen(
348 'mediagoblin.plugins.openid.finish_delete')
349 success
= _start_verification(request
, form
, return_to
, False)
354 return render_to_response(
356 'mediagoblin/plugins/openid/delete.html',
358 'post_url': request
.urlgen('mediagoblin.plugins.openid.delete')})
361 @require_active_login
362 def finish_delete(request
):
363 """Finishes the deletion of an OpenID from an user's account"""
364 response
= _finish_verification(request
)
367 # Verification failed, redirect to delete openid page.
368 return redirect(request
, 'mediagoblin.plugins.openid.delete')
370 query
= OpenIDUserURL
.query
.filter_by(
371 openid_url
=response
.identity_url
373 user
= query
.first().user
if query
.first() else None
375 # Need to check this again because of generic openid urls such as google's
376 if user
and user
.id == int(request
.session
['user_id']):
377 count
= len(user
.openid_urls
)
378 if count
> 1 or user
.pw_hash
:
379 # User has more then one openid or also has a password.
380 query
.first().delete()
382 messages
.add_message(
385 _('OpenID was successfully removed.'))
387 return redirect(request
, 'mediagoblin.edit.account')
390 messages
.add_message(
393 _("You can't delete your only OpenID URL unless you have a "
396 return redirect(request
, 'mediagoblin.plugins.openid.delete')
399 messages
.add_message(
402 _('That OpenID is not registered to this account.'))
404 return redirect(request
, 'mediagoblin.plugins.openid.delete')