Skip to content

RLS: finish the org-context sweep and make ione_app a supported runtime role #25

Description

@mortonanalytics

PR #23 (migration 0050, src/rls.rs) made row-level security provably enforceable, but only partially adopted. This issue closes the remainder.

What already works

  • FORCE ROW LEVEL SECURITY on all 11 org-scoped tables.
  • Restricted role ione_app — non-SUPERUSER, non-BYPASSRLS.
  • src/rls.rs::org_scoped_tx sets app.current_org_id per transaction.
  • tests/rls_enforcement_integration.rs proves isolation by reading broker_credentials with no org_id predicate, so RLS is demonstrably the only filter.

Migrated: WorkspacePeerCredentialRepo::{get,list_for_workspace,delete}, WorkspacePeerDelegationRepo::{get,delete}, BrokerCredentialRepo::{create_pending,list_for_user,find_for_user,store_tokens,delete}.

What remains

1. The deployment everyone runs still bypasses RLS. DATABASE_URL, docker-compose.yml, CI and the dev loop connect as ione, which is SUPERUSER + BYPASSRLS. Postgres lets such a role past row security unconditionally — FORCE does not apply to it. On that connection the application-layer WHERE org_id = $n is still the only guard.

2. ione_app is not yet a supported runtime role. Under it, unmigrated paths fail closed, not open — but they do fail:

  • a fresh connection sees NULL and matches no rows;
  • a recycled pooled connection sees '', and ''::uuid raises 22P02 rather than returning NULL.

No leak in either shape (both pinned by the test), but an unmigrated write would silently affect zero rows. Cutting over requires finishing (3) first.

3. Repos and methods that never set the org context.

  • No org id in hand, so they structurally cannot: WorkspacePeerCredentialRepo::{upsert,secret_for}, WorkspacePeerDelegationRepo::{upsert_tokens,update_refreshed,material_for,begin_pending,consume_pending}, BrokerCredentialRepo::{find_by_state,consume_by_state,find_user_provider}. secret_for/material_for are reached from src/services/peer_tokens.rs and need the caller to thread an org id.
  • Entirely unmigrated: peers, workspace_peer_bindings, service_account_tokens, auto_exec_policies, interaction_events, MFA tables, identity-audit writers. Their tables carry FORCE; their queries never set context.

service_account_tokens is called out separately in md/design/headless-provisioning.md (HP-M4).

4. Consider current_setting(..., true)::uuid handling. The ''22P02 shape is fail-closed but noisy. A NULLIF(current_setting(...), '')::uuid form would fail closed quietly. Deliberately not changed in #23 to keep that PR surgical.

Acceptance

  • Every repository touching an org-scoped table sets the context, or is documented as intentionally exempt with a reason.
  • The app can run as ione_app end-to-end with the full suite green.
  • md/design/identity-broker.md AC-15 can be marked satisfied without qualification.

Context: PR #23, md/design/identity-broker.md "RLS activation".

Metadata

Metadata

Assignees

No one assigned

    Labels

    federationIONe federation / peer integration

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions