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)}}