完成(08-09-production-admin-break-glass-reset): 恢复管理员认证
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-09 09:18:13 +08:00
父节点 8806521c22
当前提交 4fbfee2ff2
共修改 4 个文件,包含 109 行新增和 40 行删除
@@ -1924,3 +1924,68 @@ node scripts/qa/payment-batch-flag-snapshot.mjs restore --snapshot-file "$REPORT
```tsx
<Link to="/mobile/payment-batches">进入付款</Link>
```
## Scenario: Unique Production Administrator Break-Glass Reset
### 1. Scope / Trigger
- Trigger: the only active production administrator cannot authenticate, self-service reset is unavailable, and a separately approved task restores access with the existing break-glass script.
- Purpose: make the single irreversible reset attempt observable without exposing credentials or treating historical audit rows as proof of the current run.
### 2. Signatures
- Read-only commands: `--preflight`, `--verify`, `--verify-password-changed`, `--diagnose-wecom-binding` on `server/src/scripts/reset-active-admin-password.js`.
- Single write command: `node src/scripts/reset-active-admin-password.js --apply ACTIVE_ADMIN_BREAK_GLASS`, with the temporary password on stdin only.
- Browser lifecycle: `/login` -> `/change-password` -> `/portal`, one login submission and one password-change submission.
### 3. Contracts
- Compare the running-container script SHA with the reviewed deployed candidate before selecting a target. A workspace copy must never be pushed into the container to make the check pass.
- Create a local apply-attempt sentinel before opening the SSH/container channel. Missing output, non-zero transport status or invalid JSON enters `APPLY_UNCERTAIN`; only `--verify` may classify the result, and apply is never retried.
- Capture the break-glass audit count before apply and require an increment of exactly one afterward. `auditPresent=true` alone is insufficient because a historical reset may already satisfy that boolean.
- `--verify` immediately after apply must prove the unique target, must-change=true, cleared locks, zero active sessions, zero active password-reset codes, zero active email-login codes and a break-glass audit.
- Atomically replace the repository-external smoke secret first with the temporary credential and later with the final credential. Before consuming the one allowed login, read the file back in-process and compare both identifier and password to the controlled source; a successful rename without content agreement is not readiness.
- The browser runner receives credentials through controlled file descriptors/stdin, blocks non-authentication business APIs, submits each form once, clears browser state and does not enter business pages.
- A prior hash may be kept only as approved `0700/0600` short-lived recovery material. Destroy it and all temporary credentials after `--verify-password-changed` passes; evidence stores booleans/counts, never values or paths.
### 4. Validation & Error Matrix
- Running script SHA mismatch, target count other than one or binding conflict -> stop before recovery material and apply.
- Active session with recent activity -> stop and confirm operator ownership; an old inactive session may be revoked only when that side effect is already approved.
- Apply channel/result uncertain -> write no second password; run `--verify` only.
- Apply PASS but `--verify` fails -> do not log in; retain controlled materials until a new decision or the custody deadline.
- Secret update content mismatch -> do not consume the login attempt; fix the local atomic update and re-verify without another apply.
- Login or change-password submission fails -> zero retry; preserve the known credential state and request a new decision.
- Recovery-material deletion fails -> task remains incomplete even when the final password works.
### 5. Good/Base/Bad Cases
- Good: audit baseline 1 becomes 2 after one apply, immediate verify passes, one login completes forced change, final verify passes, and all temporary material is destroyed.
- Base: audit baseline 0 becomes 1 with the same state-machine checks.
- Bad: accept `auditPresent=true` when the baseline was already positive, blindly retry after an SSH interruption, or attempt login before proving the temporary secret contains the new credential.
### 6. Tests Required
- Source review must trace every session/challenge update to `where.employee_id=target.id`. When this script's write behavior changes, focused unit tests must additionally assert those exact `where` arguments; call-count assertions alone do not prove target scoping.
- Unit tests cover non-unique target refusal, transaction-time target change, apply side effects and both verify modes.
- Execution evidence asserts runtime-script SHA match, apply-attempt count 1, audit delta exactly 1, the complete post-apply verify fields, login/change submit counts 1/1 and cleanup booleans.
- Run focused script tests, Node syntax, task/process/governance/sensitive gates and `git diff --check`; an authentication-state-machine task additionally receives a read-only reviewer.
### 7. Wrong vs Correct
#### Wrong
```text
apply output missing -> retry apply
verify.auditPresent=true -> current audit write proved
atomic rename succeeded -> consume the only login attempt
```
#### Correct
```text
create attempt sentinel -> apply once -> uncertain output means verify only
capture audit baseline -> require baseline + 1
atomic rename -> compare identifier/password in-process -> login once
-> change password once -> final verify -> destroy temporary material
```