Fixed #27891 -- Added PasswordResetConfirmView.post_reset_login_backend.

This commit is contained in:
Camilo Nova 2017-03-07 19:52:26 -05:00 committed by Tim Graham
parent 72ff9d53e6
commit 5db465d5a6
5 changed files with 35 additions and 2 deletions

View File

@ -409,6 +409,7 @@ class PasswordResetDoneView(PasswordContextMixin, TemplateView):
class PasswordResetConfirmView(PasswordContextMixin, FormView): class PasswordResetConfirmView(PasswordContextMixin, FormView):
form_class = SetPasswordForm form_class = SetPasswordForm
post_reset_login = False post_reset_login = False
post_reset_login_backend = None
success_url = reverse_lazy('password_reset_complete') success_url = reverse_lazy('password_reset_complete')
template_name = 'registration/password_reset_confirm.html' template_name = 'registration/password_reset_confirm.html'
title = _('Enter new password') title = _('Enter new password')
@ -461,7 +462,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView):
user = form.save() user = form.save()
del self.request.session[INTERNAL_RESET_SESSION_TOKEN] del self.request.session[INTERNAL_RESET_SESSION_TOKEN]
if self.post_reset_login: if self.post_reset_login:
auth_login(self.request, user) auth_login(self.request, user, self.post_reset_login_backend)
return super().form_valid(form) return super().form_valid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

View File

@ -118,6 +118,8 @@ Minor features
* The new ``post_reset_login`` attribute for * The new ``post_reset_login`` attribute for
:class:`~django.contrib.auth.views.PasswordResetConfirmView` allows :class:`~django.contrib.auth.views.PasswordResetConfirmView` allows
automatically logging in a user after a successful password reset. 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 * 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 Referer header (for example, if the reset page includes a reference to CSS or

View File

@ -1413,6 +1413,11 @@ implementation details see :ref:`using-the-views`.
automatically authenticated after a successful password reset. Defaults automatically authenticated after a successful password reset. Defaults
to ``False``. 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 * ``form_class``: Form that will be used to set the password. Defaults to
:class:`~django.contrib.auth.forms.SetPasswordForm`. :class:`~django.contrib.auth.forms.SetPasswordForm`.

View File

@ -8,7 +8,9 @@ from urllib.parse import ParseResult, quote, urlparse
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib.admin.models import LogEntry 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 ( from django.contrib.auth.forms import (
AuthenticationForm, PasswordChangeForm, SetPasswordForm, AuthenticationForm, PasswordChangeForm, SetPasswordForm,
) )
@ -327,6 +329,22 @@ class PasswordResetTest(AuthViewsTestCase):
self.assertRedirects(response, '/reset/done/', fetch_redirect_response=False) self.assertRedirects(response, '/reset/done/', fetch_redirect_response=False)
self.assertIn(SESSION_KEY, self.client.session) 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): def test_confirm_login_post_reset_already_logged_in(self):
url, path = self._test_confirm_start() url, path = self._test_confirm_start()
path = path.replace('/reset/', '/reset/post_reset_login/') path = path.replace('/reset/', '/reset/post_reset_login/')

View File

@ -90,6 +90,13 @@ urlpatterns = auth_urlpatterns + [
views.PasswordResetConfirmView.as_view(success_url=reverse_lazy('password_reset'))), views.PasswordResetConfirmView.as_view(success_url=reverse_lazy('password_reset'))),
url(r'^reset/post_reset_login/{}/$'.format(uid_token), url(r'^reset/post_reset_login/{}/$'.format(uid_token),
views.PasswordResetConfirmView.as_view(post_reset_login=True)), 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/$', url(r'^password_change/custom/$',
views.PasswordChangeView.as_view(success_url='/custom/')), views.PasswordChangeView.as_view(success_url='/custom/')),
url(r'^password_change/custom/named/$', url(r'^password_change/custom/named/$',