From 33d2c53fb150d58fe4b212438050eea28bf4bf6f Mon Sep 17 00:00:00 2001 From: Camilo Nova Date: Tue, 7 Mar 2017 19:52:26 -0500 Subject: [PATCH] [1.11.x] Fixed #27891 -- Added PasswordResetConfirmView.post_reset_login_backend. Backport of 5db465d5a6c389b8f9c6e21f7233be9387dc069d from master --- django/contrib/auth/views.py | 3 ++- docs/releases/1.11.txt | 2 ++ docs/topics/auth/default.txt | 5 +++++ tests/auth_tests/test_views.py | 20 +++++++++++++++++++- tests/auth_tests/urls.py | 7 +++++++ 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index ef2a106e6f..8f841ab571 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -442,6 +442,7 @@ class PasswordResetDoneView(PasswordContextMixin, TemplateView): class PasswordResetConfirmView(PasswordContextMixin, FormView): form_class = SetPasswordForm post_reset_login = False + post_reset_login_backend = None success_url = reverse_lazy('password_reset_complete') template_name = 'registration/password_reset_confirm.html' title = _('Enter new password') @@ -494,7 +495,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): user = form.save() del self.request.session[INTERNAL_RESET_SESSION_TOKEN] if self.post_reset_login: - auth_login(self.request, user) + auth_login(self.request, user, self.post_reset_login_backend) return super(PasswordResetConfirmView, self).form_valid(form) def get_context_data(self, **kwargs): diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index eb983de0dc..3ba4eb2e35 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -118,6 +118,8 @@ Minor features * The new ``post_reset_login`` attribute for :class:`~django.contrib.auth.views.PasswordResetConfirmView` allows automatically logging in a user after a successful password reset. + If you have multiple ``AUTHENTICATION_BACKENDS`` configured, use the + ``post_reset_login_backend`` attribute to choose which one to use. * To avoid the possibility of leaking a password reset token via the HTTP Referer header (for example, if the reset page includes a reference to CSS or diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index f4090f77cb..1d91bdcf42 100644 --- a/docs/topics/auth/default.txt +++ b/docs/topics/auth/default.txt @@ -1506,6 +1506,11 @@ implementation details see :ref:`using-the-views`. automatically authenticated after a successful password reset. Defaults to ``False``. + * ``post_reset_login_backend``: A dotted path to the authentication + backend to use when authenticating a user if ``post_reset_login`` is + ``True``. Required only if you have multiple + :setting:`AUTHENTICATION_BACKENDS` configured. Defaults to ``None``. + * ``form_class``: Form that will be used to set the password. Defaults to :class:`~django.contrib.auth.forms.SetPasswordForm`. diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index 07989a23b0..a66d1e5809 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -10,7 +10,9 @@ from importlib import import_module from django.apps import apps from django.conf import settings from django.contrib.admin.models import LogEntry -from django.contrib.auth import REDIRECT_FIELD_NAME, SESSION_KEY +from django.contrib.auth import ( + BACKEND_SESSION_KEY, REDIRECT_FIELD_NAME, SESSION_KEY, +) from django.contrib.auth.forms import ( AuthenticationForm, PasswordChangeForm, SetPasswordForm, ) @@ -331,6 +333,22 @@ class PasswordResetTest(AuthViewsTestCase): self.assertRedirects(response, '/reset/done/', fetch_redirect_response=False) self.assertIn(SESSION_KEY, self.client.session) + @override_settings( + AUTHENTICATION_BACKENDS=[ + 'django.contrib.auth.backends.ModelBackend', + 'django.contrib.auth.backends.AllowAllUsersModelBackend', + ] + ) + def test_confirm_login_post_reset_custom_backend(self): + # This backend is specified in the url(). + backend = 'django.contrib.auth.backends.AllowAllUsersModelBackend' + url, path = self._test_confirm_start() + path = path.replace('/reset/', '/reset/post_reset_login_custom_backend/') + response = self.client.post(path, {'new_password1': 'anewpassword', 'new_password2': 'anewpassword'}) + self.assertRedirects(response, '/reset/done/', fetch_redirect_response=False) + self.assertIn(SESSION_KEY, self.client.session) + self.assertEqual(self.client.session[BACKEND_SESSION_KEY], backend) + def test_confirm_login_post_reset_already_logged_in(self): url, path = self._test_confirm_start() path = path.replace('/reset/', '/reset/post_reset_login/') diff --git a/tests/auth_tests/urls.py b/tests/auth_tests/urls.py index c1bb5aae49..3c85b52564 100644 --- a/tests/auth_tests/urls.py +++ b/tests/auth_tests/urls.py @@ -90,6 +90,13 @@ urlpatterns = auth_urlpatterns + [ views.PasswordResetConfirmView.as_view(success_url=reverse_lazy('password_reset'))), url(r'^reset/post_reset_login/{}/$'.format(uid_token), views.PasswordResetConfirmView.as_view(post_reset_login=True)), + url( + r'^reset/post_reset_login_custom_backend/{}/$'.format(uid_token), + views.PasswordResetConfirmView.as_view( + post_reset_login=True, + post_reset_login_backend='django.contrib.auth.backends.AllowAllUsersModelBackend', + ), + ), url(r'^password_change/custom/$', views.PasswordChangeView.as_view(success_url='/custom/')), url(r'^password_change/custom/named/$',