- json/users.json: add 2 toolbar tools (selected_row) + script binds, same pattern as enable_user/disable_user; reset_password collects new password via prompt and POSTs JSON body to admin_reset_password.dspy - api/admin_reset_password.dspy: orgid ownership check, min length 8, cfm match, password_encode (RC4), also resets login_fail counters, audit_log(password_reset), new password never logged - api/clear_login_fail.dspy: ownership check, SET login_fail_count=0 last_login_fail=NULL, audit_log(login_fail_clear) - scripts/load_path.py: new PATHS_ADMIN section registered as *.admin (NOT logined - sensitive endpoints must not be open to any logged user)
73 lines
2.7 KiB
Plaintext
73 lines
2.7 KiB
Plaintext
ns = params_kw.copy()
|
|
|
|
userorgid = await get_userorgid()
|
|
if not userorgid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
|
|
target_id = ns.get('userid')
|
|
new_password = ns.get('new_password')
|
|
cfm_password = ns.get('cfm_password')
|
|
|
|
if not target_id:
|
|
return {"widgettype":"Error","options":{"title":"Parameter Error","cwidth":16,"cheight":9,"timeout":3,"message":"missing userid"}}
|
|
if not new_password or len(new_password) < 8:
|
|
return {"widgettype":"Error","options":{"title":"Parameter Error","cwidth":16,"cheight":9,"timeout":3,"message":"new password must be at least 8 characters"}}
|
|
if new_password != cfm_password:
|
|
return {"widgettype":"Error","options":{"title":"Parameter Error","cwidth":16,"cheight":9,"timeout":3,"message":"two passwords do not match"}}
|
|
|
|
db = DBPools()
|
|
dbname = get_module_dbname('rbac')
|
|
try:
|
|
async with db.sqlorContext(dbname) as sor:
|
|
recs = await sor.sqlExe(
|
|
"SELECT id, username, orgid FROM users WHERE id=${id}$",
|
|
{"id": target_id})
|
|
await sor.sqlExe("COMMIT", {})
|
|
if not recs:
|
|
return {"widgettype":"Error","options":{"title":"Reset Password Error","cwidth":16,"cheight":9,"timeout":3,"message":"user not exist"}}
|
|
rec = recs[0]
|
|
if rec.orgid and rec.orgid != userorgid:
|
|
return {"widgettype":"Error","options":{"title":"Reset Password Error","cwidth":16,"cheight":9,"timeout":3,"message":"Record no exist or with wrong ownership"}}
|
|
|
|
encoded = password_encode(new_password)
|
|
await sor.sqlExe(
|
|
"UPDATE users SET password=${pwd}$, login_fail_count = 0, last_login_fail = NULL WHERE id=${id}$",
|
|
{"pwd": encoded, "id": target_id})
|
|
await sor.sqlExe("COMMIT", {})
|
|
|
|
# 审计:操作留痕(失败不阻断主流程)
|
|
try:
|
|
from app_audit import audit_log
|
|
op_id = await get_user()
|
|
await audit_log(sor, str(op_id or ''), '', 'password_reset',
|
|
target=f'users:{rec.username}',
|
|
detail='admin reset password (new password not logged)',
|
|
client_ip=request.get('client_ip', '') if hasattr(request, 'get') else '')
|
|
await sor.sqlExe("COMMIT", {})
|
|
except Exception as ae:
|
|
debug(f'admin_reset_password audit failed: {ae}')
|
|
|
|
debug(f'admin_reset_password ok: target={rec.username} operator_orgid={userorgid}')
|
|
return {
|
|
"widgettype":"Message",
|
|
"options":{
|
|
"title":"Reset Password Success",
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"timeout":3,
|
|
"message":f"user {rec.username} password has been reset"
|
|
}
|
|
}
|
|
except Exception as e:
|
|
debug(f'admin_reset_password error: {format_exc()}')
|
|
return {"widgettype":"Error","options":{"title":"Reset Password Error","cwidth":16,"cheight":9,"timeout":5,"message":str(e)}}
|