Two backlog trees are complete: `docs/project/backlog/` (75 files, every WP done) and `docs/project/refactor-backlog-setup/` (the arc before it). Move both under `docs/project/archive/` with `git mv`, so history stays intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them, because it points at the now-archived backlog README. Add `docs/project/archive/README.md`. It states that these trees are historical and names the two directories that are still live. Repoint every inbound reference named in RD-30's Files table: CLAUDE.md, the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the `document-feature` and `new-ssp` skills, and the readable-codebase PLAN, README, and RD-19 ticket. Fix two upward-relative links inside the moved WP files (WP-68, WP-69) that gained a directory level and would otherwise break. Repoint `.prettierignore`'s two agent-prompt exclusions to their new path, so prettier keeps leaving those files' exact wording alone. Mark RD-30 done and check off its acceptance criteria; flip its README row to done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
2.4 KiB
Markdown
47 lines
2.4 KiB
Markdown
# WP-41 — Persisted, queryable authz/PII-reveal audit
|
|
|
|
Status: done
|
|
Phase: 8 — platform/DX/showcase
|
|
Priority: P2
|
|
Depends on: WP-40
|
|
|
|
## Outcome
|
|
|
|
New data-minimised EF table `AuthzAuditEntry` (`Data/AuthzAuditStore.cs`, DbSet + key config in
|
|
`AppDbContext`, migration `AuthzAudit`): `At, Action, Resource, Decision, Role, CorrelationId` —
|
|
**never** a name/BSN/value. `AuditAuthz` now persists (via `AuthzAuditStore.Record`) alongside its
|
|
log line, so every authz denial + BIG-nummer reveal/step-up attempt is captured. `GET /admin/audit`
|
|
(admin-gated by the existing `CasesAdmin`/`cases:manage` — a dedicated `audit:read` cap is a later
|
|
refinement) returns the trail newest-first (client-side sort — SQLite can't ORDER BY DateTimeOffset).
|
|
+3 backend tests (deny recorded, reveal recorded, **schema-carries-no-PII** reflection test). Typed
|
|
client regenerated (`audit()` + `AuthzAuditDto`). No FE consumer yet — a future audit view (WP-42
|
|
finish) must add `/api/v1/admin/audit` to the `role.interceptor` ROLE_AWARE list or it silently 403s.
|
|
|
|
## Why
|
|
|
|
The security-relevant events (authz denials via `AuditAuthz`, BIG-nummer reveal, step-up) are
|
|
**log-only** — never persisted, so there's no queryable trail. The one persisted `AuditEntry` table
|
|
covers document lifecycle only. PRD-0002 §8 calls for a persisted authorization audit. A register
|
|
(BIO) wants this queryable, and strictly data-minimised.
|
|
|
|
## Decisions
|
|
|
|
- Persist authz/reveal/step-up events into a data-minimised EF table extending the existing
|
|
`AuditEntry`/`AppDbContext` pattern: role, action, resource-ref, decision, correlation-id,
|
|
timestamp — **never** the value or any PII.
|
|
- Wire `AuditAuthz` + the reveal endpoint to persist (in addition to the log line).
|
|
- Optional thin admin read view — reuse `cases:manage` or add an `audit:read` capability.
|
|
|
|
## Files
|
|
|
|
- `backend/.../Data/` — new audit entity + `AppDbContext` DbSet + EF migration.
|
|
- `backend/.../Program.cs` — `AuditAuthz` + reveal endpoint persist.
|
|
- Backend tests: events are persisted; **no PII columns**; correlation-id captured.
|
|
- (Optional) FE audit read page + adapter/store, gated by the capability.
|
|
|
|
## Acceptance criteria
|
|
|
|
- [x] Denials, reveals, and step-up attempts land as rows with no PII/value fields.
|
|
- [x] A test asserts the schema carries no name/bsn/value column.
|
|
- [x] `dotnet test` (132) + `npm run ci` green; api-client drift clean after commit.
|