From c5bf0d3f59a1cacd73ecd5ee3cd6a005dcc43b73 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Wed, 11 Mar 2026 18:06:15 +0530 Subject: [PATCH] fix: prevent user from setting same password on forced reset (#37652) Co-authored-by: UmakanthKaspa --- frappe/core/doctype/user/user.py | 10 +++++++++- frappe/utils/password.py | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index b1831d59f9..99b68ed536 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -36,7 +36,7 @@ from frappe.utils import ( ) from frappe.utils.data import sha256_hash from frappe.utils.html_utils import sanitize_html -from frappe.utils.password import check_password, get_password_reset_limit +from frappe.utils.password import check_password, get_password_reset_limit, is_password_reused from frappe.utils.password import update_password as _update_password from frappe.utils.user import get_system_managers from frappe.website.utils import get_home_page, is_signup_disabled @@ -929,6 +929,14 @@ def update_password( else: user = res["user"] + if is_password_reused(user, new_password): + frappe.throw( + _( + "New password cannot be the same as your current password. Please choose a different password." + ), + title=_("Invalid Password"), + ) + logout_all_sessions = cint(logout_all_sessions) or frappe.get_system_settings("logout_on_password_reset") _update_password(user, new_password, logout_all_sessions=cint(logout_all_sessions)) diff --git a/frappe/utils/password.py b/frappe/utils/password.py index 0393cd69dd..a8d8807117 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -80,6 +80,24 @@ def remove_encrypted_password(doctype, name, fieldname="password"): frappe.db.delete("__Auth", {"doctype": doctype, "name": name, "fieldname": fieldname}) +def is_password_reused(user, pwd, doctype="User", fieldname="password"): + """Return True if pwd matches the stored password for user, else False.""" + result = ( + frappe.qb.from_(Auth) + .select(Auth.password) + .where( + (Auth.doctype == doctype) + & (Auth.name == user) + & (Auth.fieldname == fieldname) + & (Auth.encrypted == 0) + ) + .limit(1) + .run(as_dict=True) + ) + + return bool(result and passlibctx.verify(pwd, result[0].password)) + + def check_password(user, pwd, doctype="User", fieldname="password", delete_tracker_cache=True): """Checks if user and password are correct, else raises frappe.AuthenticationError"""