views.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. import logging
  5. from django.conf import settings
  6. from django.contrib import auth
  7. from django.http import HttpResponse
  8. from django.middleware.csrf import get_token
  9. from django.shortcuts import resolve_url
  10. from django.utils.http import is_safe_url
  11. from django.views.decorators.cache import never_cache
  12. from django.views.generic import View
  13. from django_browserid.base import sanity_checks
  14. from django_browserid.http import JSONResponse
  15. logger = logging.getLogger(__name__)
  16. class JSONView(View):
  17. def http_method_not_allowed(self, *args, **kwargs):
  18. response = JSONResponse({'error': 'Method not allowed.'}, status=405)
  19. allowed_methods = [m.upper() for m in self.http_method_names if hasattr(self, m)]
  20. response['Allow'] = ', '.join(allowed_methods)
  21. return response
  22. def _get_next(request):
  23. """
  24. Get the next parameter from the request's POST arguments and
  25. validate it.
  26. :returns:
  27. The next parameter or None if it was not found or invalid.
  28. """
  29. next = request.POST.get('next')
  30. if is_safe_url(next, host=request.get_host()):
  31. return next
  32. else:
  33. return None
  34. class Verify(JSONView):
  35. """
  36. Send an assertion to the remote verification service, and log the
  37. user in upon success.
  38. """
  39. @property
  40. def failure_url(self):
  41. """
  42. URL to redirect users to when login fails. This uses the value
  43. of ``settings.LOGIN_REDIRECT_URL_FAILURE``, and defaults to
  44. ``'/'`` if the setting doesn't exist.
  45. """
  46. return resolve_url(
  47. getattr(settings, 'LOGIN_REDIRECT_URL_FAILURE', '/'))
  48. @property
  49. def success_url(self):
  50. """
  51. URL to redirect users to when login succeeds. This uses the
  52. value of ``settings.LOGIN_REDIRECT_URL``, and defaults to
  53. ``'/'`` if the setting doesn't exist.
  54. """
  55. return resolve_url(getattr(settings, 'LOGIN_REDIRECT_URL', '/'))
  56. def login_success(self):
  57. """Log the user into the site."""
  58. auth.login(self.request, self.user)
  59. return JSONResponse({
  60. 'email': self.user.email,
  61. 'redirect': _get_next(self.request) or self.success_url
  62. })
  63. def login_failure(self):
  64. """
  65. Redirect the user to a login-failed page. By default a 403 is
  66. returned.
  67. """
  68. return JSONResponse({'redirect': self.failure_url}, status=403)
  69. def post(self, *args, **kwargs):
  70. """
  71. Send the given assertion to the remote verification service and,
  72. depending on the result, trigger login success or failure.
  73. """
  74. assertion = self.request.POST.get('assertion')
  75. if not assertion:
  76. return self.login_failure()
  77. self.user = auth.authenticate(request=self.request, assertion=assertion)
  78. if self.user and self.user.is_active:
  79. return self.login_success()
  80. return self.login_failure()
  81. def dispatch(self, request, *args, **kwargs):
  82. """
  83. Run some sanity checks on the request prior to dispatching it.
  84. """
  85. sanity_checks(request)
  86. return super(Verify, self).dispatch(request, *args, **kwargs)
  87. class CsrfToken(JSONView):
  88. """Fetch a CSRF token for the frontend JavaScript."""
  89. @never_cache
  90. def get(self, request):
  91. # Different CSRF libraries store the CSRF token in different
  92. # places. Here we support both standard Django CSRF and the
  93. # django-session-csrf library.
  94. if hasattr(request, 'csrf_token'):
  95. csrf_token = request.csrf_token
  96. else:
  97. csrf_token = get_token(request)
  98. return HttpResponse(csrf_token)
  99. class Logout(JSONView):
  100. @property
  101. def redirect_url(self):
  102. """
  103. URL to redirect users to post-login. Uses
  104. ``settings.LOGOUT_REDIRECT_URL`` and defaults to ``/`` if the
  105. setting isn't found.
  106. """
  107. return resolve_url(getattr(settings, 'LOGOUT_REDIRECT_URL', '/'))
  108. def post(self, request):
  109. """Log the user out."""
  110. auth.logout(request)
  111. return JSONResponse({
  112. 'redirect': _get_next(self.request) or self.redirect_url
  113. })