mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-09 15:10:27 +00:00
Merge pull request #1457 from nkukard/nkupdates-password-policy
Implement password strength & complexity checking
This commit is contained in:
commit
4a6d31cfa4
@ -205,6 +205,14 @@ class Setting(db.Model):
|
|||||||
'deny_domain_override': False,
|
'deny_domain_override': False,
|
||||||
'account_name_extra_chars': False,
|
'account_name_extra_chars': False,
|
||||||
'gravatar_enabled': False,
|
'gravatar_enabled': False,
|
||||||
|
'pwd_enforce_characters': False,
|
||||||
|
'pwd_min_len': 10,
|
||||||
|
'pwd_min_lowercase': 3,
|
||||||
|
'pwd_min_uppercase': 2,
|
||||||
|
'pwd_min_digits': 2,
|
||||||
|
'pwd_min_special': 1,
|
||||||
|
'pwd_enforce_complexity': False,
|
||||||
|
'pwd_min_complexity': 11
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, id=None, name=None, value=None):
|
def __init__(self, id=None, name=None, value=None):
|
||||||
|
@ -1557,7 +1557,23 @@ def setting_authentication():
|
|||||||
local_db_enabled = True if request.form.get(
|
local_db_enabled = True if request.form.get(
|
||||||
'local_db_enabled') else False
|
'local_db_enabled') else False
|
||||||
signup_enabled = True if request.form.get(
|
signup_enabled = True if request.form.get(
|
||||||
'signup_enabled', ) else False
|
'signup_enabled') else False
|
||||||
|
|
||||||
|
pwd_enforce_characters = True if request.form.get('pwd_enforce_characters') else False
|
||||||
|
pwd_min_len = safe_cast(request.form.get('pwd_min_len', Setting().defaults["pwd_min_len"]), int,
|
||||||
|
Setting().defaults["pwd_min_len"])
|
||||||
|
pwd_min_lowercase = safe_cast(request.form.get('pwd_min_lowercase', Setting().defaults["pwd_min_lowercase"]), int,
|
||||||
|
Setting().defaults["pwd_min_lowercase"])
|
||||||
|
pwd_min_uppercase = safe_cast(request.form.get('pwd_min_uppercase', Setting().defaults["pwd_min_uppercase"]), int,
|
||||||
|
Setting().defaults["pwd_min_uppercase"])
|
||||||
|
pwd_min_digits = safe_cast(request.form.get('pwd_min_digits', Setting().defaults["pwd_min_digits"]), int,
|
||||||
|
Setting().defaults["pwd_min_digits"])
|
||||||
|
pwd_min_special = safe_cast(request.form.get('pwd_min_special', Setting().defaults["pwd_min_special"]), int,
|
||||||
|
Setting().defaults["pwd_min_special"])
|
||||||
|
|
||||||
|
pwd_enforce_complexity = True if request.form.get('pwd_enforce_complexity') else False
|
||||||
|
pwd_min_complexity = safe_cast(request.form.get('pwd_min_complexity', Setting().defaults["pwd_min_complexity"]), int,
|
||||||
|
Setting().defaults["pwd_min_complexity"])
|
||||||
|
|
||||||
if not has_an_auth_method(local_db_enabled=local_db_enabled):
|
if not has_an_auth_method(local_db_enabled=local_db_enabled):
|
||||||
result = {
|
result = {
|
||||||
@ -1569,7 +1585,19 @@ def setting_authentication():
|
|||||||
else:
|
else:
|
||||||
Setting().set('local_db_enabled', local_db_enabled)
|
Setting().set('local_db_enabled', local_db_enabled)
|
||||||
Setting().set('signup_enabled', signup_enabled)
|
Setting().set('signup_enabled', signup_enabled)
|
||||||
|
|
||||||
|
Setting().set('pwd_enforce_characters', pwd_enforce_characters)
|
||||||
|
Setting().set('pwd_min_len', pwd_min_len)
|
||||||
|
Setting().set('pwd_min_lowercase', pwd_min_lowercase)
|
||||||
|
Setting().set('pwd_min_uppercase', pwd_min_uppercase)
|
||||||
|
Setting().set('pwd_min_digits', pwd_min_digits)
|
||||||
|
Setting().set('pwd_min_special', pwd_min_special)
|
||||||
|
|
||||||
|
Setting().set('pwd_enforce_complexity', pwd_enforce_complexity)
|
||||||
|
Setting().set('pwd_min_complexity', pwd_min_complexity)
|
||||||
|
|
||||||
result = {'status': True, 'msg': 'Saved successfully'}
|
result = {'status': True, 'msg': 'Saved successfully'}
|
||||||
|
|
||||||
elif conf_type == 'ldap':
|
elif conf_type == 'ldap':
|
||||||
ldap_enabled = True if request.form.get('ldap_enabled') else False
|
ldap_enabled = True if request.form.get('ldap_enabled') else False
|
||||||
|
|
||||||
@ -2151,3 +2179,10 @@ def validateURN(value):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def safe_cast(val, to_type, default=None):
|
||||||
|
try:
|
||||||
|
return to_type(val)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return default
|
||||||
|
@ -5,6 +5,8 @@ import traceback
|
|||||||
import datetime
|
import datetime
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import base64
|
import base64
|
||||||
|
import string
|
||||||
|
from zxcvbn import zxcvbn
|
||||||
from distutils.util import strtobool
|
from distutils.util import strtobool
|
||||||
from yaml import Loader, load
|
from yaml import Loader, load
|
||||||
from flask import Blueprint, render_template, make_response, url_for, current_app, g, session, request, redirect, abort
|
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)
|
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'])
|
@index_bp.route('/register', methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
CAPTCHA_ENABLE = current_app.config.get('CAPTCHA_ENABLE')
|
CAPTCHA_ENABLE = current_app.config.get('CAPTCHA_ENABLE')
|
||||||
@ -700,6 +788,10 @@ def register():
|
|||||||
email=email
|
email=email
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(password_policy_pass, password_policy) = password_policy_check(user, password)
|
||||||
|
if not password_policy_pass:
|
||||||
|
return render_template('register.html', error_messages=password_policy, captcha_enable=CAPTCHA_ENABLE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = user.create_local_user()
|
result = user.create_local_user()
|
||||||
if result and result['status']:
|
if result and result['status']:
|
||||||
|
@ -9,6 +9,8 @@ from flask_login import current_user, login_required, login_manager
|
|||||||
|
|
||||||
from ..models.user import User, Anonymous
|
from ..models.user import User, Anonymous
|
||||||
from ..models.setting import Setting
|
from ..models.setting import Setting
|
||||||
|
from .index import password_policy_check
|
||||||
|
|
||||||
|
|
||||||
user_bp = Blueprint('user',
|
user_bp = Blueprint('user',
|
||||||
__name__,
|
__name__,
|
||||||
@ -79,12 +81,23 @@ def profile():
|
|||||||
.format(current_user.username)
|
.format(current_user.username)
|
||||||
}), 400)
|
}), 400)
|
||||||
|
|
||||||
|
(password_policy_pass, password_policy) = password_policy_check(current_user.get_user_info_by_username(), new_password)
|
||||||
|
if not password_policy_pass:
|
||||||
|
if request.data:
|
||||||
|
return make_response(
|
||||||
|
jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'msg': password_policy['password'],
|
||||||
|
}), 400)
|
||||||
|
return render_template('user_profile.html', error_messages=password_policy)
|
||||||
|
|
||||||
user = User(username=current_user.username,
|
user = User(username=current_user.username,
|
||||||
plain_text_password=new_password,
|
plain_text_password=new_password,
|
||||||
firstname=firstname,
|
firstname=firstname,
|
||||||
lastname=lastname,
|
lastname=lastname,
|
||||||
email=email,
|
email=email,
|
||||||
reload_info=False)
|
reload_info=False)
|
||||||
|
|
||||||
user.update_profile()
|
user.update_profile()
|
||||||
|
|
||||||
return render_template('user_profile.html')
|
return render_template('user_profile.html')
|
||||||
|
@ -78,24 +78,89 @@
|
|||||||
<h3 class="card-title">Basic Settings</h3>
|
<h3 class="card-title">Basic Settings</h3>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.card-header -->
|
<!-- /.card-header -->
|
||||||
<div class="card-body">
|
<fieldset>
|
||||||
<div class="form-group">
|
<div class="card-body">
|
||||||
<input type="checkbox" id="local_db_enabled"
|
<div class="form-group">
|
||||||
name="local_db_enabled"
|
<input type="checkbox" id="local_db_enabled"
|
||||||
class="checkbox"
|
name="local_db_enabled"
|
||||||
{% if SETTING.get('local_db_enabled') %}checked{% endif %}>
|
class="checkbox"
|
||||||
<label for="local_db_enabled">Local DB
|
{% if SETTING.get('local_db_enabled') %}checked{% endif %}>
|
||||||
Authentication</label>
|
<label for="local_db_enabled">Local DB
|
||||||
|
Authentication</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="signup_enabled"
|
||||||
|
name="signup_enabled"
|
||||||
|
class="checkbox"
|
||||||
|
{% if SETTING.get('signup_enabled') %}checked{% endif %}>
|
||||||
|
<label for="signup_enabled">Allow users to sign
|
||||||
|
up</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</fieldset>
|
||||||
<input type="checkbox" id="signup_enabled"
|
<fieldset>
|
||||||
name="signup_enabled"
|
<legend>PASSWORD REQUIREMENTS</legend>
|
||||||
class="checkbox"
|
<div class="card-body">
|
||||||
{% if SETTING.get('signup_enabled') %}checked{% endif %}>
|
<div class="form-group">
|
||||||
<label for="signup_enabled">Allow users to sign
|
<input type="checkbox" id="pwd_enforce_characters"
|
||||||
up</label>
|
name="pwd_enforce_characters" class="checkbox"
|
||||||
|
{% if SETTING.get('pwd_enforce_characters') %}checked{% endif %}>
|
||||||
|
<label for="pwd_enforce_characters">
|
||||||
|
Enforce Character Requirements
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pwd_min_len">Minimum Password Length</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="pwd_min_len" id="pwd_min_len"
|
||||||
|
data-error="Please enter a minimum password length"
|
||||||
|
value="{{ SETTING.get('pwd_min_len') }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pwd_min_lowercase">Minimum Lowercase Characters</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="pwd_min_lowercase" id="pwd_min_lowercase"
|
||||||
|
data-error="Please enter the minimum number of lowercase letters required"
|
||||||
|
value="{{ SETTING.get('pwd_min_lowercase') }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pwd_min_uppercase">Minimum Uppercase Characters</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="pwd_min_uppercase" id="pwd_min_uppercase"
|
||||||
|
data-error="Please enter the minimum number of uppercase letters required"
|
||||||
|
value="{{ SETTING.get('pwd_min_uppercase') }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pwd_min_digits">Minimum Digit Characters</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="pwd_min_digits" id="pwd_min_digits"
|
||||||
|
data-error="Please enter the minimum number of digits required"
|
||||||
|
value="{{ SETTING.get('pwd_min_digits') }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pwd_min_special">Minimum Special Characters</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="pwd_min_special" id="pwd_min_special"
|
||||||
|
data-error="Please enter the minimum number of special characters required"
|
||||||
|
value="{{ SETTING.get('pwd_min_special') }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="pwd_enforce_complexity"
|
||||||
|
name="pwd_enforce_complexity" class="checkbox"
|
||||||
|
{% if SETTING.get('pwd_enforce_complexity') %}checked{% endif %}>
|
||||||
|
<label for="pwd_enforce_complexity">
|
||||||
|
Enforce Complexity Requirement
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pwd_min_complexity">Minimum Complexity (zxcvbn)</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="pwd_min_complexity" id="pwd_min_complexity"
|
||||||
|
data-error="Please enter the minimum password complexity required"
|
||||||
|
value="{{ SETTING.get('pwd_min_complexity') }}">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
<!-- /.card-body -->
|
<!-- /.card-body -->
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<button type="submit" class="btn btn-primary"
|
<button type="submit" class="btn btn-primary"
|
||||||
@ -117,7 +182,49 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- /.card-header -->
|
<!-- /.card-header -->
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>Fill in all the fields in the left form.</p>
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Local DB Authentication</dt>
|
||||||
|
<dd>Enable/disable local database authentication.</dd>
|
||||||
|
<dt>Allow Users to Signup</dt>
|
||||||
|
<dd>Allow users to signup. This requires local database authentication
|
||||||
|
to be enabled.</dd>
|
||||||
|
<legend>PASSWORD REQUIREMENTS</legend>
|
||||||
|
This section allows you to customize your local DB password requirements
|
||||||
|
and ensure that when users change their password or signup they are
|
||||||
|
picking strong passwords.
|
||||||
|
<br/>
|
||||||
|
Setting any entry field to a blank value will revert it back to default.
|
||||||
|
|
||||||
|
<dt>Enforce Character Requirements</dt>
|
||||||
|
<dd>This option will enforce the character type requirements for
|
||||||
|
passwords.
|
||||||
|
<ul>
|
||||||
|
<li>Minimum Lowercase Characters - Minimum number of lowercase
|
||||||
|
characters required to appear in the password.</li>
|
||||||
|
<li>Minimum Uppercase Characters - Minimum number of uppercase
|
||||||
|
characters required to appear in the password.</li>
|
||||||
|
<li>Minimum Digit Characters - Minimum number of digits
|
||||||
|
required to appear in the password. Digits include
|
||||||
|
1234567890.</li>
|
||||||
|
<li>Minimum Special Characters - Minimum number of special
|
||||||
|
characters required to appear in the password. Special
|
||||||
|
characters include
|
||||||
|
`!@#$%^&*()_-=+[]\{}|;:",.></?.</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>Enforce Complexity Requirement</dt>
|
||||||
|
<dd>Enable the enforcement of complex passwords. We currently use
|
||||||
|
<a href="https://github.com/dropbox/zxcvbn">zxcvbn</a> for
|
||||||
|
determining this.
|
||||||
|
<ul>
|
||||||
|
<li>Minimum Complexity - The default value of the log factor
|
||||||
|
is 11 as it is considered secure. More information about
|
||||||
|
the this can be found at
|
||||||
|
<a href="https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_wheeler.pdf">here</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.card-body -->
|
<!-- /.card-body -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,13 +34,13 @@
|
|||||||
<div class="nav-tabs-custom mb-2">
|
<div class="nav-tabs-custom mb-2">
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="#tabs-personal" data-toggle="tab">
|
<a class="nav-link {{ 'active' if not error_messages else '' }}" href="#tabs-personal" data-toggle="tab">
|
||||||
Personal Info
|
Personal Info
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% if session['authentication_type'] == 'LOCAL' %}
|
{% if session['authentication_type'] == 'LOCAL' %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#tabs-password" data-toggle="tab">
|
<a class="nav-link {{ 'active' if 'password' in error_messages else '' }}" href="#tabs-password" data-toggle="tab">
|
||||||
Change Password
|
Change Password
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -57,7 +57,8 @@
|
|||||||
<!-- /.nav-tabs-custom -->
|
<!-- /.nav-tabs-custom -->
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane fade show active" id="tabs-personal">
|
<div class="tab-pane fade {{ 'show active' if not error_messages else '' }}"
|
||||||
|
id="tabs-personal">
|
||||||
<form role="form" method="post" action="{{ user_profile }}">
|
<form role="form" method="post" action="{{ user_profile }}">
|
||||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -91,7 +92,8 @@
|
|||||||
<!-- /.tab-pane -->
|
<!-- /.tab-pane -->
|
||||||
|
|
||||||
{% if session['authentication_type'] == 'LOCAL' %}
|
{% if session['authentication_type'] == 'LOCAL' %}
|
||||||
<div class="tab-pane fade" id="tabs-password">
|
<div class="tab-pane fade {{ 'show active' if 'password' in error_messages else '' }}"
|
||||||
|
id="tabs-password">
|
||||||
{% if not current_user.password %}
|
{% if not current_user.password %}
|
||||||
Your account password is managed via LDAP which isn't supported to
|
Your account password is managed via LDAP which isn't supported to
|
||||||
change here.
|
change here.
|
||||||
@ -101,8 +103,15 @@
|
|||||||
value="{{ csrf_token() }}">
|
value="{{ csrf_token() }}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password">New Password</label>
|
<label for="password">New Password</label>
|
||||||
<input type="password" class="form-control" name="password"
|
<input type="password" class="form-control {{ 'is-invalid' if 'password' in error_messages else '' }}"
|
||||||
|
name="password"
|
||||||
id="newpassword">
|
id="newpassword">
|
||||||
|
{% if 'password' in error_messages %}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
{{ error_messages['password'] }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="rpassword">Re-type New Password</label>
|
<label for="rpassword">Re-type New Password</label>
|
||||||
|
@ -43,4 +43,5 @@ webcolors==1.12
|
|||||||
werkzeug==2.1.2
|
werkzeug==2.1.2
|
||||||
zipp==3.11.0
|
zipp==3.11.0
|
||||||
rcssmin==1.1.1
|
rcssmin==1.1.1
|
||||||
psycopg2==2.9.5
|
zxcvbn==4.4.28
|
||||||
|
psycopg2==2.9.5
|
Loading…
Reference in New Issue
Block a user