misc: validate password strength (#63830)
This commit is contained in:
parent
1a9537f2e3
commit
8e15762cd5
1
setup.py
1
setup.py
|
@ -182,6 +182,7 @@ setup(
|
|||
'chardet',
|
||||
'attrs>17',
|
||||
'atomicwrites',
|
||||
'zxcvbn',
|
||||
],
|
||||
zip_safe=False,
|
||||
classifiers=[
|
||||
|
|
|
@ -202,6 +202,9 @@ default_settings = dict(
|
|||
A2_PASSWORD_POLICY_SHOW_LAST_CHAR=Setting(
|
||||
default=False, definition='Show last character in password fields'
|
||||
),
|
||||
A2_PASSWORD_POLICY_MIN_STRENGTH=Setting(
|
||||
default=None, definition='Minimun password strength on a 0-4 scale'
|
||||
),
|
||||
A2_SUGGESTED_EMAIL_DOMAINS=Setting(
|
||||
default=[
|
||||
'gmail.com',
|
||||
|
|
|
@ -23,6 +23,7 @@ from django.core.exceptions import ValidationError
|
|||
from django.utils.functional import lazy
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.translation import ugettext as _
|
||||
from zxcvbn import zxcvbn
|
||||
|
||||
from . import app_settings
|
||||
|
||||
|
@ -90,7 +91,12 @@ class DefaultPasswordChecker(PasswordChecker):
|
|||
def regexp_label(self):
|
||||
return app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG
|
||||
|
||||
@property
|
||||
def min_strength(self):
|
||||
return app_settings.A2_PASSWORD_POLICY_MIN_STRENGTH
|
||||
|
||||
def __call__(self, password, **kwargs):
|
||||
|
||||
if self.min_length:
|
||||
yield self.Check(
|
||||
result=len(password) >= self.min_length, label=_('%s characters') % self.min_length
|
||||
|
@ -108,6 +114,13 @@ class DefaultPasswordChecker(PasswordChecker):
|
|||
if self.regexp and self.regexp_label:
|
||||
yield self.Check(result=bool(re.match(self.regexp, password)), label=self.regexp_label)
|
||||
|
||||
if self.min_strength:
|
||||
score = 0
|
||||
if password:
|
||||
score = zxcvbn(password)['score']
|
||||
|
||||
yield self.Check(result=score > self.min_strength, label=_('Secure password'))
|
||||
|
||||
|
||||
def get_password_checker(*args, **kwargs):
|
||||
return import_string(app_settings.A2_PASSWORD_POLICY_CLASS)(*args, **kwargs)
|
||||
|
|
|
@ -1756,6 +1756,23 @@ def test_validate_password_regex(app, settings):
|
|||
assert response.json['checks'][4]['result'] is True
|
||||
|
||||
|
||||
def test_validate_password_strength(app, settings):
|
||||
settings.A2_PASSWORD_POLICY_MIN_STRENGTH = 2
|
||||
response = app.post_json('/api/validate-password/', params={'password': 'w34k P455w0rd'})
|
||||
assert response.json['result'] == 1
|
||||
assert response.json['ok'] is False
|
||||
assert len(response.json['checks']) == 5
|
||||
assert response.json['checks'][4]['label'] == 'Secure password'
|
||||
assert response.json['checks'][4]['result'] is False
|
||||
|
||||
response = app.post_json('/api/validate-password/', params={'password': 'xbA2E4]#o'})
|
||||
assert response.json['result'] == 1
|
||||
assert response.json['ok'] is True
|
||||
assert len(response.json['checks']) == 5
|
||||
assert response.json['checks'][4]['label'] == 'Secure password'
|
||||
assert response.json['checks'][4]['result'] is True
|
||||
|
||||
|
||||
def test_api_users_get_or_create(settings, app, admin):
|
||||
app.authorization = ('Basic', (admin.username, admin.username))
|
||||
# test missing first_name
|
||||
|
|
Loading…
Reference in New Issue