UPD: auth: added a custom wordlist password validator
This commit is contained in:
@@ -2,25 +2,25 @@
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
|
||||||
from ..validators import PasswordScoreValidator, CharacterClassPasswordValidator
|
from ..validators import PasswordScoreValidator, CustomWordlistPasswordValidator, CharacterClassPasswordValidator
|
||||||
|
|
||||||
|
|
||||||
class PasswordScoreValidatorTestCase(SimpleTestCase):
|
class PasswordScoreValidatorTestCase(SimpleTestCase):
|
||||||
def test_too_little_score(self):
|
def test_too_little_score(self):
|
||||||
passwords = [
|
passwords = [
|
||||||
'', # score = 0
|
u'', # score = 0
|
||||||
'abcdefghijklmnopq', # score = 17
|
u'abcdefghijklmnopq', # score = 17
|
||||||
'Abcdefghijklmnop', # score = 16 + 1
|
u'Abcdefghijklmnop', # score = 16 + 1
|
||||||
'ABcdefghijklmno', # score = 15 + 2
|
u'ABcdefghijklmno', # score = 15 + 2
|
||||||
'AB1defghijklmn', # score = 14 + 2 + 1
|
u'AB1defghijklmn', # score = 14 + 2 + 1
|
||||||
'AB12efghijklm', # score = 13 + 2 + 2
|
u'AB12efghijklm', # score = 13 + 2 + 2
|
||||||
'AB12+fghijkl', # score = 12 + 2 + 2 + 1
|
u'AB12+fghijkl', # score = 12 + 2 + 2 + 1
|
||||||
'AB12+-ghijk', # score = 11 + 2 + 2 + 2
|
u'AB12+-ghijk', # score = 11 + 2 + 2 + 2
|
||||||
'AB12+-*hij', # score = 10 + 2 + 2 + 3
|
u'AB12+-*hij', # score = 10 + 2 + 2 + 3
|
||||||
'AB12+-*hi/', # score = 10 + 2 + 2 + 3
|
u'AB12+-*hi/', # score = 10 + 2 + 2 + 3
|
||||||
'AB12+-*hi3', # score = 10 + 2 + 2 + 3
|
u'AB12+-*hi3', # score = 10 + 2 + 2 + 3
|
||||||
'AB12+-*hiC', # score = 10 + 2 + 2 + 3
|
u'AB12+-*hiC', # score = 10 + 2 + 2 + 3
|
||||||
'abcbbbgbbjklmnopqr', # score = 17
|
u'abcbbbgbbjklmnopqr', # score = 17
|
||||||
]
|
]
|
||||||
|
|
||||||
validator = PasswordScoreValidator(min_classes=0)
|
validator = PasswordScoreValidator(min_classes=0)
|
||||||
@@ -31,19 +31,19 @@ class PasswordScoreValidatorTestCase(SimpleTestCase):
|
|||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertEqual('too_little_score', e.code)
|
self.assertEqual('too_little_score', e.code)
|
||||||
else:
|
else:
|
||||||
self.fail('%s: no validation error was raised' % password)
|
self.fail(u'%s: no validation error was raised' % password)
|
||||||
|
|
||||||
def test_too_few_classes(self):
|
def test_too_few_classes(self):
|
||||||
passwords = [
|
passwords = [
|
||||||
'', # classes = 0
|
u'', # classes = 0
|
||||||
'abcdefgh', # classes = 1
|
u'abcdefgh', # classes = 1
|
||||||
'abcdef+-', # classes = 2
|
u'abcdef+-', # classes = 2
|
||||||
'abcd12gh', # classes = 2
|
u'abcd12gh', # classes = 2
|
||||||
'abcd12-+', # classes = 3
|
u'abcd12-+', # classes = 3
|
||||||
'abCDefgh', # classes = 2
|
u'abCDefgh', # classes = 2
|
||||||
'abCDef+-', # classes = 3
|
u'abCDef+-', # classes = 3
|
||||||
'abCD12gh', # classes = 3
|
u'abCD12gh', # classes = 3
|
||||||
'ABCD12-+', # classes = 3
|
u'ABCD12-+', # classes = 3
|
||||||
]
|
]
|
||||||
|
|
||||||
validator = PasswordScoreValidator(min_score=0, min_classes=4)
|
validator = PasswordScoreValidator(min_score=0, min_classes=4)
|
||||||
@@ -54,20 +54,20 @@ class PasswordScoreValidatorTestCase(SimpleTestCase):
|
|||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertEqual('too_few_classes', e.code)
|
self.assertEqual('too_few_classes', e.code)
|
||||||
else:
|
else:
|
||||||
self.fail('%s: no validation error was raised' % password)
|
self.fail(u'%s: no validation error was raised' % password)
|
||||||
|
|
||||||
def test_valid(self):
|
def test_valid(self):
|
||||||
passwords = [
|
passwords = [
|
||||||
'Abcdefghijklmnopq',
|
u'Abcdefghijklmnopq',
|
||||||
'ABcdefghijklmnop',
|
u'ABcdefghijklmnop',
|
||||||
'AB1defghijklmno',
|
u'AB1defghijklmno',
|
||||||
'AB12efghijklmn',
|
u'AB12efghijklmn',
|
||||||
'AB12+fghijklm',
|
u'AB12+fghijklm',
|
||||||
'AB12+-ghijkl',
|
u'AB12+-ghijkl',
|
||||||
'AB12+-*hijk',
|
u'AB12+-*hijk',
|
||||||
'ab1defghijklmnopq',
|
u'ab1defghijklmnopq',
|
||||||
'abcd+fghijklmnopq',
|
u'abcd+fghijklmnopq',
|
||||||
'AB1CDEFGHIJKLMNOPQ',
|
u'AB1CDEFGHIJKLMNOPQ',
|
||||||
]
|
]
|
||||||
|
|
||||||
validator = PasswordScoreValidator()
|
validator = PasswordScoreValidator()
|
||||||
@@ -79,6 +79,59 @@ class PasswordScoreValidatorTestCase(SimpleTestCase):
|
|||||||
self.fail(e)
|
self.fail(e)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomWordlistPasswordValidatorTestCase(SimpleTestCase):
|
||||||
|
def test_invalid(self):
|
||||||
|
invalid_passwords = [
|
||||||
|
(u'passwort', [
|
||||||
|
u'The password must not contain the word \'passwort\'',
|
||||||
|
]),
|
||||||
|
(u'abcdDaVefgh', [
|
||||||
|
u'The password must not contain the word \'dav\'',
|
||||||
|
]),
|
||||||
|
(u'abcdsektIonefgh', [
|
||||||
|
u'The password must not contain the word \'sektion\'',
|
||||||
|
]),
|
||||||
|
(u'alpen12verein34KArlsruhE berge', [
|
||||||
|
u'The password must not contain the word \'karlsruhe\'',
|
||||||
|
u'The password must not contain the word \'berge\'',
|
||||||
|
]),
|
||||||
|
(u'heinzel@alpenverein-karlsruhe.de', [
|
||||||
|
u'The password must not contain the word \'heinzel\'',
|
||||||
|
u'The password must not contain the word \'alpenverein\'',
|
||||||
|
u'The password must not contain the word \'karlsruhe\'',
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
|
validator = CustomWordlistPasswordValidator()
|
||||||
|
|
||||||
|
for password, expected_errors in invalid_passwords:
|
||||||
|
try:
|
||||||
|
validator.validate(password)
|
||||||
|
except ValidationError as e:
|
||||||
|
errors = e.messages
|
||||||
|
for expected_error in expected_errors:
|
||||||
|
self.assertIn(expected_error, errors)
|
||||||
|
for error in errors:
|
||||||
|
self.assertIn(error, expected_errors)
|
||||||
|
else:
|
||||||
|
self.fail(u'%s: no validation error was raised' % password)
|
||||||
|
|
||||||
|
def test_valid(self):
|
||||||
|
passwords = [
|
||||||
|
u'',
|
||||||
|
u'password',
|
||||||
|
u'münchen',
|
||||||
|
]
|
||||||
|
|
||||||
|
validator = CustomWordlistPasswordValidator()
|
||||||
|
|
||||||
|
for password in passwords:
|
||||||
|
try:
|
||||||
|
validator.validate(password)
|
||||||
|
except ValidationError as e:
|
||||||
|
self.fail(e)
|
||||||
|
|
||||||
|
|
||||||
class CharacterClassPasswordValidatorTestCase(SimpleTestCase):
|
class CharacterClassPasswordValidatorTestCase(SimpleTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CharacterClassPasswordValidatorTestCase, self).setUp()
|
super(CharacterClassPasswordValidatorTestCase, self).setUp()
|
||||||
@@ -86,72 +139,75 @@ class CharacterClassPasswordValidatorTestCase(SimpleTestCase):
|
|||||||
|
|
||||||
def test_invalid(self):
|
def test_invalid(self):
|
||||||
invalid_passwords = [
|
invalid_passwords = [
|
||||||
('', [
|
(u'', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
('A+-', [
|
(u'A+-', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
]),
|
]),
|
||||||
('1234567890*', [
|
(u'1234567890*', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
('34*/()', [
|
(u'34*/()', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
]),
|
]),
|
||||||
('AA', [
|
(u'AA', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
('CD0.,', [
|
(u'CD0.,', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
]),
|
]),
|
||||||
('EF56', [
|
(u'EF56', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
('8GH?!8', [
|
(u'8GH?!8', [
|
||||||
u'The password must contain at least 2 characters from a-z',
|
u'The password must contain at least 2 characters from a-z',
|
||||||
]),
|
]),
|
||||||
('bbX', [
|
(u'bbX', [
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
('$cd%', [
|
(u'$cd%', [
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
]),
|
]),
|
||||||
('ef90', [
|
(u'ef90', [
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
('1g=h3~', [
|
(u'1g=h3~', [
|
||||||
u'The password must contain at least 2 characters from A-Z',
|
u'The password must contain at least 2 characters from A-Z',
|
||||||
]),
|
]),
|
||||||
('Gi&jH', [
|
(u'Gi&jH', [
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
('IkK:i;', [
|
(u'IkK:i;', [
|
||||||
u'The password must contain at least 2 digits from 0-9',
|
u'The password must contain at least 2 digits from 0-9',
|
||||||
]),
|
]),
|
||||||
('mKn4L8', [
|
(u'mKn4L8', [
|
||||||
u'The password must contain at least 2 non alpha numeric characters',
|
u'The password must contain at least 2 non alpha numeric characters',
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
validator = self.validator
|
||||||
|
|
||||||
for password, expected_errors in invalid_passwords:
|
for password, expected_errors in invalid_passwords:
|
||||||
try:
|
try:
|
||||||
self.validator.validate(password)
|
validator.validate(password)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
errors = e.messages
|
errors = e.messages
|
||||||
for expected_error in expected_errors:
|
for expected_error in expected_errors:
|
||||||
@@ -159,12 +215,13 @@ class CharacterClassPasswordValidatorTestCase(SimpleTestCase):
|
|||||||
for error in errors:
|
for error in errors:
|
||||||
self.assertIn(error, expected_errors)
|
self.assertIn(error, expected_errors)
|
||||||
else:
|
else:
|
||||||
self.fail('%s: no validation error was raised' % password)
|
self.fail(u'%s: no validation error was raised' % password)
|
||||||
|
|
||||||
def test_valid(self):
|
def test_valid(self):
|
||||||
valid_passwords = ['abCD12+-']
|
valid_passwords = [u'abCD12+-']
|
||||||
|
validator = self.validator
|
||||||
for password in valid_passwords:
|
for password in valid_passwords:
|
||||||
try:
|
try:
|
||||||
self.validator.validate(password)
|
validator.validate(password)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.fail(e)
|
self.fail(e)
|
||||||
|
|||||||
@@ -4,69 +4,6 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class CharacterClassPasswordValidator(object):
|
|
||||||
def _is_enough_lower(self, password):
|
|
||||||
lower = re.sub(r'[^a-z]', '', password)
|
|
||||||
if len(lower) < self.minimum_lower:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _is_enough_upper(self, password):
|
|
||||||
upper = re.sub(r'[^A-Z]', '', password)
|
|
||||||
if len(upper) < self.minimum_upper:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _is_enough_digits(self, password):
|
|
||||||
digits = re.sub(r'[^0-9]', '', password)
|
|
||||||
if len(digits) < self.minimum_digits:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _is_enough_others(self, password):
|
|
||||||
others = re.sub(r'[a-zA-Z0-9]', '', password)
|
|
||||||
if len(others) < self.minimum_others:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __init__(self, minimum_lower=2, minimum_upper=2, minimum_digits=2, minimum_others=2):
|
|
||||||
self.minimum_lower = minimum_lower
|
|
||||||
self.minimum_upper = minimum_upper
|
|
||||||
self.minimum_digits = minimum_digits
|
|
||||||
self.minimum_others = minimum_others
|
|
||||||
|
|
||||||
def validate(self, password, user=None):
|
|
||||||
errors = []
|
|
||||||
if not self._is_enough_lower(password):
|
|
||||||
errors.append(ValidationError(_(u'The password must contain at least %(min_lower)d characters from a-z'),
|
|
||||||
code='too_few_lower_characters',
|
|
||||||
params={'min_lower': self.minimum_lower}))
|
|
||||||
if not self._is_enough_upper(password):
|
|
||||||
errors.append(ValidationError(_(u'The password must contain at least %(min_upper)d characters from A-Z'),
|
|
||||||
code='too_few_upper_characters',
|
|
||||||
params={'min_upper': self.minimum_upper}))
|
|
||||||
if not self._is_enough_digits(password):
|
|
||||||
errors.append(ValidationError(_(u'The password must contain at least %(min_digits)d digits from 0-9'),
|
|
||||||
code='too_few_digits',
|
|
||||||
params={'min_digits': self.minimum_digits}))
|
|
||||||
if not self._is_enough_others(password):
|
|
||||||
errors.append(ValidationError(_(u'The password must contain at least %(min_others)d'
|
|
||||||
u' non alpha numeric characters'),
|
|
||||||
code='too_few_other_characters',
|
|
||||||
params={'min_others': self.minimum_others}))
|
|
||||||
if errors:
|
|
||||||
raise ValidationError(errors)
|
|
||||||
|
|
||||||
def get_help_text(self):
|
|
||||||
text = u'%s %s %s %s' % (
|
|
||||||
_('The password must contain at least %d characters from a-z.') % self.minimum_lower,
|
|
||||||
_('The password must contain at least %d characters from A-Z.') % self.minimum_upper,
|
|
||||||
_('The password must contain at least %d digits from 0-9.') % self.minimum_digits,
|
|
||||||
_('The password must contain at least %d non alpha numeric characters.') % self.minimum_others,
|
|
||||||
)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordScoreValidator(object):
|
class PasswordScoreValidator(object):
|
||||||
def _get_score(self, password, user=None):
|
def _get_score(self, password, user=None):
|
||||||
score = 0
|
score = 0
|
||||||
@@ -159,3 +96,105 @@ class PasswordScoreValidator(object):
|
|||||||
text += _(u'Also the password must contain characters from %d different character classes'
|
text += _(u'Also the password must contain characters from %d different character classes'
|
||||||
u' (i.e. lower, upper, digits, others).') % self.min_classes
|
u' (i.e. lower, upper, digits, others).') % self.min_classes
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
class CustomWordlistPasswordValidator(object):
|
||||||
|
context = 'the Sektion Karlsruhe'
|
||||||
|
words = (
|
||||||
|
u'dav',
|
||||||
|
u'berge',
|
||||||
|
u'sektion',
|
||||||
|
u'karlsruhe',
|
||||||
|
u'alpenverein',
|
||||||
|
u'heinzel',
|
||||||
|
u'passwort',
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, password, user=None):
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
lower_pw = password.lower()
|
||||||
|
for word in self.words:
|
||||||
|
if word in lower_pw:
|
||||||
|
error = ValidationError(_(u'The password must not contain the word \'%(word)s\''),
|
||||||
|
code='forbidden_word',
|
||||||
|
params={'word': word})
|
||||||
|
errors.append(error)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
def get_help_text(self):
|
||||||
|
text = _(u'The password must not contain some specific words,'
|
||||||
|
u' that are common in context with %(context)s.') % {'context': self.context}
|
||||||
|
text += u'\n'
|
||||||
|
text += _(u'All words are matched case insensitive.')
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
class CharacterClassPasswordValidator(object):
|
||||||
|
def _is_enough_lower(self, password):
|
||||||
|
lower = re.sub(r'[^a-z]', '', password)
|
||||||
|
if len(lower) < self.minimum_lower:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _is_enough_upper(self, password):
|
||||||
|
upper = re.sub(r'[^A-Z]', '', password)
|
||||||
|
if len(upper) < self.minimum_upper:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _is_enough_digits(self, password):
|
||||||
|
digits = re.sub(r'[^0-9]', '', password)
|
||||||
|
if len(digits) < self.minimum_digits:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _is_enough_others(self, password):
|
||||||
|
others = re.sub(r'[a-zA-Z0-9]', '', password)
|
||||||
|
if len(others) < self.minimum_others:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __init__(self, minimum_lower=2, minimum_upper=2, minimum_digits=2, minimum_others=2):
|
||||||
|
self.minimum_lower = minimum_lower
|
||||||
|
self.minimum_upper = minimum_upper
|
||||||
|
self.minimum_digits = minimum_digits
|
||||||
|
self.minimum_others = minimum_others
|
||||||
|
|
||||||
|
def validate(self, password, user=None):
|
||||||
|
errors = []
|
||||||
|
if not self._is_enough_lower(password):
|
||||||
|
error = ValidationError(_(u'The password must contain at least %(min_lower)d characters from a-z'),
|
||||||
|
code='too_few_lower_characters',
|
||||||
|
params={'min_lower': self.minimum_lower})
|
||||||
|
errors.append(error)
|
||||||
|
if not self._is_enough_upper(password):
|
||||||
|
error = ValidationError(_(u'The password must contain at least %(min_upper)d characters from A-Z'),
|
||||||
|
code='too_few_upper_characters',
|
||||||
|
params={'min_upper': self.minimum_upper})
|
||||||
|
errors.append(error)
|
||||||
|
if not self._is_enough_digits(password):
|
||||||
|
error = ValidationError(_(u'The password must contain at least %(min_digits)d digits from 0-9'),
|
||||||
|
code='too_few_digits',
|
||||||
|
params={'min_digits': self.minimum_digits})
|
||||||
|
errors.append(error)
|
||||||
|
if not self._is_enough_others(password):
|
||||||
|
error = ValidationError(_(u'The password must contain at least %(min_others)d'
|
||||||
|
u' non alpha numeric characters'),
|
||||||
|
code='too_few_other_characters',
|
||||||
|
params={'min_others': self.minimum_others})
|
||||||
|
errors.append(error)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
def get_help_text(self):
|
||||||
|
text = u'%s %s %s %s' % (
|
||||||
|
_('The password must contain at least %d characters from a-z.') % self.minimum_lower,
|
||||||
|
_('The password must contain at least %d characters from A-Z.') % self.minimum_upper,
|
||||||
|
_('The password must contain at least %d digits from 0-9.') % self.minimum_digits,
|
||||||
|
_('The password must contain at least %d non alpha numeric characters.') % self.minimum_others,
|
||||||
|
)
|
||||||
|
return text
|
||||||
|
|||||||
Reference in New Issue
Block a user