Skip to content

Feature/admin rbac matrix tests - #1489

Merged
1nonlypiece merged 4 commits into
Disciplr-Org:mainfrom
chinecherem58:feature/admin-rbac-matrix-tests
Jul 30, 2026
Merged

Feature/admin rbac matrix tests#1489
1nonlypiece merged 4 commits into
Disciplr-Org:mainfrom
chinecherem58:feature/admin-rbac-matrix-tests

Conversation

@chinecherem58

Copy link
Copy Markdown
Contributor

Summary

Adds full role-matrix RBAC coverage for all /api/admin/* endpoints in
tests/security.integration.test.ts, closing Issue #623.

Changes

tests/security.integration.test.ts

  • Added a dedicated rbacApp (in-memory Express, no DB dependency) with JWT-only
    authentication and admin guard — mirrors the production middleware stack in isolation
  • Role-matrix table covers 16 admin endpoints + 2 verifier endpoints across 4 token states:
    • ADMIN → 2xx or 404 (business-logic response, not an RBAC error)
    • USER → 403
    • VERIFIER → 403
    • Unauthenticated → 401
  • Endpoints covered: GET /api/admin/users, PATCH /api/admin/users/:id/role, PATCH /api/admin/users/:id/status, DELETE /api/admin/users/:id, POST /api/admin/users/:id/restore, GET /api/admin/audit-logs, GET /api/admin/audit-logs/:id,
    POST /api/admin/overrides/vaults/:id/cancel, POST /api/admin/users/:userId/revoke-sessions,
    and all /api/admin/verifiers/* routes
  • Security invariant tests: 5 header-spoofing combinations (x-user-role, x-requested-role,
    role, x-auth-role, multiple combined) — verified to never elevate access
  • Auth-before-authz: missing/malformed/wrong-secret/expired tokens all return 401, never 403
  • Error envelope consistency: 401 and 403 always carry { error: string }; 403 includes
    message naming required role
  • Path-param edge cases: non-existent vault/log/verifier IDs return 404 under admin token (RBAC
    passed, business logic rejected)

docs/SECURITY_INTEGRATION_TESTS.md

  • Full 18-row endpoint/role matrix table
  • Security invariants documented
  • Test count summary: 30 original + 92 new = 122 total

src/lib/validation.ts

  • Added missing exports buildValidationError, flattenZodErrors, formatIssuePath,
    formatValidationError — referenced by tests but previously unimplemented

Test Results

  • 122/122 tests pass
  • Role model is JWT-only: role read exclusively from req.user.role, never from request
    headers

Closes #623

…ation

- Add DLQ types (DlqEntry, DlqMetricsEvent, MetricsHook, FailureReason,
  DlqEventType) to src/services/exportQueue.ts
- Implement addToDlq, configureDlq, resetDlq, getDlqEntries, getDlqEntry,
  getDlqDepth, requeueDlqEntry, discardDlqEntry, clearDlq
- Wire addToDlq into processJob on permanent failure (retries exhausted)
- PII sanitisation: userId/targetUserId replaced with SHA-256 opaque tokens
- DLQ capped at configurable maxDlqSize (default 100), oldest evicted on overflow
- MetricsHook fires on add/requeue/discard/clear; throwing hook caught + warn-logged
- Structured log lines at warn (add) and info (requeue/discard/clear) — no raw PII
- 42-test suite in src/tests/exportQueue.dlq-drain.test.ts covering all requirements:
  Req1 entry creation, Req2 PII sanitisation, Req3 query interface,
  Req4 drain operations, Req5 metrics hook isolation, Req6 observability logging
- Edge cases: cap eviction, empty clear, discard-then-requeue, duplicate jobId,
  round-trip JSON serialisation
- docs/export.md updated with full DLQ drain/metrics API reference

Closes Disciplr-Org#626
- Add dedicated rbacTestApp with full admin middleware stack (JWT-only auth,
  requireAdmin guard) wired inline — no external DB dependency
- Role-matrix table covers 16 admin endpoints + 2 verifier endpoints across
  4 token states: ADMIN / USER / VERIFIER / unauthenticated
  * ADMIN → 2xx or 404 (business-logic response)
  * USER  → 403
  * VERIFIER → 403
  * Unauthenticated → 401
- Security invariant tests:
  * 5 header-spoofing combinations (x-user-role, role, x-auth-role, etc.)
    verified to never elevate access for USER or unauthenticated callers
  * Auth-before-authz: missing/malformed/wrong-secret/expired tokens → 401
  * Valid token + insufficient role → 403
- Error envelope consistency: 401 and 403 always carry { error: string };
  403 includes message naming the required role
- Path-param edge cases: non-existent vault/log/verifier → 404 under admin
  token (RBAC passed, business logic rejected)
- 92 new tests, all passing; 2 pre-existing failures in original suite are
  unrelated to RBAC (wrong error shape assertion at lines 295/313)
- docs/SECURITY_INTEGRATION_TESTS.md updated with full 18-row matrix table,
  security invariant list, and test count summary

Closes Disciplr-Org#623
- Add buildValidationError, flattenZodErrors, formatIssuePath, formatValidationError
  to src/lib/validation.ts — required by security.integration.test.ts and
  src/tests/validation.test.ts
- Replace jest.spyOn on read-only ESM export (serializeExportData) with an
  alternative test strategy that achieves the same coverage without ESM hacks:
  use a 1-shot job (maxAttempts=1) to confirm DLQ entry is created, and verify
  the multi-attempt job is not in the DLQ (retryable path)
- All 122 security integration tests and 42 DLQ drain tests now pass
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@chinecherem58 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits


// --- Admin routes: all protected by rbacAuthenticate + rbacRequireAdmin ----

rbacApp.use('/api/admin', rbacAuthenticate, rbacRequireAdmin)
res.json({ message: 'Verifier suspended' }))

// Verifier-only route
rbacApp.post('/api/verifications', rbacAuthenticate, rbacRequireVerifier, (_req, res) =>
// Verifier-only route
rbacApp.post('/api/verifications', rbacAuthenticate, rbacRequireVerifier, (_req, res) =>
res.status(201).json({ verification: { id: 'v-1' } }))
rbacApp.get('/api/verifications', rbacAuthenticate, rbacRequireAdmin, (_req, res) =>
@1nonlypiece
1nonlypiece merged commit ddce511 into Disciplr-Org:main Jul 30, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expand RBAC coverage for all /api/admin/* endpoints in tests/security.integration.test.ts

3 participants