PageRenderTime 70ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/apps/openidconsumer/views.py

https://bitbucket.org/resplin/byteflow
Python | 278 lines | 237 code | 34 blank | 7 comment | 33 complexity | 94358e29df1b76db3e2a0c3f703c4084 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. import re
  2. from urllib import urlencode
  3. from openid.consumer.consumer import Consumer, SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
  4. from openid.consumer.discover import DiscoveryFailure
  5. from openid.yadis import xri
  6. from django.contrib.auth.decorators import login_required
  7. from django.http import HttpResponseRedirect
  8. from django.conf import settings
  9. from django.contrib.auth import authenticate, login, logout
  10. from django.core.urlresolvers import reverse
  11. from django.utils.translation import ugettext_lazy as _
  12. from django.views.generic.list_detail import object_list
  13. from lib.decorators import render_to
  14. from lib.exceptions import RedirectException
  15. from lib.helpers import absolutize_uri
  16. from openidconsumer.util import DjangoOpenIDStore, from_openid_response
  17. from openidconsumer.forms import OpenidSigninForm, OpenidRegistrationForm, OpenidVerifyForm, OpenidAssociateForm
  18. from openidconsumer.models import UserAssociation
  19. from accounts.views import redirect
  20. def get_address(path, request):
  21. try:
  22. out = path.split(request.get_host(), 1)[1]
  23. except AttributeError:
  24. return None
  25. nickname_re = re.compile(r'^https?://(?:www\.)?([^\.]+)')
  26. next_url_re = re.compile(r'^/[-\w/]+$')
  27. def is_valid_next_url(next):
  28. # When we allow this:
  29. # /openid/?next=/welcome/
  30. # For security reasons we want to restrict the next= bit to being a local
  31. # path, not a complete URL.
  32. return bool(next_url_re.match(str(next)))
  33. @render_to('openid/failure.html')
  34. def default_on_failure(request, message):
  35. return {'message': message}
  36. def default_on_success(request, identity_url, openid_response):
  37. if 'openids' not in request.session.keys():
  38. request.session['openids'] = []
  39. # Eliminate any duplicates
  40. request.session['openids'] = [o for o in request.session['openids'] if o.openid != identity_url]
  41. openid = from_openid_response(openid_response)
  42. request.session['openids'].append(openid)
  43. user = authenticate(openid=str(openid))
  44. if not user:
  45. return register_new_openid(request)
  46. login(request, user)
  47. if is_valid_next_url(request.GET.get('next')):
  48. next = request.GET['next']
  49. else:
  50. next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
  51. return HttpResponseRedirect(next)
  52. @render_to('openid/signin.html')
  53. def signin(request, sreg=None, extension_args={}, on_failure=default_on_failure):
  54. openid_url = None
  55. if is_valid_next_url(request.GET.get('next')):
  56. next = request.GET['next']
  57. elif is_valid_next_url(get_address(request.META.get('HTTP_REFERER'), request)):
  58. next = get_address(request.META['HTTP_REFERER'], request)
  59. else:
  60. next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
  61. def get_with_next(url, next):
  62. if next:
  63. return '%s?%s' % (url, urlencode({'next': next}))
  64. else:
  65. return url
  66. if request.user.is_authenticated() and 'force' not in request.GET:
  67. return HttpResponseRedirect(next)
  68. request_path = get_with_next(request.path, next)
  69. if request.method == 'POST':
  70. form = OpenidSigninForm(request.POST, auto_id='id_%s')
  71. if form.is_valid():
  72. # first remove email and nickname from sreg if the are in to prevent dup
  73. extension_args['sreg.optional'] = 'email,nickname'
  74. if sreg:
  75. sreg = ','.join([arg for arg in sreg.split(',') if arg not in extension_args['sreg.optional']])
  76. extension_args['sreg.optional'] += ',' + sreg
  77. trust_root = getattr(settings, 'OPENID_TRUST_ROOT', absolutize_uri(request, '/'))
  78. redirect_to = get_with_next(absolutize_uri(request, reverse('openid_complete')), next)
  79. if xri.identifierScheme(form.cleaned_data['openid_url']) == 'XRI' and getattr(
  80. settings, 'OPENID_DISALLOW_INAMES', False):
  81. return on_failure(request, _("i-names are not supported"))
  82. consumer = Consumer(request.session, DjangoOpenIDStore())
  83. try:
  84. auth_request = consumer.begin(form.cleaned_data['openid_url'])
  85. except DiscoveryFailure:
  86. return on_failure(request, _("The OpenID was invalid"))
  87. # Add extension args (for things like simple registration)
  88. for name, value in extension_args.items():
  89. namespace, key = name.split('.', 1)
  90. auth_request.addExtensionArg(namespace, key, value)
  91. redirect_url = auth_request.redirectURL(trust_root, redirect_to)
  92. return HttpResponseRedirect(redirect_url)
  93. else:
  94. form = OpenidSigninForm(auto_id='id_%s')
  95. return {
  96. 'form': form,
  97. 'action': request_path,
  98. }
  99. def complete(request, on_success=default_on_success, on_failure=default_on_failure):
  100. consumer = Consumer(request.session, DjangoOpenIDStore())
  101. openid_response = consumer.complete(dict(request.GET.items()),
  102. request.GET.get("openid.return_to", "/"))
  103. if openid_response.status == SUCCESS:
  104. return on_success(request, openid_response.identity_url, openid_response)
  105. elif openid_response.status == CANCEL:
  106. return on_failure(request, _("The request was cancelled"))
  107. elif openid_response.status == FAILURE:
  108. return on_failure(request, openid_response.message)
  109. elif openid_response.status == SETUP_NEEDED:
  110. return on_failure(request, 'Setup needed')
  111. else:
  112. assert False, "Bad openid status: %s" % openid_response.status
  113. def register_new_openid(request):
  114. if request.GET.get('next'):
  115. next = "?" + urlencode({
  116. 'next': request.GET['next']
  117. })
  118. else:
  119. next = ''
  120. openids = request.session.get('openids', [])
  121. if openids and len(openids) > 0:
  122. openid = openids[-1] # Last authenticated OpenID
  123. else:
  124. raise RedirectException(reverse('openid_signin') + next)
  125. nickname = openid.sreg.get('nickname', '')
  126. email = openid.sreg.get('email', '')
  127. if not nickname:
  128. nickname = nickname_re.search(openid.openid).group(1)
  129. if nickname in ('www', 'livejournal', 'users'):
  130. nickname = ''
  131. if request.user.is_authenticated():
  132. form = OpenidAssociateForm(request.user, {'openid': openid.openid})
  133. else:
  134. form = OpenidRegistrationForm(openid.openid, {'name': nickname, 'email': email})
  135. if form.is_valid():
  136. form.save()
  137. user = authenticate(openid=openid.openid)
  138. if user:
  139. login(request, user)
  140. next = request.GET.get('next', '').strip()
  141. if not next or not is_valid_next_url(next):
  142. next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
  143. raise RedirectException(next)
  144. else:
  145. return register(request)
  146. @render_to('openid/complete.html')
  147. def register(request):
  148. is_redirect = False
  149. next=''
  150. if request.GET.get('next'):
  151. next = "?" + urlencode({
  152. 'next': request.GET['next']
  153. })
  154. openids = request.session.get('openids', [])
  155. if openids and len(openids) > 0:
  156. openid = openids[-1] # Last authenticated OpenID
  157. else:
  158. return HttpResponseRedirect(reverse('openid_signin') + next)
  159. nickname = openid.sreg.get('nickname', '')
  160. email = openid.sreg.get('email', '')
  161. if not nickname:
  162. nickname = openid.openid[7:].strip('w.').split('.')[0]
  163. if nickname in ('www', 'livejournal', 'users'):
  164. nickname = ''
  165. form1 = OpenidRegistrationForm(openid.openid, initial={'name': nickname, 'email': email})
  166. form2 = OpenidVerifyForm(initial={'email': email})
  167. if request.POST:
  168. if 'bnewaccount' in request.POST:
  169. form1 = OpenidRegistrationForm(request.POST)
  170. if form1.is_valid():
  171. is_redirect = True
  172. form1.save()
  173. user = authenticate(openid=openid.openid)
  174. if user:
  175. login(request, user)
  176. elif 'bverify' in request.POST:
  177. form2 = OpenidVerifyForm(request.POST)
  178. if form2.is_valid():
  179. is_redirect = True
  180. user = form2.get_user()
  181. ua = UserAssociation(openid_url=openid.openid, user=user)
  182. ua.save()
  183. login(request, user)
  184. # redirect, can redirect only if forms are valid.
  185. if is_redirect:
  186. next = request.GET.get('next', '').strip()
  187. if not next or not is_valid_next_url(next):
  188. next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
  189. return HttpResponseRedirect(next)
  190. return {
  191. 'form1': form1,
  192. 'form2': form2,
  193. 'action': reverse('openidconsumer.views.register') + next,
  194. 'nickname': nickname,
  195. 'email': email,
  196. }
  197. def signout(request):
  198. request.session['openids'] = []
  199. next = request.GET.get('next', '/')
  200. if not is_valid_next_url(next):
  201. next = '/'
  202. logout(request)
  203. return HttpResponseRedirect(next)
  204. @login_required
  205. def openid_list(request, **kwargs):
  206. kwargs['queryset'] = UserAssociation.objects.filter(user=request.user)
  207. kwargs['template_name'] = 'openid/list.html'
  208. kwargs['extra_context'] = {'form': OpenidSigninForm(auto_id='id_%s')}
  209. return object_list(request, **kwargs)
  210. @login_required
  211. def delete(request):
  212. next = redirect(request)
  213. if not request.method == 'POST' or not request.POST.get('openid'):
  214. raise RedirectException(next, _('Invalid POST data'))
  215. if not UserAssociation.objects.filter(user=request.user).count() > 1 and not request.user.email:
  216. raise RedirectException(next, _("You must have at least one OpenID if you don't have email!"))
  217. try:
  218. ua = UserAssociation.objects.get(openid_url=request.POST['openid'], user=request.user)
  219. ua.delete()
  220. except UserAssociation.DoesNotExist:
  221. pass
  222. raise RedirectException(next, _("OpenID deassociated successfully"))