diff --git a/django/contrib/auth/password_validation.py b/django/contrib/auth/password_validation.py index e5ab22b614..5fdae05a84 100644 --- a/django/contrib/auth/password_validation.py +++ b/django/contrib/auth/password_validation.py @@ -6,7 +6,9 @@ import re from difflib import SequenceMatcher from django.conf import settings -from django.core.exceptions import ImproperlyConfigured, ValidationError +from django.core.exceptions import ( + FieldDoesNotExist, ImproperlyConfigured, ValidationError, +) from django.utils import lru_cache from django.utils._os import upath from django.utils.encoding import force_text @@ -144,7 +146,10 @@ class UserAttributeSimilarityValidator(object): value_parts = re.split('\W+', value) + [value] for value_part in value_parts: if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() > self.max_similarity: - verbose_name = force_text(user._meta.get_field(attribute_name).verbose_name) + try: + verbose_name = force_text(user._meta.get_field(attribute_name).verbose_name) + except FieldDoesNotExist: + verbose_name = attribute_name raise ValidationError( _("The password is too similar to the %(verbose_name)s."), code='password_too_similar', diff --git a/tests/auth_tests/test_validators.py b/tests/auth_tests/test_validators.py index 49a3c5395b..97cf396b1a 100644 --- a/tests/auth_tests/test_validators.py +++ b/tests/auth_tests/test_validators.py @@ -13,7 +13,9 @@ from django.contrib.auth.password_validation import ( validate_password, ) from django.core.exceptions import ValidationError +from django.db import models from django.test import TestCase, override_settings +from django.test.utils import isolate_apps from django.utils._os import upath @@ -127,6 +129,19 @@ class UserAttributeSimilarityValidatorTest(TestCase): UserAttributeSimilarityValidator(user_attributes=['first_name']).validate('testclient', user=user) ) + @isolate_apps('auth_tests') + def test_validate_property(self): + class TestUser(models.Model): + pass + + @property + def username(self): + return 'foobar' + + with self.assertRaises(ValidationError) as cm: + UserAttributeSimilarityValidator().validate('foobar', user=TestUser()), + self.assertEqual(cm.exception.messages, ['The password is too similar to the username.']) + def test_help_text(self): self.assertEqual( UserAttributeSimilarityValidator().get_help_text(),