mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-12-04 19:15:30 +00:00
feat(authentication): added password policy checker function
This commit is contained in:
parent
bb6d2d0497
commit
1cea4b7ce3
@ -5,6 +5,8 @@ import traceback
|
||||
import datetime
|
||||
import ipaddress
|
||||
import base64
|
||||
import string
|
||||
from zxcvbn import zxcvbn
|
||||
from distutils.util import strtobool
|
||||
from yaml import Loader, load
|
||||
from flask import Blueprint, render_template, make_response, url_for, current_app, g, session, request, redirect, abort
|
||||
@ -649,6 +651,92 @@ def logout():
|
||||
return redirect(redirect_uri)
|
||||
|
||||
|
||||
def password_policy_check(user, password):
|
||||
|
||||
def check_policy(chars, user_password, setting):
|
||||
lenreq = int(Setting().get(setting))
|
||||
test_string = user_password
|
||||
for c in chars:
|
||||
test_string = test_string.replace(c, '')
|
||||
return (lenreq, len(user_password) - len(test_string))
|
||||
|
||||
def matches_policy(item, policy_fails):
|
||||
return "*" if item in policy_fails else ""
|
||||
|
||||
policy = []
|
||||
policy_fails = {}
|
||||
|
||||
# If either policy is enabled check basics first ... this is obvious!
|
||||
if Setting().get('pwd_enforce_characters') or Setting().get('pwd_enforce_complexity'):
|
||||
# Cannot contain username
|
||||
if user.username in password:
|
||||
policy_fails["username"] = True
|
||||
policy.append(f"{matches_policy('username', policy_fails)}cannot contain username")
|
||||
|
||||
# Cannot contain password
|
||||
if user.firstname in password:
|
||||
policy_fails["firstname"] = True
|
||||
policy.append(f"{matches_policy('firstname', policy_fails)}cannot contain firstname")
|
||||
|
||||
# Cannot contain lastname
|
||||
if user.lastname in password:
|
||||
policy_fails["lastname"] = True
|
||||
policy.append(f"{matches_policy('lastname', policy_fails)}cannot contain lastname")
|
||||
|
||||
# Cannot contain email
|
||||
if user.email in password:
|
||||
policy_fails["email"] = True
|
||||
policy.append(f"{matches_policy('email', policy_fails)}cannot contain email")
|
||||
|
||||
# Check if we're enforcing character requirements
|
||||
if Setting().get('pwd_enforce_characters'):
|
||||
# Length
|
||||
pwd_min_len_setting = int(Setting().get('pwd_min_len'))
|
||||
pwd_len = len(password)
|
||||
if pwd_len < pwd_min_len_setting:
|
||||
policy_fails["length"] = True
|
||||
policy.append(f"{matches_policy('length', policy_fails)}length={pwd_len}/{pwd_min_len_setting}")
|
||||
# Digits
|
||||
(pwd_min_digits_setting, pwd_digits) = check_policy(string.digits, password, 'pwd_min_digits')
|
||||
if pwd_digits < pwd_min_digits_setting:
|
||||
policy_fails["digits"] = True
|
||||
policy.append(f"{matches_policy('digits', policy_fails)}digits={pwd_digits}/{pwd_min_digits_setting}")
|
||||
# Lowercase
|
||||
(pwd_min_lowercase_setting, pwd_lowercase) = check_policy(string.digits, password, 'pwd_min_lowercase')
|
||||
if pwd_lowercase < pwd_min_lowercase_setting:
|
||||
policy_fails["lowercase"] = True
|
||||
policy.append(f"{matches_policy('lowercase', policy_fails)}lowercase={pwd_lowercase}/{pwd_min_lowercase_setting}")
|
||||
# Uppercase
|
||||
(pwd_min_uppercase_setting, pwd_uppercase) = check_policy(string.digits, password, 'pwd_min_uppercase')
|
||||
if pwd_uppercase < pwd_min_uppercase_setting:
|
||||
policy_fails["uppercase"] = True
|
||||
policy.append(f"{matches_policy('uppercase', policy_fails)}uppercase={pwd_uppercase}/{pwd_min_uppercase_setting}")
|
||||
# Special
|
||||
(pwd_min_special_setting, pwd_special) = check_policy(string.digits, password, 'pwd_min_special')
|
||||
if pwd_special < pwd_min_special_setting:
|
||||
policy_fails["special"] = True
|
||||
policy.append(f"{matches_policy('special', policy_fails)}special={pwd_special}/{pwd_min_special_setting}")
|
||||
|
||||
if Setting().get('pwd_enforce_complexity'):
|
||||
# Complexity checking
|
||||
zxcvbn_inputs = []
|
||||
for input in (user.firstname, user.lastname, user.username, user.email):
|
||||
if len(input):
|
||||
zxcvbn_inputs.append(input)
|
||||
|
||||
result = zxcvbn(password, user_inputs=zxcvbn_inputs)
|
||||
pwd_min_complexity_setting = int(Setting().get('pwd_min_complexity'))
|
||||
pwd_complexity = result['guesses_log10']
|
||||
if pwd_complexity < pwd_min_complexity_setting:
|
||||
policy_fails["complexity"] = True
|
||||
policy.append(f"{matches_policy('complexity', policy_fails)}complexity={pwd_complexity:.0f}/{pwd_min_complexity_setting}")
|
||||
|
||||
policy_str = {"password": f"Fails policy: {', '.join(policy)}. Items prefixed with '*' failed."}
|
||||
|
||||
# NK: the first item in the tuple indicates a PASS, so, we check for any True's and negate that
|
||||
return (not any(policy_fails.values()), policy_str)
|
||||
|
||||
|
||||
@index_bp.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
CAPTCHA_ENABLE = current_app.config.get('CAPTCHA_ENABLE')
|
||||
|
@ -43,3 +43,4 @@ webcolors==1.12
|
||||
werkzeug==2.1.2
|
||||
zipp==3.11.0
|
||||
rcssmin==1.1.1
|
||||
zxcvbn==4.4.28
|
Loading…
Reference in New Issue
Block a user