feat(audit): add platform security audit log

New audit_events table, distinct from the Provisioner's environment_events (Lab-lifecycle
only). recordAudit() is called from the auth/RBAC/admin layers added in this phase — login
success/failure, logout, password change, instructor CRUD, role changes, session
revocation, cohort/roster changes, curriculum assignment, Prepare/Destroy Labs, and every
403 authorization denial (recorded centrally by the RBAC middleware, not per-route). Never
records plaintext passwords, raw session tokens, Provisioner secrets, or raw routing tokens.
This commit is contained in:
2026-08-18 13:36:11 -05:00
parent 85fa920f96
commit 37230d4ef1
2 changed files with 40 additions and 0 deletions
@@ -0,0 +1,20 @@
-- audit_events — platform-level security/admin audit log, distinct from the Provisioner's
-- environment_events (which is Lab-lifecycle-only). Never stores plaintext passwords, raw
-- session tokens, Provisioner secrets, or raw routing tokens — see src/audit.js callers.
CREATE TABLE IF NOT EXISTS audit_events (
audit_event_id BIGSERIAL PRIMARY KEY,
actor_type TEXT NOT NULL, -- 'instructor' | 'student' | 'system'
actor_id TEXT, -- instructor_id or student_id as text; NULL for e.g. a failed login with no matching account
action TEXT NOT NULL,
resource_type TEXT,
resource_id TEXT,
result TEXT NOT NULL, -- 'success' | 'failure'
source_ip TEXT,
metadata JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_events(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit_events(actor_type, actor_id);
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_events(action);