1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00

Fixed #35693 -- Made password validators callable.

This commit is contained in:
antoliny0919 2024-12-01 10:33:37 +09:00
parent 86661f2449
commit 92f6a713d3
3 changed files with 66 additions and 0 deletions

View File

@ -104,6 +104,9 @@ class MinimumLengthValidator:
def __init__(self, min_length=8): def __init__(self, min_length=8):
self.min_length = min_length self.min_length = min_length
def __call__(self, *args, **kwargs):
return self.validate(*args, kwargs)
def validate(self, password, user=None): def validate(self, password, user=None):
if len(password) < self.min_length: if len(password) < self.min_length:
raise ValidationError(self.get_error_message(), code="password_too_short") raise ValidationError(self.get_error_message(), code="password_too_short")
@ -175,6 +178,9 @@ class UserAttributeSimilarityValidator:
raise ValueError("max_similarity must be at least 0.1") raise ValueError("max_similarity must be at least 0.1")
self.max_similarity = max_similarity self.max_similarity = max_similarity
def __call__(self, *args, **kwargs):
return self.validate(*args, **kwargs)
def validate(self, password, user=None): def validate(self, password, user=None):
if not user: if not user:
return return
@ -241,6 +247,9 @@ class CommonPasswordValidator:
with open(password_list_path) as f: with open(password_list_path) as f:
self.passwords = {x.strip() for x in f} self.passwords = {x.strip() for x in f}
def __call__(self, *args, **kwargs):
return self.validate(*args, **kwargs)
def validate(self, password, user=None): def validate(self, password, user=None):
if password.lower().strip() in self.passwords: if password.lower().strip() in self.passwords:
raise ValidationError( raise ValidationError(
@ -260,6 +269,9 @@ class NumericPasswordValidator:
Validate that the password is not entirely numeric. Validate that the password is not entirely numeric.
""" """
def __call__(self, *args, **kwargs):
return self.validate(*args, **kwargs)
def validate(self, password, user=None): def validate(self, password, user=None):
if password.isdigit(): if password.isdigit():
raise ValidationError( raise ValidationError(

View File

@ -91,6 +91,10 @@ Minor features
now have a new method ``get_error_message()``, which can be overridden in now have a new method ``get_error_message()``, which can be overridden in
subclasses to customize the error messages. subclasses to customize the error messages.
* The :ref:`password validator classes <included-password-validators>`
now have a new method ``__call__()``, calling the password validator class
object will perform ``validate()`` method.
:mod:`django.contrib.contenttypes` :mod:`django.contrib.contenttypes`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -158,6 +158,19 @@ class MinimumLengthValidatorTest(SimpleTestCase):
with self.assertRaisesMessage(ValidationError, expected_error % 3) as cm: with self.assertRaisesMessage(ValidationError, expected_error % 3) as cm:
CustomMinimumLengthValidator(min_length=3).validate("12") CustomMinimumLengthValidator(min_length=3).validate("12")
def test_callable_validate(self):
expected_error = (
"This password is too short. It must contain at least %d characters."
)
validator = MinimumLengthValidator()
self.assertIsNone(validator("12345678"))
with self.assertRaises(ValidationError) as cm:
validator("1234567")
self.assertEqual(cm.exception.messages, [expected_error % 8])
self.assertEqual(cm.exception.error_list[0].code, "password_too_short")
class UserAttributeSimilarityValidatorTest(TestCase): class UserAttributeSimilarityValidatorTest(TestCase):
def test_validate(self): def test_validate(self):
@ -263,6 +276,24 @@ class UserAttributeSimilarityValidatorTest(TestCase):
with self.assertRaisesMessage(ValidationError, expected_error): with self.assertRaisesMessage(ValidationError, expected_error):
CustomUserAttributeSimilarityValidator().validate("testclient", user=user) CustomUserAttributeSimilarityValidator().validate("testclient", user=user)
def test_callable_validate(self):
user = User.objects.create_user(
username="testclient",
password="password",
email="testclient@example.com",
first_name="Test",
last_name="Client",
)
expected_error = "The password is too similar to the %s."
validator = UserAttributeSimilarityValidator()
self.assertIsNone(validator("testclient"))
with self.assertRaises(ValidationError) as cm:
validator("testclient", user=user)
self.assertEqual(cm.exception.messages, [expected_error % "username"])
self.assertEqual(cm.exception.error_list[0].code, "password_too_similar")
class CommonPasswordValidatorTest(SimpleTestCase): class CommonPasswordValidatorTest(SimpleTestCase):
def test_validate(self): def test_validate(self):
@ -307,6 +338,15 @@ class CommonPasswordValidatorTest(SimpleTestCase):
with self.assertRaisesMessage(ValidationError, expected_error): with self.assertRaisesMessage(ValidationError, expected_error):
CustomCommonPasswordValidator().validate("godzilla") CustomCommonPasswordValidator().validate("godzilla")
def test_callable_validate(self):
expected_error = "This password is too common."
validator = CommonPasswordValidator()
self.assertIsNone(validator("a-safe-password"))
with self.assertRaises(ValidationError) as cm:
validator("godzilla")
self.assertEqual(cm.exception.messages, [expected_error])
class NumericPasswordValidatorTest(SimpleTestCase): class NumericPasswordValidatorTest(SimpleTestCase):
def test_validate(self): def test_validate(self):
@ -334,6 +374,16 @@ class NumericPasswordValidatorTest(SimpleTestCase):
with self.assertRaisesMessage(ValidationError, expected_error): with self.assertRaisesMessage(ValidationError, expected_error):
CustomNumericPasswordValidator().validate("42424242") CustomNumericPasswordValidator().validate("42424242")
def test_callable_validate(self):
expected_error = "This password is entirely numeric."
validator = NumericPasswordValidator()
self.assertIsNone(validator("a-safe-password"))
with self.assertRaises(ValidationError) as cm:
validator("42424242")
self.assertEqual(cm.exception.messages, [expected_error])
self.assertEqual(cm.exception.error_list[0].code, "password_entirely_numeric")
class UsernameValidatorsTests(SimpleTestCase): class UsernameValidatorsTests(SimpleTestCase):
def test_unicode_validator(self): def test_unicode_validator(self):