fix(auth): gate the bunyip path on user and tenant status #476
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/PMS-698-bunyip-principal-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The bunyip Resource-Server branch of
auth_middlewareresolved a placement, JIT-mirrored the users row, reconciled the role, and authenticated - without ever looking atusers.statusor the owning tenant's status. Only the legacy HS256 fallback ran those checks, so on the path the SPA actually uses a user flipped toinactive, or a member of a tenant suspended viaPOST /tenants/{id}/suspend, kept full read and write access to every/api/v1/*route until their bunyip token expired. Deactivation and tenant suspension were effectively no-ops in production.Extract the two checks into
AuthService::ensure_principal_usableand call it from both branches: the legacy branch throughensure_user_and_tenant_active(which keeps the PMS-681 password-change cutoff on top of it) and the bunyip branch fromplace_bunyip_user, which now returnsNoneon failure so the request ends unauthenticated instead of silently authenticating. Theiat-vs-password_changed_atcutoff deliberately stays legacy-only: bunyip owns the credential on the RS path, so a mokosh-side password change is not a revocation signal for a bunyip token.tests/bunyip_principal_gate.rsboots the real router with the RS verifier mounted against a stub OP (Ed25519 JWKS + userinfo, signed with the RFC 8032 test vector so no key-generation dependency is needed) and asserts that both a bunyip bearer and a legacy bearer for the same fixture go non-2xx onGET /api/v1/ticketsafter a tenant suspension and after a user deactivation.tests/bunyip_login.rsadds the matching function-level assertions thatplace_bunyip_userreturnsNonein both cases.#PMS-698
The bunyip Resource-Server branch of `auth_middleware` resolved a placement, JIT-mirrored the users row, reconciled the role, and authenticated - without ever looking at `users.status` or the owning tenant's status. Only the legacy HS256 fallback ran those checks, so on the path the SPA actually uses a user flipped to `inactive`, or a member of a tenant suspended via `POST /tenants/{id}/suspend`, kept full read and write access to every `/api/v1/*` route until their bunyip token expired. Deactivation and tenant suspension were effectively no-ops in production. Extract the two checks into `AuthService::ensure_principal_usable` and call it from both branches: the legacy branch through `ensure_user_and_tenant_active` (which keeps the PMS-681 password-change cutoff on top of it) and the bunyip branch from `place_bunyip_user`, which now returns `None` on failure so the request ends unauthenticated instead of silently authenticating. The `iat`-vs-`password_changed_at` cutoff deliberately stays legacy-only: bunyip owns the credential on the RS path, so a mokosh-side password change is not a revocation signal for a bunyip token. `tests/bunyip_principal_gate.rs` boots the real router with the RS verifier mounted against a stub OP (Ed25519 JWKS + userinfo, signed with the RFC 8032 test vector so no key-generation dependency is needed) and asserts that both a bunyip bearer and a legacy bearer for the same fixture go non-2xx on `GET /api/v1/tickets` after a tenant suspension and after a user deactivation. `tests/bunyip_login.rs` adds the matching function-level assertions that `place_bunyip_user` returns `None` in both cases. #PMS-698