Loading django/contrib/admin/sites.py +4 −4 Original line number Diff line number Diff line Loading @@ -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), Loading @@ -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): Loading @@ -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. Loading @@ -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): """ Loading django/contrib/auth/urls.py +2 −2 Original line number Diff line number Diff line Loading @@ -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'), Loading django/contrib/auth/views.py +117 −78 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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): Loading @@ -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, '')) if redirect_authenticated_user and request.user.is_authenticated: redirect_to = _get_login_redirect_url(request, redirect_to) if redirect_to == request.path: form_class = AuthenticationForm authentication_form = None redirect_field_name = REDIRECT_FIELD_NAME template_name = 'registration/login.html' redirect_authenticated_user = False extra_context = None @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) 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) 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, def get_form_class(self): return self.authentication_form or self.form_class 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 extra_context is not None: context.update(extra_context) return TemplateResponse(request, template_name, context) }) 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. """ 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) 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)) 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=request.get_host()): next_page = request.path if next_page: # Redirect to this page until the session has been cleared. return HttpResponseRedirect(next_page) current_site = get_current_site(request) context = { if not is_safe_url(url=next_page, host=self.request.get_host()): next_page = self.request.path return 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 extra_context is not None: context.update(extra_context) 'title': _('Logged out'), }) if self.extra_context is not None: context.update(self.extra_context) return 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 Loading docs/internals/deprecation.txt +2 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ about each item can often be found in the release notes of two versions prior. See the :ref:`Django 1.11 release notes<deprecated-features-1.11>` for more details on these changes. * ``contrib.auth.views.login()`` and ``logout()`` will be removed. .. _deprecation-removed-in-2.0: 2.0 Loading docs/ref/contrib/auth.txt +2 −2 Original line number Diff line number Diff line Loading @@ -93,7 +93,7 @@ Fields if you want to allow inactive users to login. In this case, you'll also want to customize the :class:`~django.contrib.auth.forms.AuthenticationForm` used by the :func:`~django.contrib.auth.views.login` view as it rejects inactive :class:`~django.contrib.auth.views.LoginView` as it rejects inactive users. Be aware that the permission-checking methods such as :meth:`~django.contrib.auth.models.User.has_perm` and the authentication in the Django admin all return ``False`` for inactive Loading Loading @@ -582,7 +582,7 @@ The following backends are available in :mod:`django.contrib.auth.backends`: When using this backend, you'll likely want to customize the :class:`~django.contrib.auth.forms.AuthenticationForm` used by the :func:`~django.contrib.auth.views.login` view by overriding the :class:`~django.contrib.auth.views.LoginView` by overriding the :meth:`~django.contrib.auth.forms.AuthenticationForm.confirm_login_allowed` method as it rejects inactive users. Loading Loading
django/contrib/admin/sites.py +4 −4 Original line number Diff line number Diff line Loading @@ -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), Loading @@ -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): Loading @@ -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. Loading @@ -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): """ Loading
django/contrib/auth/urls.py +2 −2 Original line number Diff line number Diff line Loading @@ -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'), Loading
django/contrib/auth/views.py +117 −78 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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): Loading @@ -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, '')) if redirect_authenticated_user and request.user.is_authenticated: redirect_to = _get_login_redirect_url(request, redirect_to) if redirect_to == request.path: form_class = AuthenticationForm authentication_form = None redirect_field_name = REDIRECT_FIELD_NAME template_name = 'registration/login.html' redirect_authenticated_user = False extra_context = None @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) 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) 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, def get_form_class(self): return self.authentication_form or self.form_class 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 extra_context is not None: context.update(extra_context) return TemplateResponse(request, template_name, context) }) 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. """ 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) 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)) 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=request.get_host()): next_page = request.path if next_page: # Redirect to this page until the session has been cleared. return HttpResponseRedirect(next_page) current_site = get_current_site(request) context = { if not is_safe_url(url=next_page, host=self.request.get_host()): next_page = self.request.path return 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 extra_context is not None: context.update(extra_context) 'title': _('Logged out'), }) if self.extra_context is not None: context.update(self.extra_context) return 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 Loading
docs/internals/deprecation.txt +2 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ about each item can often be found in the release notes of two versions prior. See the :ref:`Django 1.11 release notes<deprecated-features-1.11>` for more details on these changes. * ``contrib.auth.views.login()`` and ``logout()`` will be removed. .. _deprecation-removed-in-2.0: 2.0 Loading
docs/ref/contrib/auth.txt +2 −2 Original line number Diff line number Diff line Loading @@ -93,7 +93,7 @@ Fields if you want to allow inactive users to login. In this case, you'll also want to customize the :class:`~django.contrib.auth.forms.AuthenticationForm` used by the :func:`~django.contrib.auth.views.login` view as it rejects inactive :class:`~django.contrib.auth.views.LoginView` as it rejects inactive users. Be aware that the permission-checking methods such as :meth:`~django.contrib.auth.models.User.has_perm` and the authentication in the Django admin all return ``False`` for inactive Loading Loading @@ -582,7 +582,7 @@ The following backends are available in :mod:`django.contrib.auth.backends`: When using this backend, you'll likely want to customize the :class:`~django.contrib.auth.forms.AuthenticationForm` used by the :func:`~django.contrib.auth.views.login` view by overriding the :class:`~django.contrib.auth.views.LoginView` by overriding the :meth:`~django.contrib.auth.forms.AuthenticationForm.confirm_login_allowed` method as it rejects inactive users. Loading