Fixed #27467 -- Made UserAttributeSimilarityValidator max_similarity=0/1 work as documented.

Thanks goblinJoel for the report and feedback.
This commit is contained in:
Tim Graham 2016-11-10 06:30:38 -05:00
parent 45e01df373
commit 0d9ff873d9
3 changed files with 21 additions and 7 deletions

View File

@ -147,7 +147,7 @@ class UserAttributeSimilarityValidator(object):
continue
value_parts = re.split(r'\W+', value) + [value]
for value_part in value_parts:
if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() > self.max_similarity:
if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() >= self.max_similarity:
try:
verbose_name = force_text(user._meta.get_field(attribute_name).verbose_name)
except FieldDoesNotExist:

View File

@ -545,11 +545,10 @@ Django includes four validators:
is used: ``'username', 'first_name', 'last_name', 'email'``.
Attributes that don't exist are ignored.
The maximum similarity the password can have, before it is rejected, can
be set with the ``max_similarity`` parameter, on a scale of 0 to 1.
A setting of 0 will cause all passwords to be rejected, whereas a setting
of 1 will cause it to only reject passwords that are identical to an
attribute's value.
The minimum similarity of a rejected password can be set on a scale of 0 to
1 with the ``max_similarity`` parameter. A setting of 0 rejects all
passwords, whereas a setting of 1 rejects only passwords that are identical
to an attribute's value.
.. class:: CommonPasswordValidator(password_list_path=DEFAULT_PASSWORD_LIST_PATH)

View File

@ -124,7 +124,22 @@ class UserAttributeSimilarityValidatorTest(TestCase):
max_similarity=0.3,
).validate('testclient', user=user)
self.assertEqual(cm.exception.messages, [expected_error % "first name"])
# max_similarity=1 doesn't allow passwords that are identical to the
# attribute's value.
with self.assertRaises(ValidationError) as cm:
UserAttributeSimilarityValidator(
user_attributes=['first_name'],
max_similarity=1,
).validate(user.first_name, user=user)
self.assertEqual(cm.exception.messages, [expected_error % "first name"])
# max_similarity=0 rejects all passwords.
with self.assertRaises(ValidationError) as cm:
UserAttributeSimilarityValidator(
user_attributes=['first_name'],
max_similarity=0,
).validate('XXX', user=user)
self.assertEqual(cm.exception.messages, [expected_error % "first name"])
# Passes validation.
self.assertIsNone(
UserAttributeSimilarityValidator(user_attributes=['first_name']).validate('testclient', user=user)
)