diff options
| author | Fabian Braun <fsbraun@gmx.de> | 2024-05-28 08:15:12 +0200 |
|---|---|---|
| committer | Natalia <124304+nessita@users.noreply.github.com> | 2024-05-30 16:31:52 -0300 |
| commit | 9996bb1eadc108d228a20d928bccae84afd338d6 (patch) | |
| tree | 830c6eca11945c20dc8c6f8e704975133a68af4a | |
| parent | f0d592ed34a723998f83fa53ee4578e1c7dfbfc5 (diff) | |
[5.1.x] Fixed #35477 -- Corrected 'required' errors in auth password set/change forms.
The auth forms using SetPasswordMixin were incorrectly including the
'This field is required.' error when additional validations (e.g.,
overriding `clean_password1`) were performed and failed.
This fix ensures accurate error reporting for password fields.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Backport of 339977d4441fd353e20950b98bad3d42afb1f126 from main.
| -rw-r--r-- | django/contrib/auth/forms.py | 4 | ||||
| -rw-r--r-- | tests/auth_tests/test_forms.py | 69 |
2 files changed, 71 insertions, 2 deletions
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index ab46caa12e..31e96ff91c 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -154,14 +154,14 @@ class SetPasswordMixin: if not usable_password: return self.cleaned_data - if not password1: + if not password1 and password1_field_name not in self.errors: error = ValidationError( self.fields[password1_field_name].error_messages["required"], code="required", ) self.add_error(password1_field_name, error) - if not password2: + if not password2 and password2_field_name not in self.errors: error = ValidationError( self.fields[password2_field_name].error_messages["required"], code="required", diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py index b44f1edb24..3dd9324304 100644 --- a/tests/auth_tests/test_forms.py +++ b/tests/auth_tests/test_forms.py @@ -60,6 +60,21 @@ class TestDataMixin: ) +class ExtraValidationFormMixin: + def __init__(self, *args, failing_fields=None, **kwargs): + super().__init__(*args, **kwargs) + self.failing_fields = failing_fields or {} + + def failing_helper(self, field_name): + if field_name in self.failing_fields: + errors = [ + ValidationError(error, code="invalid") + for error in self.failing_fields[field_name] + ] + raise ValidationError(errors) + return self.cleaned_data[field_name] + + class BaseUserCreationFormTest(TestDataMixin, TestCase): def test_user_already_exists(self): data = { @@ -324,6 +339,22 @@ class BaseUserCreationFormTest(TestDataMixin, TestCase): "</li></ul>", ) + def test_password_extra_validations(self): + class ExtraValidationForm(ExtraValidationFormMixin, BaseUserCreationForm): + def clean_password1(self): + return self.failing_helper("password1") + + def clean_password2(self): + return self.failing_helper("password2") + + data = {"username": "extra", "password1": "abc", "password2": "abc"} + for fields in (["password1"], ["password2"], ["password1", "password2"]): + with self.subTest(fields=fields): + errors = {field: [f"Extra validation for {field}."] for field in fields} + form = ExtraValidationForm(data, failing_fields=errors) + self.assertIs(form.is_valid(), False) + self.assertDictEqual(form.errors, errors) + @override_settings( AUTH_PASSWORD_VALIDATORS=[ { @@ -865,6 +896,27 @@ class SetPasswordFormTest(TestDataMixin, TestCase): form.fields[field_name].widget.attrs["autocomplete"], autocomplete ) + def test_password_extra_validations(self): + class ExtraValidationForm(ExtraValidationFormMixin, SetPasswordForm): + def clean_new_password1(self): + return self.failing_helper("new_password1") + + def clean_new_password2(self): + return self.failing_helper("new_password2") + + user = User.objects.get(username="testclient") + data = {"new_password1": "abc", "new_password2": "abc"} + for fields in ( + ["new_password1"], + ["new_password2"], + ["new_password1", "new_password2"], + ): + with self.subTest(fields=fields): + errors = {field: [f"Extra validation for {field}."] for field in fields} + form = ExtraValidationForm(user, data, failing_fields=errors) + self.assertIs(form.is_valid(), False) + self.assertDictEqual(form.errors, errors) + class PasswordChangeFormTest(TestDataMixin, TestCase): def test_incorrect_password(self): @@ -1456,6 +1508,23 @@ class AdminPasswordChangeFormTest(TestDataMixin, TestCase): self.assertEqual(form.cleaned_data["password2"], data["password2"]) self.assertEqual(form.changed_data, ["password"]) + def test_password_extra_validations(self): + class ExtraValidationForm(ExtraValidationFormMixin, AdminPasswordChangeForm): + def clean_password1(self): + return self.failing_helper("password1") + + def clean_password2(self): + return self.failing_helper("password2") + + user = User.objects.get(username="testclient") + data = {"username": "extra", "password1": "abc", "password2": "abc"} + for fields in (["password1"], ["password2"], ["password1", "password2"]): + with self.subTest(fields=fields): + errors = {field: [f"Extra validation for {field}."] for field in fields} + form = ExtraValidationForm(user, data, failing_fields=errors) + self.assertIs(form.is_valid(), False) + self.assertDictEqual(form.errors, errors) + def test_non_matching_passwords(self): user = User.objects.get(username="testclient") data = {"password1": "password1", "password2": "password2"} |
