getting file size for all dict files to be downloaded. coming to be 400mb or so.
[worddb.git] / libs / django_authopenid / views.py
blob3466e4d61d43d54db0929537e47353871750783d
1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2007, 2008, BenoƮt Chesneau
3 # Copyright (c) 2007 Simon Willison, original work on django-openid
4 #
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
11 # * Redistributions of source code must retain the above copyright
12 # * notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above copyright
14 # * notice, this list of conditions and the following disclaimer in the
15 # * documentation and/or other materials provided with the
16 # * distribution. Neither the name of the <ORGANIZATION> nor the names
17 # * of its contributors may be used to endorse or promote products
18 # * derived from this software without specific prior written
19 # * permission.
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
28 # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 from django.http import HttpResponseRedirect, get_host
34 from django.shortcuts import render_to_response as render
35 from django.template import RequestContext, loader, Context
36 from django.conf import settings
37 from django.contrib.auth.models import User
38 from django.contrib.auth import login, logout
39 from django.contrib.auth.decorators import login_required
40 from django.core.urlresolvers import reverse
41 from django.utils.encoding import smart_unicode
42 from django.utils.html import escape
43 from django.utils.translation import ugettext as _
44 from django.contrib.sites.models import Site
45 from django.utils.http import urlquote_plus
46 from django.core.mail import send_mail
48 from openid.consumer.consumer import Consumer, \
49 SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
50 from openid.consumer.discover import DiscoveryFailure
51 from openid.extensions import sreg
52 # needed for some linux distributions like debian
53 try:
54 from openid.yadis import xri
55 except ImportError:
56 from yadis import xri
58 import re
59 import urllib
62 from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response, clean_next
63 from django_authopenid.models import UserAssociation, UserPasswordQueue
64 from django_authopenid.forms import OpenidSigninForm, OpenidAuthForm, OpenidRegisterForm, \
65 OpenidVerifyForm, RegistrationForm, ChangepwForm, ChangeemailForm, \
66 ChangeopenidForm, DeleteForm, EmailPasswordForm
68 def get_url_host(request):
69 if request.is_secure():
70 protocol = 'https'
71 else:
72 protocol = 'http'
73 host = escape(get_host(request))
74 return '%s://%s' % (protocol, host)
76 def get_full_url(request):
77 return get_url_host(request) + request.get_full_path()
81 def ask_openid(request, openid_url, redirect_to, on_failure=None,
82 sreg_request=None):
83 """ basic function to ask openid and return response """
84 on_failure = on_failure or signin_failure
86 trust_root = getattr(
87 settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
89 if xri.identifierScheme(openid_url) == 'XRI' and getattr(
90 settings, 'OPENID_DISALLOW_INAMES', False
92 msg = _("i-names are not supported")
93 return on_failure(request, msg)
94 consumer = Consumer(request.session, DjangoOpenIDStore())
95 try:
96 auth_request = consumer.begin(openid_url)
97 except DiscoveryFailure:
98 msg = _("The OpenID %s was invalid" % openid_url)
99 return on_failure(request, msg)
101 if sreg_request:
102 auth_request.addExtension(sreg_request)
103 redirect_url = auth_request.redirectURL(trust_root, redirect_to)
104 return HttpResponseRedirect(redirect_url)
106 def complete(request, on_success=None, on_failure=None, return_to=None):
107 """ complete openid signin """
108 on_success = on_success or default_on_success
109 on_failure = on_failure or default_on_failure
111 consumer = Consumer(request.session, DjangoOpenIDStore())
112 # make sure params are encoded in utf8
113 params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
114 openid_response = consumer.complete(params, return_to)
117 if openid_response.status == SUCCESS:
118 return on_success(request, openid_response.identity_url,
119 openid_response)
120 elif openid_response.status == CANCEL:
121 return on_failure(request, 'The request was canceled')
122 elif openid_response.status == FAILURE:
123 return on_failure(request, openid_response.message)
124 elif openid_response.status == SETUP_NEEDED:
125 return on_failure(request, 'Setup needed')
126 else:
127 assert False, "Bad openid status: %s" % openid_response.status
129 def default_on_success(request, identity_url, openid_response):
130 """ default action on openid signin success """
131 request.session['openid'] = from_openid_response(openid_response)
132 return HttpResponseRedirect(clean_next(request.GET.get('next')))
134 def default_on_failure(request, message):
135 """ default failure action on signin """
136 return render('openid_failure.html', {
137 'message': message
141 def not_authenticated(func):
142 """ decorator that redirect user to next page if
143 he is already logged."""
144 def decorated(request, *args, **kwargs):
145 if request.user.is_authenticated():
146 next = request.GET.get("next", "/")
147 return HttpResponseRedirect(next)
148 return func(request, *args, **kwargs)
149 return decorated
151 @not_authenticated
152 def signin(request):
154 signin page. It manage the legacy authentification (user/password)
155 and authentification with openid.
157 url: /signin/
159 template : authopenid/signin.htm
162 on_failure = signin_failure
163 next = clean_next(request.GET.get('next'))
165 form_signin = OpenidSigninForm(initial={'next':next})
166 form_auth = OpenidAuthForm(initial={'next':next})
168 if request.POST:
169 if 'bsignin' in request.POST.keys():
170 form_signin = OpenidSigninForm(request.POST)
171 if form_signin.is_valid():
172 next = clean_next(form_signin.cleaned_data.get('next'))
173 sreg_req = sreg.SRegRequest(optional=['nickname', 'email'])
174 redirect_to = "%s%s?%s" % (
175 get_url_host(request),
176 reverse('user_complete_signin'),
177 urllib.urlencode({'next':next})
180 return ask_openid(request,
181 form_signin.cleaned_data['openid_url'],
182 redirect_to,
183 on_failure=signin_failure,
184 sreg_request=sreg_req)
186 elif 'blogin' in request.POST.keys():
187 # perform normal django authentification
188 form_auth = OpenidAuthForm(request.POST)
189 if form_auth.is_valid():
190 user_ = form_auth.get_user()
191 login(request, user_)
192 next = clean_next(form_auth.cleaned_data.get('next'))
193 return HttpResponseRedirect(next)
196 return render('authopenid/signin.html', {
197 'form1': form_auth,
198 'form2': form_signin,
199 'msg': request.GET.get('msg',''),
200 'sendpw_url': reverse('user_sendpw'),
201 }, context_instance=RequestContext(request))
203 def complete_signin(request):
204 """ in case of complete signin with openid """
205 return complete(request, signin_success, signin_failure,
206 get_url_host(request) + reverse('user_complete_signin'))
209 def signin_success(request, identity_url, openid_response):
211 openid signin success.
213 If the openid is already registered, the user is redirected to
214 url set par next or in settings with OPENID_REDIRECT_NEXT variable.
215 If none of these urls are set user is redirectd to /.
217 if openid isn't registered user is redirected to register page.
220 openid_ = from_openid_response(openid_response)
221 request.session['openid'] = openid_
222 try:
223 rel = UserAssociation.objects.get(openid_url__exact = str(openid_))
224 except:
225 # try to register this new user
226 return register(request)
227 user_ = rel.user
228 if user_.is_active:
229 user_.backend = "django.contrib.auth.backends.ModelBackend"
230 login(request, user_)
232 next = clean_next(request.GET.get('next'))
233 return HttpResponseRedirect(next)
235 def is_association_exist(openid_url):
236 """ test if an openid is already in database """
237 is_exist = True
238 try:
239 uassoc = UserAssociation.objects.get(openid_url__exact = openid_url)
240 except:
241 is_exist = False
242 return is_exist
244 @not_authenticated
245 def register(request):
247 register an openid.
249 If user is already a member he can associate its openid with
250 its account.
252 A new account could also be created and automaticaly associated
253 to the openid.
255 url : /complete/
257 template : authopenid/complete.html
260 is_redirect = False
261 next = clean_next(request.GET.get('next'))
262 openid_ = request.session.get('openid', None)
263 if not openid_:
264 return HttpResponseRedirect(reverse('user_signin') + next)
266 nickname = openid_.sreg.get('nickname', '')
267 email = openid_.sreg.get('email', '')
269 form1 = OpenidRegisterForm(initial={
270 'next': next,
271 'username': nickname,
272 'email': email,
274 form2 = OpenidVerifyForm(initial={
275 'next': next,
276 'username': nickname,
279 if request.POST:
280 just_completed = False
281 if 'bnewaccount' in request.POST.keys():
282 form1 = OpenidRegisterForm(request.POST)
283 if form1.is_valid():
284 next = clean_next(form1.cleaned_data.get('next'))
285 is_redirect = True
286 tmp_pwd = User.objects.make_random_password()
287 user_ = User.objects.create_user(form1.cleaned_data['username'],
288 form1.cleaned_data['email'], tmp_pwd)
290 # make association with openid
291 uassoc = UserAssociation(openid_url=str(openid_),
292 user_id=user_.id)
293 uassoc.save()
295 # login
296 user_.backend = "django.contrib.auth.backends.ModelBackend"
297 login(request, user_)
298 elif 'bverify' in request.POST.keys():
299 form2 = OpenidVerifyForm(request.POST)
300 if form2.is_valid():
301 is_redirect = True
302 next = clean_next(form2.cleaned_data.get('next'))
303 user_ = form2.get_user()
305 uassoc = UserAssociation(openid_url=str(openid_),
306 user_id=user_.id)
307 uassoc.save()
308 login(request, user_)
310 # redirect, can redirect only if forms are valid.
311 if is_redirect:
312 return HttpResponseRedirect(next)
314 return render('authopenid/complete.html', {
315 'form1': form1,
316 'form2': form2,
317 'nickname': nickname,
318 'email': email
319 }, context_instance=RequestContext(request))
321 def signin_failure(request, message):
323 falure with openid signin. Go back to signin page.
325 template : "authopenid/signin.html"
327 next = clean_next(request.GET.get('next'))
328 form_signin = OpenidSigninForm(initial={'next': next})
329 form_auth = OpenidAuthForm(initial={'next': next})
331 return render('authopenid/signin.html', {
332 'msg': message,
333 'form1': form_auth,
334 'form2': form_signin,
335 }, context_instance=RequestContext(request))
337 @not_authenticated
338 def signup(request):
340 signup page. Create a legacy account
342 url : /signup/"
344 templates: authopenid/signup.html, authopenid/confirm_email.txt
346 action_signin = reverse('user_signin')
347 next = clean_next(request.GET.get('next'))
348 form = RegistrationForm(initial={'next':next})
349 form_signin = OpenidSigninForm(initial={'next':next})
351 if request.POST:
352 form = RegistrationForm(request.POST)
353 if form.is_valid():
354 next = clean_next(form.cleaned_data.get('next'))
355 user_ = User.objects.create_user( form.cleaned_data['username'],
356 form.cleaned_data['email'], form.cleaned_data['password1'])
358 user_.backend = "django.contrib.auth.backends.ModelBackend"
359 login(request, user_)
361 # send email
362 current_domain = Site.objects.get_current().domain
363 subject = _("Welcome")
364 message_template = loader.get_template(
365 'authopenid/confirm_email.txt'
367 message_context = Context({
368 'site_url': 'http://%s/' % current_domain,
369 'username': form.cleaned_data['username'],
370 'password': form.cleaned_data['password1']
372 message = message_template.render(message_context)
373 send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
374 [user_.email])
376 return HttpResponseRedirect(next)
378 return render('authopenid/signup.html', {
379 'form': form,
380 'form2': form_signin,
381 }, context_instance=RequestContext(request))
383 @login_required
384 def signout(request):
386 signout from the website. Remove openid from session and kill it.
388 url : /signout/"
390 try:
391 del request.session['openid']
392 except KeyError:
393 pass
394 next = clean_next(request.GET.get('next'))
395 logout(request)
397 return HttpResponseRedirect(next)
399 def xrdf(request):
400 url_host = get_url_host(request)
401 return_to = [
402 "%s%s" % (url_host, reverse('user_complete_signin'))
404 return render('authopenid/yadis.xrdf', {
405 'return_to': return_to
406 }, context_instance=RequestContext(request))
408 @login_required
409 def account_settings(request):
411 index pages to changes some basic account settings :
412 - change password
413 - change email
414 - associate a new openid
415 - delete account
417 url : /
419 template : authopenid/settings.html
421 msg = request.GET.get('msg', '')
422 is_openid = True
424 try:
425 uassoc = UserAssociation.objects.get(
426 user__username__exact=request.user.username
428 except:
429 is_openid = False
432 return render('authopenid/settings.html', {
433 'msg': msg,
434 'is_openid': is_openid
435 }, context_instance=RequestContext(request))
437 @login_required
438 def changepw(request):
440 change password view.
442 url : /changepw/
443 template: authopenid/changepw.html
446 user_ = request.user
448 if request.POST:
449 form = ChangepwForm(request.POST, user=user_)
450 if form.is_valid():
451 user_.set_password(form.cleaned_data['password1'])
452 user_.save()
453 msg = _("Password changed.")
454 redirect = "%s?msg=%s" % (
455 reverse('user_account_settings'),
456 urlquote_plus(msg))
457 return HttpResponseRedirect(redirect)
458 else:
459 form = ChangepwForm(user=user_)
461 return render('authopenid/changepw.html', {'form': form },
462 context_instance=RequestContext(request))
464 @login_required
465 def changeemail(request):
466 """
467 changeemail view. It require password or openid to allow change.
469 url: /changeemail/
471 template : authopenid/changeemail.html
473 msg = request.GET.get('msg', '')
474 extension_args = {}
475 user_ = request.user
477 redirect_to = get_url_host(request) + reverse('user_changeemail')
479 if request.POST:
480 form = ChangeemailForm(request.POST, user=user_)
481 if form.is_valid():
482 if not form.test_openid:
483 user_.email = form.cleaned_data['email']
484 user_.save()
485 msg = _("Email changed.")
486 redirect = "%s?msg=%s" % (reverse('user_account_settings'),
487 urlquote_plus(msg))
488 return HttpResponseRedirect(redirect)
489 else:
490 request.session['new_email'] = form.cleaned_data['email']
491 return ask_openid(request, form.cleaned_data['password'],
492 redirect_to, on_failure=emailopenid_failure)
493 elif not request.POST and 'openid.mode' in request.GET:
494 return complete(request, emailopenid_success,
495 emailopenid_failure, redirect_to)
496 else:
497 form = ChangeemailForm(initial={'email': user_.email},
498 user=user_)
500 return render('authopenid/changeemail.html', {
501 'form': form,
502 'msg': msg
503 }, context_instance=RequestContext(request))
506 def emailopenid_success(request, identity_url, openid_response):
507 openid_ = from_openid_response(openid_response)
509 user_ = request.user
510 try:
511 uassoc = UserAssociation.objects.get(
512 openid_url__exact=identity_url
514 except:
515 return emailopenid_failure(request,
516 _("No OpenID %s found associated in our database" % identity_url))
518 if uassoc.user.username != request.user.username:
519 return emailopenid_failure(request,
520 _("The OpenID %s isn't associated to current user logged in" %
521 identity_url))
523 new_email = request.session.get('new_email', '')
524 if new_email:
525 user_.email = new_email
526 user_.save()
527 del request.session['new_email']
528 msg = _("Email Changed.")
530 redirect = "%s?msg=%s" % (reverse('user_account_settings'),
531 urlquote_plus(msg))
532 return HttpResponseRedirect(redirect)
535 def emailopenid_failure(request, message):
536 redirect_to = "%s?msg=%s" % (
537 reverse('user_changeemail'), urlquote_plus(message))
538 return HttpResponseRedirect(redirect_to)
540 @login_required
541 def changeopenid(request):
543 change openid view. Allow user to change openid
544 associated to its username.
546 url : /changeopenid/
548 template: authopenid/changeopenid.html
551 extension_args = {}
552 openid_url = ''
553 has_openid = True
554 msg = request.GET.get('msg', '')
556 user_ = request.user
558 try:
559 uopenid = UserAssociation.objects.get(user=user_)
560 openid_url = uopenid.openid_url
561 except:
562 has_openid = False
564 redirect_to = get_url_host(request) + reverse('user_changeopenid')
565 if request.POST and has_openid:
566 form = ChangeopenidForm(request.POST, user=user_)
567 if form.is_valid():
568 return ask_openid(request, form.cleaned_data['openid_url'],
569 redirect_to, on_failure=changeopenid_failure)
570 elif not request.POST and has_openid:
571 if 'openid.mode' in request.GET:
572 return complete(request, changeopenid_success,
573 changeopenid_failure, redirect_to)
575 form = ChangeopenidForm(initial={'openid_url': openid_url }, user=user_)
576 return render('authopenid/changeopenid.html', {
577 'form': form,
578 'has_openid': has_openid,
579 'msg': msg
580 }, context_instance=RequestContext(request))
582 def changeopenid_success(request, identity_url, openid_response):
583 openid_ = from_openid_response(openid_response)
584 is_exist = True
585 try:
586 uassoc = UserAssociation.objects.get(openid_url__exact=identity_url)
587 except:
588 is_exist = False
590 if not is_exist:
591 try:
592 uassoc = UserAssociation.objects.get(
593 user__username__exact=request.user.username
595 uassoc.openid_url = identity_url
596 uassoc.save()
597 except:
598 uassoc = UserAssociation(user=request.user,
599 openid_url=identity_url)
600 uassoc.save()
601 elif uassoc.user.username != request.user.username:
602 return changeopenid_failure(request,
603 _('This OpenID is already associated with another account.'))
605 request.session['openids'] = []
606 request.session['openids'].append(openid_)
608 msg = _("OpenID %s is now associated with your account." % identity_url)
609 redirect = "%s?msg=%s" % (
610 reverse('user_account_settings'),
611 urlquote_plus(msg))
612 return HttpResponseRedirect(redirect)
615 def changeopenid_failure(request, message):
616 redirect_to = "%s?msg=%s" % (
617 reverse('user_changeopenid'),
618 urlquote_plus(message))
619 return HttpResponseRedirect(redirect_to)
621 @login_required
622 def delete(request):
624 delete view. Allow user to delete its account. Password/openid are required to
625 confirm it. He should also check the confirm checkbox.
627 url : /delete
629 template : authopenid/delete.html
632 extension_args = {}
634 user_ = request.user
636 redirect_to = get_url_host(request) + reverse('user_delete')
637 if request.POST:
638 form = DeleteForm(request.POST, user=user_)
639 if form.is_valid():
640 if not form.test_openid:
641 user_.delete()
642 return signout(request)
643 else:
644 return ask_openid(request, form.cleaned_data['password'],
645 redirect_to, on_failure=deleteopenid_failure)
646 elif not request.POST and 'openid.mode' in request.GET:
647 return complete(request, deleteopenid_success, deleteopenid_failure,
648 redirect_to)
650 form = DeleteForm(user=user_)
652 msg = request.GET.get('msg','')
653 return render('authopenid/delete.html', {
654 'form': form,
655 'msg': msg,
656 }, context_instance=RequestContext(request))
658 def deleteopenid_success(request, identity_url, openid_response):
659 openid_ = from_openid_response(openid_response)
661 user_ = request.user
662 try:
663 uassoc = UserAssociation.objects.get(
664 openid_url__exact=identity_url
666 except:
667 return deleteopenid_failure(request,
668 _("No OpenID %s found associated in our database" % identity_url))
670 if uassoc.user.username == user_.username:
671 user_.delete()
672 return signout(request)
673 else:
674 return deleteopenid_failure(request,
675 _("The OpenID %s isn't associated to current user logged in" %
676 identity_url))
678 msg = _("Account deleted.")
679 redirect = "/?msg=%s" % (urlquote_plus(msg))
680 return HttpResponseRedirect(redirect)
683 def deleteopenid_failure(request, message):
684 redirect_to = "%s?msg=%s" % (reverse('user_delete'), urlquote_plus(message))
685 return HttpResponseRedirect(redirect_to)
688 def sendpw(request):
690 send a new password to the user. It return a mail with
691 a new pasword and a confirm link in. To activate the
692 new password, the user should click on confirm link.
694 url : /sendpw/
696 templates : authopenid/sendpw_email.txt, authopenid/sendpw.html
699 msg = request.GET.get('msg','')
700 if request.POST:
701 form = EmailPasswordForm(request.POST)
702 if form.is_valid():
703 new_pw = User.objects.make_random_password()
704 confirm_key = UserPasswordQueue.objects.get_new_confirm_key()
705 try:
706 uqueue = UserPasswordQueue.objects.get(
707 user=form.user_cache
709 except:
710 uqueue = UserPasswordQueue(
711 user=form.user_cache
713 uqueue.new_password = new_pw
714 uqueue.confirm_key = confirm_key
715 uqueue.save()
716 # send email
717 current_domain = Site.objects.get_current().domain
718 subject = _("Request for new password")
719 message_template = loader.get_template(
720 'authopenid/sendpw_email.txt')
721 message_context = Context({
722 'site_url': 'http://%s' % current_domain,
723 'confirm_key': confirm_key,
724 'username': form.user_cache.username,
725 'password': new_pw,
726 'url_confirm': reverse('user_confirmchangepw'),
728 message = message_template.render(message_context)
729 send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
730 [form.user_cache.email])
731 msg = _("A new password has been sent to your email address.")
732 else:
733 form = EmailPasswordForm()
735 return render('authopenid/sendpw.html', {
736 'form': form,
737 'msg': msg
738 }, context_instance=RequestContext(request))
741 def confirmchangepw(request):
743 view to set new password when the user click on confirm link
744 in its mail. Basically it check if the confirm key exist, then
745 replace old password with new password and remove confirm
746 ley from the queue. Then it redirect the user to signin
747 page.
749 url : /sendpw/confirm/?key
752 confirm_key = request.GET.get('key', '')
753 if not confirm_key:
754 return HttpResponseRedirect('/')
756 try:
757 uqueue = UserPasswordQueue.objects.get(
758 confirm_key__exact=confirm_key
760 except:
761 msg = _("Could not change password. Confirmation key '%s'\
762 is not registered." % confirm_key)
763 redirect = "%s?msg=%s" % (
764 reverse('user_sendpw'), urlquote_plus(msg))
765 return HttpResponseRedirect(redirect)
767 try:
768 user_ = User.objects.get(id=uqueue.user.id)
769 except:
770 msg = _("Can not change password. User don't exist anymore \
771 in our database.")
772 redirect = "%s?msg=%s" % (reverse('user_sendpw'),
773 urlquote_plus(msg))
774 return HttpResponseRedirect(redirect)
776 user_.set_password(uqueue.new_password)
777 user_.save()
778 uqueue.delete()
779 msg = _("Password changed for %s. You may now sign in." %
780 user_.username)
781 redirect = "%s?msg=%s" % (reverse('user_signin'),
782 urlquote_plus(msg))
784 return HttpResponseRedirect(redirect)