Jul 2nd - Optimized code 2
This commit is contained in:
+1
-1
@@ -354,7 +354,7 @@ def edit_user(user_id):
|
||||
new_pw = request.form.get('new_password', '')
|
||||
if new_pw:
|
||||
confirm_pw = request.form.get('confirm_password', '')
|
||||
pw_error = validate_password(new_pw, confirm_pw)
|
||||
pw_error = validate_password(new_pw, confirm_pw, user.password_hash)
|
||||
if pw_error:
|
||||
flash(pw_error, 'danger')
|
||||
return render_template('admin/edit_user.html', user=user, roles=_roles())
|
||||
|
||||
+3
-3
@@ -173,7 +173,7 @@ def profile():
|
||||
logger.info(f'[AUTH AVATAR UPLOAD] user_id={current_user.id} file={stored_name}')
|
||||
|
||||
if new_pw:
|
||||
pw_error = validate_password(new_pw, confirm_pw)
|
||||
pw_error = validate_password(new_pw, confirm_pw, current_user.password_hash)
|
||||
if pw_error:
|
||||
flash(pw_error, 'danger')
|
||||
return render_template('auth/profile.html')
|
||||
@@ -236,12 +236,12 @@ def reset_password(token):
|
||||
if request.method == 'POST':
|
||||
password = request.form.get('password', '')
|
||||
confirm = request.form.get('confirm_password', '')
|
||||
pw_error = validate_password(password, confirm)
|
||||
user = token_row.user
|
||||
pw_error = validate_password(password, confirm, user.password_hash)
|
||||
if pw_error:
|
||||
flash(pw_error, 'danger')
|
||||
return render_template('auth/reset_password.html', token=token)
|
||||
|
||||
user = token_row.user
|
||||
user.set_password(password)
|
||||
db.session.delete(token_row) # single-use — delete immediately
|
||||
log_action(user.id, 'password_reset', 'user', user.id)
|
||||
|
||||
@@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# ─── Password Validation ──────────────────────────────────────────────────────
|
||||
|
||||
def validate_password(password: str, confirm: str) -> str | None:
|
||||
def validate_password(password: str, confirm: str, current_hash: str | None = None) -> str | None:
|
||||
"""Validate a new password and its confirmation field.
|
||||
|
||||
Returns an error message string if validation fails, or None if the
|
||||
@@ -27,11 +27,17 @@ def validate_password(password: str, confirm: str) -> str | None:
|
||||
- Must contain at least one uppercase letter (A-Z).
|
||||
- Must contain at least one digit (0-9).
|
||||
- Must contain at least one special character (!@#$%^&* etc.).
|
||||
- If current_hash is given, the new password must differ from it.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
password : str – the candidate password (plain text)
|
||||
confirm : str – the confirmation field value
|
||||
current_hash : str|None – the user's existing password_hash, if this is
|
||||
a password *change* (profile, admin edit,
|
||||
reset) rather than new-account creation.
|
||||
When given, re-using the current password is
|
||||
rejected.
|
||||
"""
|
||||
import re
|
||||
if password != confirm:
|
||||
@@ -48,6 +54,12 @@ def validate_password(password: str, confirm: str) -> str | None:
|
||||
return 'Password must contain at least one number.'
|
||||
if not re.search(r'[!@#$%^&*()\-_=+\[\]{};:\'",.<>?/\\|`~]', password):
|
||||
return 'Password must contain at least one special character.'
|
||||
# Hash comparison is the most expensive check, so it runs last —
|
||||
# only after every cheap format check has already passed.
|
||||
if current_hash:
|
||||
from werkzeug.security import check_password_hash
|
||||
if check_password_hash(current_hash, password):
|
||||
return 'New password must be different from your current password.'
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pw-match" id="pw-match-msg"></div>
|
||||
<div class="pw-match" id="pw-match-msg" aria-live="polite" aria-atomic="true"></div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<!-- Strength bar -->
|
||||
@@ -136,7 +136,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Rule checklist — mirrors server-side validate_password() rules -->
|
||||
<div class="pw-rules" id="pw-rules-box" style="display:none;">
|
||||
<div class="pw-rules" id="pw-rules-box" style="display:none;" aria-live="polite" aria-atomic="true">
|
||||
<div class="pw-rule unmet" id="pw-rule-len">
|
||||
<span class="ri"><i class="bi bi-circle"></i></span>
|
||||
<span>8+ characters</span>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pw-match" id="pw-match-msg"></div>
|
||||
<div class="pw-match" id="pw-match-msg" aria-live="polite" aria-atomic="true"></div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<!-- Strength bar -->
|
||||
@@ -82,7 +82,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Rule checklist — mirrors server-side validate_password() rules -->
|
||||
<div class="pw-rules" id="pw-rules-box" style="display:none;">
|
||||
<div class="pw-rules" id="pw-rules-box" style="display:none;" aria-live="polite" aria-atomic="true">
|
||||
<div class="pw-rule unmet" id="pw-rule-len">
|
||||
<span class="ri"><i class="bi bi-circle"></i></span>
|
||||
<span>8+ characters</span>
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<div class="pw-strength-label" id="prof-strength-label"></div>
|
||||
</div>
|
||||
<!-- Rule checklist -->
|
||||
<div class="pw-rules" id="prof-rules" style="display:none;">
|
||||
<div class="pw-rules" id="prof-rules" style="display:none;" aria-live="polite" aria-atomic="true">
|
||||
<div class="pw-rule unmet" id="prof-rule-len"><span class="ri"><i class="bi bi-circle"></i></span> 8+ characters</div>
|
||||
<div class="pw-rule unmet" id="prof-rule-upper"><span class="ri"><i class="bi bi-circle"></i></span> Uppercase letter</div>
|
||||
<div class="pw-rule unmet" id="prof-rule-digit"><span class="ri"><i class="bi bi-circle"></i></span> Number</div>
|
||||
@@ -112,7 +112,7 @@
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pw-match" id="prof-match-msg"></div>
|
||||
<div class="pw-match" id="prof-match-msg" aria-live="polite" aria-atomic="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Rule checklist — mirrors server-side validate_password() rules -->
|
||||
<div class="pw-rules" id="rules-box" style="display:none;">
|
||||
<div class="pw-rules" id="rules-box" style="display:none;" aria-live="polite" aria-atomic="true">
|
||||
<div class="pw-rule unmet" id="rule-len">
|
||||
<span class="ri"><i class="bi bi-circle"></i></span>
|
||||
<span>8+ characters</span>
|
||||
@@ -155,7 +155,7 @@
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pw-match" id="match-msg"></div>
|
||||
<div class="pw-match" id="match-msg" aria-live="polite" aria-atomic="true"></div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn" id="submit-btn">
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Rule checklist — mirrors server-side validate_password() rules -->
|
||||
<div class="pw-rules" id="rules-box" style="display:none;">
|
||||
<div class="pw-rules" id="rules-box" style="display:none;" aria-live="polite" aria-atomic="true">
|
||||
<div class="pw-rule unmet" id="rule-len">
|
||||
<span class="ri"><i class="bi bi-circle"></i></span>
|
||||
<span>8+ characters</span>
|
||||
@@ -109,7 +109,7 @@
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pw-match" id="match-msg"></div>
|
||||
<div class="pw-match" id="match-msg" aria-live="polite" aria-atomic="true"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn" id="submit-btn"><i class="bi bi-lock me-2"></i>Update Password</button>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user