Commit Graph

8 Commits

Author SHA1 Message Date
Hartmut fe79810a85 security: MFA backup codes — issue on enable, redeem at login, regenerate on demand (#43)
CI / Architecture Guardrails (push) Successful in 6m1s
CI / Assistant Split Regression (push) Successful in 6m52s
CI / Lint (push) Successful in 8m40s
CI / Typecheck (push) Successful in 9m45s
CI / Unit Tests (push) Successful in 7m28s
CI / Build (push) Failing after 10m16s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
Adds a one-time-use backup code set so users with a lost authenticator are not
locked out. Codes are Crockford base32 (XXXXX-XXXXX), hashed with argon2id, and
redeemed under a WHERE-guarded delete so a concurrent replay race fails closed.

- New MfaBackupCode model + migration
- Issue 10 codes inside the enable transaction; show plaintext exactly once
- Sign-in page accepts TOTP or backup code, reporting remaining count
- regenerateBackupCodes tRPC mutation wipes + reissues atomically
- Unit coverage for generator, normalizer, verify, redeem, and race path

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 18:47:18 +02:00
Hartmut e2dddd30df security: RBAC cache cross-instance invalidation + force re-login on role/perm change (#57)
- shrink roleDefaults cache TTL from 60s to 10s (safety-net staleness bound)
- publish/subscribe on capakraken:rbac-invalidate so peer instances drop
  their local role-defaults cache on mutation (ioredis pub/sub; lazy init
  so idle test files don't open connections)
- after updateUserRole/setUserPermissions/resetUserPermissions: delete
  all ActiveSession rows for that user so the next request re-auths via
  tRPC's jti check, and invalidate the role-defaults cache
- tests: peer-instance invalidation via FakeRedis pub/sub fan-out; mutation
  side-effects assert session deletion + cache invalidation on each path

Without this, demoted admins kept their JWT valid until expiry and peer
instances kept serving stale role defaults for up to the TTL window.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 13:01:15 +02:00
Hartmut 4ff7bc90c3 security: SSRF guard covers IPv6 + DNS-rebind defence via pinned IP (#49)
Expand the SSRF blocklist from IPv4-only to IPv6 loopback/ULA (fc00::/7)/
link-local (fe80::/10)/multicast/IPv4-mapped, plus the missing IPv4 ranges
0.0.0.0/8, 100.64.0.0/10 CGNAT, and TEST-NET/benchmark ranges. Replace the
single-lookup SSRF guard with resolveAndValidate(): resolves all DNS records
(lookup { all: true }) so a hostname returning "public + private" is
rejected, and returns the first validated address for connection pinning.

The webhook dispatcher now switches from plain fetch() to https.request()
with a custom Agent.lookup that returns the pre-validated IP. A DNS rebind
between the guard check and the TCP connect() can no longer redirect the
dial to an internal address. Hostname still flows through for SNI and
certificate validation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:19:07 +02:00
Hartmut dfeb4d361e fix(tests): align 20 drifted tests with current source behavior
Tests fell behind source changes: lastTotpAt replay-attack prevention,
activeSession invalidation on password reset, select clauses in
permission updates, UNAUTHORIZED (anti-enumeration) for disabled TOTP,
and password minimum raised from 8 to 12 characters.

Also fix root eslint.config.mjs to ignore packages/ (linted via turbo)
and add --no-warn-ignored to lint-staged to suppress warnings for
ignored files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:41:42 +02:00
Hartmut 5ad1048519 feat(dashboard): expand grid to 16 columns with auto-migration for saved 12-col layouts 2026-04-09 20:50:40 +02:00
Hartmut 41eb722369 feat: user invite flow, deactivate/delete, favicon, dashboard loading fix, admin full-width
- Invite flow: admin can invite users by email with role selection; accept-invite page
  sets password and creates the account; 72-hour token expiry; E2E tests
- User deactivate/reactivate/delete: new tRPC procedures + UI buttons; deactivation
  revokes all active sessions immediately; delete cascades vacation/broadcast records;
  isActive field added via migration 20260402000000_user_isactive
- Auth: block login for inactive users with audit entry
- Favicon: SVG favicon + ICO/PNG fallbacks (16, 32, 180, 192, 512px); manifest updated
- Dashboard: GridLayout dynamic-import loading skeleton prevents blank dark area
  on first login before react-grid-layout chunk is cached
- Admin users: remove max-w-5xl constraint so table uses full page width
- Dev: docker container restart workflow documented in LEARNINGS.md; Prisma generate
  must run inside the container after schema changes (named node_modules volume)

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-02 20:19:26 +02:00
Hartmut 99db52929f fix(api): harden user self-service and resource linking 2026-03-31 21:02:36 +02:00
Hartmut 4ce8577824 test(api): cover notification and user edge cases 2026-03-30 11:51:26 +02:00