From aeb8c381789ad93866223f8bd07d09ae5e2edd9e Mon Sep 17 00:00:00 2001 From: Mattia Procopio Date: Fri, 16 Mar 2018 02:33:15 +0100 Subject: [PATCH] Fixed #29206 -- Fixed PasswordResetConfirmView crash when the URL contains a non-UUID where one is expected. --- AUTHORS | 1 + django/contrib/auth/views.py | 3 ++- docs/releases/2.0.4.txt | 4 ++++ tests/auth_tests/test_views.py | 9 +++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 2a2ef3ab3d9..1f9a3757190 100644 --- a/AUTHORS +++ b/AUTHORS @@ -549,6 +549,7 @@ answer newbie questions, and generally made Django that much better: Matt Riggott Matt Robenolt Mattia Larentis + Mattia Procopio Mattias Loverot mattycakes@gmail.com Max Burstein diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 270da8b409b..8c5435e7268 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -12,6 +12,7 @@ from django.contrib.auth.forms import ( ) from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site +from django.core.exceptions import ValidationError from django.http import HttpResponseRedirect, QueryDict from django.shortcuts import resolve_url from django.urls import reverse_lazy @@ -285,7 +286,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): # urlsafe_base64_decode() decodes to bytestring uid = urlsafe_base64_decode(uidb64).decode() user = UserModel._default_manager.get(pk=uid) - except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist): + except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist, ValidationError): user = None return user diff --git a/docs/releases/2.0.4.txt b/docs/releases/2.0.4.txt index be538b46fd4..0c4a9aff5ac 100644 --- a/docs/releases/2.0.4.txt +++ b/docs/releases/2.0.4.txt @@ -17,3 +17,7 @@ Bugfixes * Corrected admin's autocomplete widget to add a space after custom classes (:ticket:`29221`). + +* Fixed ``PasswordResetConfirmView`` crash when using a user model with a + ``UUIDField`` primary key and the reset URL contains an encoded primary key + value that decodes to an invalid UUID (:ticket:`29206`). diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index e016cd07ff8..bb5bd7a0870 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -28,6 +28,7 @@ from django.middleware.csrf import CsrfViewMiddleware, get_token from django.test import Client, TestCase, override_settings from django.test.utils import patch_logger from django.urls import NoReverseMatch, reverse, reverse_lazy +from django.utils.http import urlsafe_base64_encode from django.utils.translation import LANGUAGE_SESSION_KEY from .client import PasswordResetConfirmClient @@ -437,6 +438,14 @@ class UUIDUserPasswordResetTest(CustomUserPasswordResetTest): ) return super()._test_confirm_start() + def test_confirm_invalid_uuid(self): + """A uidb64 that decodes to a non-UUID doesn't crash.""" + _, path = self._test_confirm_start() + invalid_uidb64 = urlsafe_base64_encode('INVALID_UUID'.encode()).decode() + first, _uuidb64_, second = path.strip('/').split('/') + response = self.client.get('/' + '/'.join((first, invalid_uidb64, second)) + '/') + self.assertContains(response, 'The password reset link was invalid') + class ChangePasswordTest(AuthViewsTestCase):