Files
sciagent/docs/audit-log-manager-plan.md
Thinh Lam 688fac73e9
CI/CD / backend (push) Failing after 2m8s
CI/CD / frontend (push) Failing after 1m40s
CI/CD / deploy (push) Has been skipped
sciagent code + Gitea Actions CI/CD
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 09:38:30 +07:00

144 lines
8.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Audit Log Manager — Admin Planning Document
This plan describes the **Audit Log Manager**: an admin-only surface for tracing and monitoring **per-user activity** in the web application, with **time** as the primary navigation axis. It is derived from and must stay consistent with [`assets/docs/audit-log-implementation.md`](../assets/docs/audit-log-implementation.md) (schema, `record_audit`, role-based logging rules, and Step 6 admin viewer).
---
## 1. Goals
| Goal | Description |
|------|-------------|
| **Forensic traceability** | Answer: “What did user *U* do, and when?” and “What happened to entity *E* between *T₁* and *T₂*?” |
| **Time-first exploration** | Default and sort order anchored on **`occurred_at`** (UTC stored as `TIMESTAMPTZ`; display in admins locale or a configured org timezone). |
| **Tamper-aware source** | Queries read from append-only `audit_events` (app role: `INSERT`/`SELECT` only per implementation guide). |
| **No scope creep** | v1 is **query + view**, not anomaly detection, not primary shipping to external log stacks (see implementation guide §9). |
---
## 2. Relationship to backend data model
The implementation guide defines a unified **`audit_events`** table keyed by time:
- **`occurred_at`** — canonical **timestamp** for all list, filter, and timeline views; indexed with `actor_user_id` and other dimensions.
- **Actor columns** — `actor_user_id`, `actor_email`, `actor_role` (denormalized at event time).
- **Target** — `action`, `entity_type`, `entity_id`, optional `before` / `after` JSONB snapshots, `metadata`, `request_id`.
**Note:** The repository may also contain legacy `audit_log` / trigger-based patterns. The Audit Log Manager should target the **new** `audit_events` model from the implementation guide once migrated; until then, scope API field mapping explicitly so the UI contract does not depend on legacy tables.
---
## 3. Admin API design
### 3.1 Endpoint
- **`GET /api/v1/admin/audit`** — admin role check on router **and** service layer (mirror implementation guide §8).
### 3.2 Query parameters (time + filters)
| Parameter | Purpose |
|-----------|---------|
| **`from`** | Inclusive lower bound on `occurred_at` (ISO-8601 / RFC3339, timezone-aware). |
| **`to`** | Exclusive or inclusive upper bound (pick one, document in OpenAPI; default: *inclusive* end-of-day if date-only strings are allowed). |
| **`actor_user_id`** | Filter by user UUID (strong identifier for “this users timeline”). |
| **`actor_email`** | Partial or exact match per product choice; prefer **exact** for predictable forensic use. |
| **`entity_type`**, **`entity_id`** | Narrow to one records history. |
| **`action`** | One or many `audit_action` values (`create`, `read`, `update`, `delete`, `login`, `logout`, `login_failed`). |
| **`request_id`** | Optional: correlate all events from one HTTP request. |
| **`page`**, **`page_size`** | Paginated results; cap `page_size` (e.g. 100) for performance. |
| **`sort`** | Default **`occurred_at,desc`**; optional `asc` for chronological “playback” within a window. |
### 3.3 Response shape
Each item should expose at minimum:
- `id`, `occurred_at`, `actor_user_id`, `actor_email`, `actor_role`, `action`, `entity_type`, `entity_id`, `metadata`, `request_id`, and flags or URLs indicating presence of `before` / `after` (full JSON may be omitted from list rows and loaded on detail fetch if payloads are large).
Optional follow-up:
- **`GET /api/v1/admin/audit/{id}`** — full row including `before` / `after` for the detail panel.
### 3.4 Query strategy
- Always constrain by **`occurred_at`** when the admin does not pass `from`/`to` — e.g. default **last 24 hours** or **last 7 days** to avoid full table scans.
- Use existing indexes: `(actor_user_id, occurred_at DESC)`, `(entity_type, entity_id, occurred_at DESC)`, `(action, occurred_at DESC)`.
---
## 4. Admin UI — Audit Log Manager
### 4.1 Placement and access
- Route: **`/dashboard/admin/audit`** (or equivalent admin layout path).
- Wrapped in **`ProtectedRoute`** (or equivalent) **admin-only**; no feature flags that downgrade the check in production.
### 4.2 Layout
1. **Filter bar** — time range, user (email or ID), entity type/id, action multiselect, optional free-text on `metadata` paths (defer to v2 unless trivial; GIN on `metadata` supports it later).
2. **Results table** — columns: **Time** (`occurred_at`), **Actor** (email + role), **Action**, **Entity** (type + id), **Summary** (one-line from metadata or action), **Request** (link/filter by `request_id` if present).
3. **Detail drawer / panel** — on row click: show full metadata, **`before` / `after`** side-by-side; **diff in the browser** with a JSON diff library (per implementation guide — do not compute diff on server).
### 4.3 Time-centric UX presets
Presets speed up monitoring:
- Last 24 hours, last 7 days, last 30 days, custom range (date-time pickers with timezone label).
- **“This users activity”** deep link: `/dashboard/admin/audit?actor_user_id=…&from=…&to=…`.
### 4.4 Monitoring-oriented views (v1 vs later)
| View | v1 | Later |
|------|----|--------|
| Paginated event list with time filters | Yes | — |
| Sort by `occurred_at` | Yes | — |
| Per-user timeline (same data, fixed `actor_user_id`) | Yes | Optional density / grouping by day |
| Per-entity history | Yes | — |
| Export (CSV/JSON) for compliance | Optional | Regulated customers often need it |
| Live / websocket tail | No | Only if product requires real-time |
---
## 5. Timestamp semantics
- **Storage:** `TIMESTAMPTZ` in PostgreSQL; all APIs use ISO-8601 with offset or `Z`.
- **Display:** Convert to admin-visible timezone; show UTC in tooltip or secondary row for disputes.
- **Day boundaries:** If the UI sends date-only `from`/`to`, define explicitly (e.g. start of day / end of day in chosen timezone, then convert to UTC for the query).
---
## 6. Privacy, security, and correctness
- **Admin-only** on UI and API; verify with automated tests.
- **Do not** log secrets in `before`/`after` (enforce `to_dict()` hygiene per implementation guide §5.1 and §8).
- **Failed login** events: `actor_user_id` null, `actor_email` = attempted address; UI must not imply existence of accounts beyond what policy allows (align with auth UX).
- **Read volume:** Applicant read-audit is mostly suppressed by design; admins see **all** reads they are allowed to see — train admins that “quiet” applicant timelines are expected.
---
## 7. Implementation phases (aligned with implementation guide §7)
| Phase | Deliverable for Audit Log Manager |
|-------|-----------------------------------|
| **After Step 1** | Stub UI + API returns empty or mock; contract freeze. |
| **After Steps 24** | Real data for auth, user admin, initiative writes; filters and table usable for forensic queries. |
| **After Step 5** | Read events appear per role rules; admin sees broad read coverage. |
| **Step 6 (shipping target)** | Full **Audit Log Manager** as specified here: filters, pagination, detail panel, time presets. |
| **Step 7** | Retention/partitioning — Manager may add UI notice “data older than X may be purged per policy.” |
---
## 8. Acceptance criteria (Audit Log Manager)
- [ ] Admin cannot access `/api/v1/admin/audit` as non-admin (403).
- [ ] Listing is **ordered by `occurred_at`** descending by default; ascending works for a bounded window.
- [ ] Filtering by **`from` / `to`** reduces rows correctly and uses indexed columns.
- [ ] Filtering by **`actor_user_id`** or **`actor_email`** returns only that actors events.
- [ ] Row detail shows **`before` / `after`** without server-side diff; client-side diff renders correctly for large JSON (smoke test with sample initiative snapshot).
- [ ] Time preset “last 7 days” matches server interpretation (document timezone).
- [ ] No secrets appear in exported or displayed JSON (spot-check against `to_dict()` tests).
---
## 9. References
- Primary spec: [`assets/docs/audit-log-implementation.md`](../assets/docs/audit-log-implementation.md) — sections 2 (schema), 4 (`record_audit`), 5 (where writes happen), 6 (what to log per role), 7 Step 6 (admin viewer), 8 (verification).