rbac/wwwroot/api/admin_reset_password.dspy

78 lines
3.0 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')
# 守卫:管理员不得重置自己的密码(自己改密走 /rbac/user/reset_password 自助入口)
op_id = await get_user()
if op_id and str(op_id) == str(target_id):
return {"widgettype":"Error","options":{"title":"Reset Password Error","cwidth":16,"cheight":9,"timeout":3,"message":"cannot reset your own password, use self-service reset"}}
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)}}