LEARNER-4423: Adds in PCI compliance checks for alphabetic and numeric characters
This commit is contained in:
@@ -160,6 +160,44 @@ class TestPasswordPolicy(TestCase):
|
||||
obj = json.loads(response.content)
|
||||
self.assertTrue(obj['success'])
|
||||
|
||||
@patch.dict("django.conf.settings.PASSWORD_COMPLEXITY", {'NUMERIC': 3})
|
||||
def test_not_enough_numeric_characters(self):
|
||||
self.url_params['password'] = u'thishouldfail½2'
|
||||
response = self.client.post(self.url, self.url_params)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
obj = json.loads(response.content)
|
||||
self.assertEqual(
|
||||
obj['value'],
|
||||
"Password: Must be more complex (must contain 3 or more numbers)",
|
||||
)
|
||||
|
||||
@patch.dict("django.conf.settings.PASSWORD_COMPLEXITY", {'NUMERIC': 3})
|
||||
def test_enough_numeric_characters(self):
|
||||
self.url_params['password'] = u'thisShouldPass½33' # This unicode 1/2 should count as a numeric value here
|
||||
response = self.client.post(self.url, self.url_params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
obj = json.loads(response.content)
|
||||
self.assertTrue(obj['success'])
|
||||
|
||||
@patch.dict("django.conf.settings.PASSWORD_COMPLEXITY", {'ALPHABETIC': 3})
|
||||
def test_not_enough_alphabetic_characters(self):
|
||||
self.url_params['password'] = '123456ab'
|
||||
response = self.client.post(self.url, self.url_params)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
obj = json.loads(response.content)
|
||||
self.assertEqual(
|
||||
obj['value'],
|
||||
"Password: Must be more complex (must contain 3 or more letters)",
|
||||
)
|
||||
|
||||
@patch.dict("django.conf.settings.PASSWORD_COMPLEXITY", {'ALPHABETIC': 3})
|
||||
def test_enough_alphabetic_characters(self):
|
||||
self.url_params['password'] = u'𝒯𝓗Ï𝓼𝒫å𝓼𝓼𝔼𝓼'
|
||||
response = self.client.post(self.url, self.url_params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
obj = json.loads(response.content)
|
||||
self.assertTrue(obj['success'])
|
||||
|
||||
@patch.dict("django.conf.settings.PASSWORD_COMPLEXITY", {
|
||||
'PUNCTUATION': 3,
|
||||
'WORDS': 3,
|
||||
|
||||
@@ -8,6 +8,7 @@ authored by dstufft (https://github.com/dstufft)
|
||||
from __future__ import division
|
||||
|
||||
import string
|
||||
import unicodedata
|
||||
|
||||
from nltk.metrics.distance import edit_distance
|
||||
from django.conf import settings
|
||||
@@ -63,7 +64,9 @@ def validate_password_complexity(value):
|
||||
if complexities is None:
|
||||
return
|
||||
|
||||
# Sets are here intentionally
|
||||
uppercase, lowercase, digits, non_ascii, punctuation = set(), set(), set(), set(), set()
|
||||
alphabetic, numeric = [], []
|
||||
|
||||
for character in value:
|
||||
if character.isupper():
|
||||
@@ -77,6 +80,11 @@ def validate_password_complexity(value):
|
||||
else:
|
||||
non_ascii.add(character)
|
||||
|
||||
if character.isalpha():
|
||||
alphabetic.append(character)
|
||||
if 'N' in unicodedata.category(character): # Check to see if the unicode category contains a 'N'umber
|
||||
numeric.append(character)
|
||||
|
||||
words = set(value.split())
|
||||
|
||||
errors = []
|
||||
@@ -92,6 +100,10 @@ def validate_password_complexity(value):
|
||||
errors.append(_("must contain {0} or more non ascii characters").format(complexities["NON ASCII"]))
|
||||
if len(words) < complexities.get("WORDS", 0):
|
||||
errors.append(_("must contain {0} or more unique words").format(complexities["WORDS"]))
|
||||
if len(numeric) < complexities.get("NUMERIC", 0):
|
||||
errors.append(_("must contain {0} or more numbers").format(complexities["NUMERIC"]))
|
||||
if len(alphabetic) < complexities.get("ALPHABETIC", 0):
|
||||
errors.append(_("must contain {0} or more letters").format(complexities["ALPHABETIC"]))
|
||||
|
||||
if errors:
|
||||
raise ValidationError(message.format(u', '.join(errors)), code=code)
|
||||
|
||||
Reference in New Issue
Block a user