TML-2503: service_role admin access to Supabase-internal namespaces (slice D)#845
TML-2503: service_role admin access to Supabase-internal namespaces (slice D)#845wmadden-electric wants to merge 2 commits into
Conversation
…ia the facade db.asServiceRole().sql.auth.users (and storage.*) is now queryable on the service-role-bound db — the admin path. asUser/asAnon expose only the app namespaces (auth.* absent from their type). The facade composes the extension contract's namespaces into the service_role execution context (facade-local: a second SupabaseRuntimeImpl sharing the app runtime's driver/pool). No framework changes; no new /contract export. - runtime: WithExtensionNamespaces<T>, buildServiceRoleContract, serviceRoleRuntime - example tests: asServiceRole reads auth.users (SQL targets "auth"."users", current_setting(role)=service_role); type-level test (app contract) proves asServiceRole gains auth/storage while asAnon/asUser do not - docs: overview.md corrected (admin path vs common FK+RLS path; anon/authenticated lack auth.* grants), decision C15 (cross-space-querying boundary + entrypoint), plan.md slice D rewritten Reviewed (opus): APPROVE-WITH-NITS; nits applied (non-vacuous type test, role assertion pinned, stale-hash comment). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com>
@prisma-next/extension-author-tools
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-cache
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/extension-postgis
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/extension-supabase
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/cli-telemetry
@prisma-next/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
size-limit report 📦
|
Mirror the .sql coverage on the .orm surface (the whole role-bound db is
parameterized with the merged contract, so .orm projects the internal
namespaces the same way .sql does — verified against OrmClient, not assumed):
- integration: db.asServiceRole().orm.auth.AuthUser.select(...).first({id})
reads the same seeded auth.users row (whole-shape assertion)
- type-level: asServiceRole().orm has auth/storage; asAnon()/asUser().orm do not
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (4)
📒 Files selected for processing (4)
📝 WalkthroughWalkthrough
ChangesService-role extension namespace surface
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Linked issue
Refs TML-2503. Slice D of the extension-supabase project. Builds on the merged slice A (#839). Slice contract:
projects/extension-supabase/slices/d-service-role-internal-namespaces/spec.md.At a glance
The
service_role-bound db can now query Supabase-internal tables — the admin path — while app roles can't see them:Decision
auth.*/storage.*are exposed only on theservice_role-bound db, flat (asServiceRole().sql.auth.users).asUser/asAnonkeep exactly today's surface (app namespaces only;auth.*absent from their type). The capability is bound to the one role that actually holds the grant.This is the considered answer to "how does an admin reach extension-owned tables" — preferred over making users hand-construct a second db from the extension's contract. Full rationale + the Supabase permission reality (direct-connection
service_rolecan readauth.*; the "you can't" advice is PostgREST-only) is recorded in umbrella decision C15; the boundary (cross-space querying off the app db was deliberately not built) is part of the same decision.How it works
The facade composes the Supabase extension contract's namespaces into the
service_roleexecution context itself — facade-local, since the package ships both contracts. It does not use generic cross-space querying (deliberately not built in cross-contract-refs). Concretely: a secondSupabaseRuntimeImplbuilt from the merged contract that shares the app runtime's driver/pool (one facade, one pool, two internal runtimes differing only by contract). Role binding is the inherited session-coupled-connection path —set_config('role'='service_role', …)+RESET ALL— unchanged.Behavior changes & evidence
asServiceRole().sql.auth.usersis queryable; app roles aren't.packages/3-extensions/supabase/src/runtime/supabase.ts(WithExtensionNamespaces,buildServiceRoleContract,serviceRoleRuntime). Evidence:examples/supabase/test/explicit-namespace-query.integration.test.ts— reads a seeded row, asserts emitted SQL targets"auth"."users"andcurrent_setting('role') = 'service_role'.examples/supabase/test/service-role-namespaces.test-d.ts(typed against the real app contract) —asServiceRole().sqlgainsauth/storage;asAnon()/asUser().sqldo not have them.Reviewer notes
storageHash) are applied.SupabaseRuntimeImpllowers against its own contract; they share the driver, so no second pool). The merged service-role contract intentionally keeps the app'sstorageHash(commented at the merge site) so the read-only marker check still matches./contractexport;asUser/asAnonbyte-for-byte unchanged.Verification
pnpm exec turbo run typecheck test lint --filter @prisma-next/example-supabase --filter @prisma-next/extension-supabaseon the rebased tree — 39/39 tasks pass; example suite 7/7 (incl. the two new Slice D tests).Checklist
git commit -s).asServiceRolenamespace widening (covered in decision C15 + overview.md); no CLI/config/error-code change.Summary by CodeRabbit
New Features
Tests