diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index b84fae8859..08c3510988 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -441,6 +441,15 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): @method_decorator(never_cache) def dispatch(self, *args, **kwargs): assert 'uidb64' in kwargs and 'token' in kwargs + + self.validlink = False + self.user = self.get_user(kwargs['uidb64']) + + if self.user is not None and self.token_generator.check_token(self.user, kwargs['token']): + self.validlink = True + else: + return self.render_to_response(self.get_context_data()) + return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs) def get_user(self, uidb64): @@ -455,7 +464,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): def get_form_kwargs(self): kwargs = super(PasswordResetConfirmView, self).get_form_kwargs() - kwargs['user'] = self.get_user(self.kwargs['uidb64']) + kwargs['user'] = self.user return kwargs def form_valid(self, form): @@ -466,8 +475,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): def get_context_data(self, **kwargs): context = super(PasswordResetConfirmView, self).get_context_data(**kwargs) - user = context['form'].user - if user is not None and self.token_generator.check_token(user, self.kwargs['token']): + if self.validlink: context['validlink'] = True else: context.update({ diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index 77d1ada13b..209f9f698a 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -255,6 +255,23 @@ class PasswordResetTest(AuthViewsTestCase): u = User.objects.get(email='staffmember@example.com') self.assertTrue(not u.check_password("anewpassword")) + def test_confirm_invalid_hash(self): + """A POST with an invalid token is rejected.""" + u = User.objects.get(email='staffmember@example.com') + original_password = u.password + url, path = self._test_confirm_start() + path_parts = path.split('-') + path_parts[-1] = ("0") * 20 + '/' + path = '-'.join(path_parts) + + response = self.client.post(path, { + 'new_password1': 'anewpassword', + 'new_password2': 'anewpassword', + }) + self.assertIs(response.context['validlink'], False) + u.refresh_from_db() + self.assertEqual(original_password, u.password) # password hasn't changed + def test_confirm_complete(self): url, path = self._test_confirm_start() response = self.client.post(path, {'new_password1': 'anewpassword', 'new_password2': 'anewpassword'})