diff options
| author | Claude Paroz <claude@2xlibre.net> | 2016-05-15 17:28:00 +0200 |
|---|---|---|
| committer | Claude Paroz <claude@2xlibre.net> | 2016-06-24 10:45:13 +0200 |
| commit | 78963495d0caadb77eb97ccf319ef0ba3b204fb5 (patch) | |
| tree | 52162432f13b92b85b6188a6415887cfc06c5701 /django | |
| parent | 742ea51413b3aab07c6afbfd1d52c1908ffcb510 (diff) | |
Refs #17209 -- Added LoginView and LogoutView class-based views
Thanks Tim Graham for the review.
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/admin/sites.py | 8 | ||||
| -rw-r--r-- | django/contrib/auth/urls.py | 4 | ||||
| -rw-r--r-- | django/contrib/auth/views.py | 181 |
3 files changed, 116 insertions, 77 deletions
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index aaa56f1296..707428b055 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -327,7 +327,7 @@ class AdminSite(object): This should *not* assume the user is already logged in. """ - from django.contrib.auth.views import logout + from django.contrib.auth.views import LogoutView defaults = { 'extra_context': dict( self.each_context(request), @@ -340,7 +340,7 @@ class AdminSite(object): if self.logout_template is not None: defaults['template_name'] = self.logout_template request.current_app = self.name - return logout(request, **defaults) + return LogoutView.as_view(**defaults)(request) @never_cache def login(self, request, extra_context=None): @@ -352,7 +352,7 @@ class AdminSite(object): index_path = reverse('admin:index', current_app=self.name) return HttpResponseRedirect(index_path) - from django.contrib.auth.views import login + from django.contrib.auth.views import LoginView # Since this module gets imported in the application's root package, # it cannot import models from other applications at the module level, # and django.contrib.admin.forms eventually imports User. @@ -374,7 +374,7 @@ class AdminSite(object): 'template_name': self.login_template or 'admin/login.html', } request.current_app = self.name - return login(request, **defaults) + return LoginView.as_view(**defaults)(request) def _build_app_dict(self, request, label=None): """ diff --git a/django/contrib/auth/urls.py b/django/contrib/auth/urls.py index ab2ba9e9e0..008ed092a6 100644 --- a/django/contrib/auth/urls.py +++ b/django/contrib/auth/urls.py @@ -7,8 +7,8 @@ from django.conf.urls import url from django.contrib.auth import views urlpatterns = [ - url(r'^login/$', views.login, name='login'), - url(r'^logout/$', views.logout, name='logout'), + url(r'^login/$', views.LoginView.as_view(), name='login'), + url(r'^logout/$', views.LogoutView.as_view(), name='logout'), url(r'^password_change/$', views.password_change, name='password_change'), url(r'^password_change/done/$', views.password_change_done, name='password_change_done'), url(r'^password_reset/$', views.password_reset, name='password_reset'), diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 3cf7c36b8b..38a0f658c1 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -17,7 +17,10 @@ from django.http import HttpResponseRedirect, QueryDict from django.shortcuts import resolve_url from django.template.response import TemplateResponse from django.urls import reverse -from django.utils.deprecation import RemovedInDjango20Warning +from django.utils.decorators import method_decorator +from django.utils.deprecation import ( + RemovedInDjango20Warning, RemovedInDjango21Warning, +) from django.utils.encoding import force_text from django.utils.http import is_safe_url, urlsafe_base64_decode from django.utils.six.moves.urllib.parse import urlparse, urlunparse @@ -25,6 +28,8 @@ from django.utils.translation import ugettext as _ from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_protect from django.views.decorators.debug import sensitive_post_parameters +from django.views.generic.base import TemplateView +from django.views.generic.edit import FormView def deprecate_current_app(func): @@ -48,94 +53,128 @@ def deprecate_current_app(func): return inner -def _get_login_redirect_url(request, redirect_to): - # Ensure the user-originating redirection URL is safe. - if not is_safe_url(url=redirect_to, host=request.get_host()): - return resolve_url(settings.LOGIN_REDIRECT_URL) - return redirect_to - - -@deprecate_current_app -@sensitive_post_parameters() -@csrf_protect -@never_cache -def login(request, template_name='registration/login.html', - redirect_field_name=REDIRECT_FIELD_NAME, - authentication_form=AuthenticationForm, - extra_context=None, redirect_authenticated_user=False): +class LoginView(FormView): """ Displays the login form and handles the login action. """ - redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, '')) + form_class = AuthenticationForm + authentication_form = None + redirect_field_name = REDIRECT_FIELD_NAME + template_name = 'registration/login.html' + redirect_authenticated_user = False + extra_context = None - if redirect_authenticated_user and request.user.is_authenticated: - redirect_to = _get_login_redirect_url(request, redirect_to) - if redirect_to == request.path: - raise ValueError( - "Redirection loop for authenticated user detected. Check that " - "your LOGIN_REDIRECT_URL doesn't point to a login page." - ) - return HttpResponseRedirect(redirect_to) - elif request.method == "POST": - form = authentication_form(request, data=request.POST) - if form.is_valid(): - auth_login(request, form.get_user()) - return HttpResponseRedirect(_get_login_redirect_url(request, redirect_to)) - else: - form = authentication_form(request) + @method_decorator(sensitive_post_parameters()) + @method_decorator(csrf_protect) + @method_decorator(never_cache) + def dispatch(self, request, *args, **kwargs): + if self.redirect_authenticated_user and self.request.user.is_authenticated: + redirect_to = self.get_success_url() + if redirect_to == self.request.path: + raise ValueError( + "Redirection loop for authenticated user detected. Check that " + "your LOGIN_REDIRECT_URL doesn't point to a login page." + ) + return HttpResponseRedirect(redirect_to) + return super(LoginView, self).dispatch(request, *args, **kwargs) - current_site = get_current_site(request) + def get_success_url(self): + """Ensure the user-originating redirection URL is safe.""" + redirect_to = self.request.POST.get( + self.redirect_field_name, + self.request.GET.get(self.redirect_field_name, '') + ) + if not is_safe_url(url=redirect_to, host=self.request.get_host()): + return resolve_url(settings.LOGIN_REDIRECT_URL) + return redirect_to - context = { - 'form': form, - redirect_field_name: redirect_to, - 'site': current_site, - 'site_name': current_site.name, - } - if extra_context is not None: - context.update(extra_context) + def get_form_class(self): + return self.authentication_form or self.form_class - return TemplateResponse(request, template_name, context) + def form_valid(self, form): + """Security check complete. Log the user in.""" + auth_login(self.request, form.get_user()) + return HttpResponseRedirect(self.get_success_url()) + + def get_context_data(self, **kwargs): + context = super(LoginView, self).get_context_data(**kwargs) + current_site = get_current_site(self.request) + context.update({ + self.redirect_field_name: self.get_success_url(), + 'site': current_site, + 'site_name': current_site.name, + }) + if self.extra_context is not None: + context.update(self.extra_context) + return context @deprecate_current_app -@never_cache -def logout(request, next_page=None, - template_name='registration/logged_out.html', - redirect_field_name=REDIRECT_FIELD_NAME, - extra_context=None): +def login(request, *args, **kwargs): + warnings.warn( + 'The login() view is superseded by the class-based LoginView().', + RemovedInDjango21Warning, stacklevel=2 + ) + return LoginView.as_view(**kwargs)(request, *args, **kwargs) + + +class LogoutView(TemplateView): """ Logs out the user and displays 'You are logged out' message. """ - auth_logout(request) + next_page = None + redirect_field_name = REDIRECT_FIELD_NAME + template_name = 'registration/logged_out.html' + extra_context = None + + @method_decorator(never_cache) + def dispatch(self, request, *args, **kwargs): + auth_logout(request) + next_page = self.get_next_page() + if next_page: + # Redirect to this page until the session has been cleared. + return HttpResponseRedirect(next_page) + return super(LogoutView, self).dispatch(request, *args, **kwargs) - if next_page is not None: - next_page = resolve_url(next_page) - elif settings.LOGOUT_REDIRECT_URL: - next_page = resolve_url(settings.LOGOUT_REDIRECT_URL) + def get_next_page(self): + if self.next_page is not None: + next_page = resolve_url(self.next_page) + elif settings.LOGOUT_REDIRECT_URL: + next_page = resolve_url(settings.LOGOUT_REDIRECT_URL) + else: + next_page = self.next_page - if (redirect_field_name in request.POST or - redirect_field_name in request.GET): - next_page = request.POST.get(redirect_field_name, - request.GET.get(redirect_field_name)) - # Security check -- don't allow redirection to a different host. - if not is_safe_url(url=next_page, host=request.get_host()): - next_page = request.path + if (self.redirect_field_name in self.request.POST or + self.redirect_field_name in self.request.GET): + next_page = self.request.POST.get( + self.redirect_field_name, + self.request.GET.get(self.redirect_field_name) + ) + # Security check -- don't allow redirection to a different host. + if not is_safe_url(url=next_page, host=self.request.get_host()): + next_page = self.request.path + return next_page - if next_page: - # Redirect to this page until the session has been cleared. - return HttpResponseRedirect(next_page) + def get_context_data(self, **kwargs): + context = super(LogoutView, self).get_context_data(**kwargs) + current_site = get_current_site(self.request) + context.update({ + 'site': current_site, + 'site_name': current_site.name, + 'title': _('Logged out'), + }) + if self.extra_context is not None: + context.update(self.extra_context) + return context - current_site = get_current_site(request) - context = { - 'site': current_site, - 'site_name': current_site.name, - 'title': _('Logged out') - } - if extra_context is not None: - context.update(extra_context) - return TemplateResponse(request, template_name, context) +@deprecate_current_app +def logout(request, *args, **kwargs): + warnings.warn( + 'The logout() view is superseded by the class-based LogoutView().', + RemovedInDjango21Warning, stacklevel=2 + ) + return LogoutView.as_view(**kwargs)(request, *args, **kwargs) @deprecate_current_app |
