修复认证挑战与可信代理安全边界(07-17-auth-challenge-trusted-proxy-hardening)

这个提交包含在:
2026-07-17 14:24:58 +08:00
父节点 bbb5c89825
当前提交 6ddedf1e0e
共修改 31 个文件,包含 3801 行新增和 419 行删除
+67 -5
查看文件
@@ -58,6 +58,9 @@ async function buildApp({
const employeeRoleCreateMany = vi.fn().mockResolvedValue({ count: 1 });
const employeeRoleFindMany = vi.fn().mockResolvedValue([{ role_code: "employee" }]);
const auditCreate = vi.fn().mockResolvedValue({ id: "audit-1" });
const authSessionUpdateMany = vi.fn().mockResolvedValue({ count: 0 });
const passwordResetCodeUpdateMany = vi.fn().mockResolvedValue({ count: 0 });
const emailLoginCodeUpdateMany = vi.fn().mockResolvedValue({ count: 0 });
const tx = {
department: {
@@ -75,7 +78,13 @@ async function buildApp({
create: auditCreate,
},
authSession: {
updateMany: vi.fn().mockResolvedValue({ count: 0 }),
updateMany: authSessionUpdateMany,
},
passwordResetCode: {
updateMany: passwordResetCodeUpdateMany,
},
emailLoginCode: {
updateMany: emailLoginCodeUpdateMany,
},
};
@@ -134,6 +143,10 @@ async function buildApp({
employeeUpdate,
employeeRoleCreateMany,
employeeRoleFindMany,
auditCreate,
authSessionUpdateMany,
passwordResetCodeUpdateMany,
emailLoginCodeUpdateMany,
};
}
@@ -147,7 +160,7 @@ describe("usersRoutes write flows", () => {
payload: {
name: "张三",
phone: "13800000001",
email: "zhangsan@example.com",
email: "ZhangSan@Example.COM",
roles: ["employee"],
},
});
@@ -155,11 +168,10 @@ describe("usersRoutes write flows", () => {
expect(response.statusCode).toBe(200);
expect(employeeCreate).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.not.objectContaining({
employee_no: expect.anything(),
}),
data: expect.objectContaining({ email: "[email protected]" }),
}),
);
expect(employeeCreate.mock.calls[0][0].data).not.toHaveProperty("employee_no");
expect(employeeRoleCreateMany).toHaveBeenCalledWith(
expect.objectContaining({
data: [
@@ -210,4 +222,54 @@ describe("usersRoutes write flows", () => {
await app.close();
});
it("revokes sessions and outstanding challenges during an administrator password reset", async () => {
const {
app,
employeeUpdate,
authSessionUpdateMany,
passwordResetCodeUpdateMany,
emailLoginCodeUpdateMany,
auditCreate,
} = await buildApp();
const response = await app.inject({
method: "POST",
url: `/api/users/${USER_ID}/reset-password`,
payload: { newPassword: "AdministratorReset1234" },
});
expect(response.statusCode).toBe(200);
expect(response.json().data).toEqual({
id: USER_ID,
oneTimePassword: "AdministratorReset1234",
});
expect(employeeUpdate).toHaveBeenCalledWith({
where: { id: USER_ID },
data: expect.objectContaining({
must_change_password: true,
token_version: { increment: 1 },
email_login_code_failed_count: 0,
password_reset_failed_count: 0,
password_reset_request_count: 0,
}),
});
expect(authSessionUpdateMany).toHaveBeenCalledWith({
where: { employee_id: USER_ID, revoked_at: null },
data: { revoked_at: expect.any(Date), last_seen_at: expect.any(Date) },
});
expect(passwordResetCodeUpdateMany).toHaveBeenCalledWith({
where: { employee_id: USER_ID, invalidated_at: null },
data: { invalidated_at: expect.any(Date) },
});
expect(emailLoginCodeUpdateMany).toHaveBeenCalledWith({
where: { employee_id: USER_ID, used_at: null },
data: { used_at: expect.any(Date) },
});
expect(auditCreate).toHaveBeenCalledWith({
data: expect.objectContaining({ action: "SYSTEM_USER_PASSWORD_RESET" }),
});
await app.close();
});
});