feat(security): route more self-service reads through begin_with_user (BUNYIP-344) #364
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/BUNYIP-344-route-remaining-selfservice-reads"
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?
What
Follow-up to the BUNYIP-344 phase-1 slice (#363, merged): route two more genuinely self-service reads through
begin_with_user, so per-user RLS applies to them once the NOBYPASSRLS role is provisioned.handlers/download.rs, underMemberUser): the caller's ownapplication_entitlementsread now runs inside abegin_with_usertransaction.handlers/user.rs, underAuthenticatedUser): the ownership lookup and the revokeUPDATEnow share onebegin_with_usertransaction, so on the isolating role a device owned by another user is invisible (lookup returnsNone-> 404) and theUPDATEcan only touch the caller's own row. The in-Rust ownership check is kept as defence in depth.The repo methods (
EntitlementRepository::active_application_ids,TrustedDeviceRepository::find_by_id/revoke) were widened from&PgPooltoimpl PgExecutor, which is backward compatible (&PgPoolalready satisfies the bound), so no other caller changes.Deliberately not routed
user_totpandrecovery_codesreads flow throughTotpService, andemail_change_requestsreads are by-hash - all shared with the pre-auth login / 2FA / confirm flows where there is no current-user GUC. Routing them throughbegin_with_userwould fail-close authentication on the isolating role, so they stay on the primary system pool with their application-levelWHERE user_idfilters. DecouplingTotpServicefrom the login path is a separate, larger refactor.Verification
just check-containergreen (fmt + clippy -D warnings + 102 tests).application_entitlements, proving user A cannot see user B's entitlement with a crafted no-WHERE query, alongside the existingtrusted_devicesread/write guards. The DB-backed test skips whenRLS_TEST_DATABASE_URLis unset, so the no-Postgres CI stays green.Depends on / relates to
Activation still needs BUNYIP-360 (provision the
bunyip_appNOBYPASSRLS role + setAPP_DATABASE_URL). Until then these paths use the primary-pool fallback and the change is a runtime no-op.🤖 Generated with Claude Code