LEARNER-4423: Adds in PCI compliance checks for alphabetic and numeric characters

This commit is contained in:
Jeff LaJoie
2018-03-09 09:42:50 -05:00
parent f02ba65983
commit b289bb90b5
2 changed files with 50 additions and 0 deletions

View File

@@ -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,

View File

@@ -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)