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

这个提交包含在:
2026-07-17 14:24:58 +08:00
父节点 bbb5c89825
当前提交 6ddedf1e0e
共修改 31 个文件,包含 3801 行新增和 419 行删除
@@ -0,0 +1,28 @@
ALTER TABLE "employees"
ADD COLUMN "email_login_code_failed_count" INTEGER NOT NULL DEFAULT 0,
ADD COLUMN "email_login_code_failed_first_at" TIMESTAMP(3),
ADD COLUMN "email_login_code_locked_until" TIMESTAMP(3),
ADD COLUMN "email_login_code_last_requested_at" TIMESTAMP(3),
ADD COLUMN "password_reset_failed_count" INTEGER NOT NULL DEFAULT 0,
ADD COLUMN "password_reset_failed_first_at" TIMESTAMP(3),
ADD COLUMN "password_reset_locked_until" TIMESTAMP(3),
ADD COLUMN "password_reset_request_count" INTEGER NOT NULL DEFAULT 0,
ADD COLUMN "password_reset_request_first_at" TIMESTAMP(3),
ADD COLUMN "password_reset_last_requested_at" TIMESTAMP(3);
ALTER TABLE "password_reset_codes"
ADD COLUMN "invalidated_at" TIMESTAMP(3),
ADD COLUMN "attempts_count" INTEGER NOT NULL DEFAULT 0;
CREATE INDEX "password_reset_codes_employee_id_invalidated_at_created_at_idx"
ON "password_reset_codes"("employee_id", "invalidated_at", "created_at");
-- Existing codes were generated and consumed under the weaker legacy contract.
-- Invalidate them at rollout instead of carrying unsafe compatibility forward.
UPDATE "password_reset_codes"
SET "invalidated_at" = CURRENT_TIMESTAMP
WHERE "used_at" IS NULL AND "invalidated_at" IS NULL;
UPDATE "email_login_codes"
SET "used_at" = CURRENT_TIMESTAMP
WHERE "used_at" IS NULL;
+13
查看文件
@@ -605,6 +605,16 @@ model Employee {
login_failed_count Int @default(0)
login_failed_first_at DateTime?
login_locked_until DateTime?
email_login_code_failed_count Int @default(0)
email_login_code_failed_first_at DateTime?
email_login_code_locked_until DateTime?
email_login_code_last_requested_at DateTime?
password_reset_failed_count Int @default(0)
password_reset_failed_first_at DateTime?
password_reset_locked_until DateTime?
password_reset_request_count Int @default(0)
password_reset_request_first_at DateTime?
password_reset_last_requested_at DateTime?
archived_at DateTime?
created_at DateTime @default(now())
@@ -723,6 +733,8 @@ model PasswordResetCode {
code_hash String
expires_at DateTime
used_at DateTime?
invalidated_at DateTime?
attempts_count Int @default(0)
created_at DateTime @default(now())
created_ip String?
created_user_agent String?
@@ -730,6 +742,7 @@ model PasswordResetCode {
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
@@index([employee_id, created_at])
@@index([employee_id, invalidated_at, created_at])
@@index([expires_at])
@@map("password_reset_codes")
}