Skip to content

Decide RLS's role in this backend — service-role key bypasses it for every table #551

Description

@CRamsan

Follow-up from #356 / #549 — reworded after the original premise turned out to be wrong.

Original premise (incorrect)

This issue originally asked to org-scope user_property_mapping's RLS policy, on the assumption that RLS provides real protection for this table the way it (supposedly) would for properties.

Corrected understanding

While implementing #549, we confirmed the backend's Postgrest client is a single Koin-singleton SupabaseClient built from a service-role key (DatastoreModule.kt), shared across every datastore, with no per-request JWT impersonation anywhere. Service-role keys bypass RLS by design (BYPASSRLS) — confirmed explicitly in this repo's own migration comments. The front-end has no Postgrest client at all; every table access goes through this same backend, which always uses this same key.

RLS is currently inert for every table in this database, not just user_property_mapping. Org-scoping this table's policy (or any table's) would add no actual protection under the current architecture. The real and only enforcement today is application-layer RBAC (RBACService.hasRoleOrHigher) in each controller.

The actual open question

Rather than "fix this one table's RLS," the real decision is architectural:

  • Option A — RLS is not meant to be a security boundary here; application-layer RBAC is the intended and sufficient model. In that case, blanket-allow policies on backend-only tables are expected, not a gap, and this issue can be closed with no code change.
  • Option B — RLS is intended as defense-in-depth (protecting against service-role key leakage, a future direct-to-Postgres path, or Supabase dashboard/SQL-editor misuse). In that case, making it actually effective requires moving the backend from a single shared service-role client to per-request JWT-forwarding/impersonation against Postgrest — a meaningfully larger architectural change than adding one policy, and one that would apply to every table, not just this one.

This issue is intentionally left open as that decision, not as a specific fix to implement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions