feat(authz): integrate OPA fine-grained authorization middleware#94
Conversation
…acle656#86) Adds Open Policy Agent authorization evaluated on every request before handlers execute, with structured denial logging. - policies/authz.rego: Rego rules covering public paths (no token required), bearer-token-gated routes, admin-only prefixes, and rate-limit guard; each deny path emits a deny_reason + deny_rule for audit logs - policies/authz_test.rego: OPA unit tests for all allow/deny branches - src/middleware/opa.ts: Express middleware factory (createOpaMiddleware) that POSTs request context (path, method, token, role, user) to the OPA /v1/data endpoint; returns 403 with rule+reason on denial, 503 on OPA unreachable (fail-closed) - src/__tests__/opa.test.ts: Jest tests covering allow, deny, 503-on-unavailable, and custom extractor paths
|
@Depo-dev 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! 🚀 |
Miracle656
left a comment
There was a problem hiding this comment.
Good shape — the middleware + policy + tests are all real:
authz.rego: default-deny baseline (default allow := false), public-path allow set, bearer-token gate, admin-prefix gate, rate-limit guard. Every deny path emits bothdeny_reasonanddeny_ruleso logs are structured rather than free-text.authz_test.rego: 11 Rego tests covering public paths, authed routes (with/without token), admin paths (with/without role), and the deny-reason/rule values themselves. Good coverage.createOpaMiddleware: clean factory, POSTs{ input: {...} }to/v1/data/<package>, returns 403 with{ error, rule, reason }JSON body on deny. Closed-fail to 503 when OPA is unreachable — the right default for an authz dependency.opa.test.ts: mockshttp.requestat the constructor level, exercises allow/deny/503 paths plus the customgetRole/getUserextractors.
One note (not blocking the merge)
The middleware is available but not enforced — src/api.ts isn't modified to actually app.use(createOpaMiddleware()). The PR description claims "Every request evaluated against policy engine" as a satisfied criterion; that's slightly aspirational since the middleware is sitting in the codebase unwired.
That's actually the right call for a first landing — flipping default-deny on all consumers in the same commit would break every client not yet issuing bearer tokens. The follow-up to wire it in needs a coordinated rollout (a WRAITH_OPA_ENFORCE=true env-var gate, then flip after consumers migrate). Worth filing as a tracking issue when you decide to roll out.
Merging. Closes #86.
Summary
Integrates Open Policy Agent (OPA) for fine-grained API authorization (Issue #86).
Acceptance criteria
Test plan
Closes #86