Skip to main content

Module org_scope

Module org_scope 

Source
Expand description

Org-tree request scope for the entitlement-union RLS fence (ADR-0028/0029).

ADR-0028 replaces the single company_id scoping key with org_unit_id: one org tree per tenant database (root / company / branch nodes), and a session sees the UNION of the subtrees under every node it is entitled to — always including the root node, which owns tenant-wide shared rows. The database half is the policy org_unit_id = ANY(string_to_array(current_setting('app.scope_unit_ids', true), ',')::uuid[]); this module is the application half that resolves a session’s entitled ids and carries them for the duration of a request.

Session variables set by an org request scope, in full:

  • app.scope_unit_ids — the entitlement-union fence (read by org policies).
  • app.company_id — the legacy equality fence during the re-key transition, resolved from the acting node’s company ancestry.
  • app.acting_unit_id — where new records land: the column DEFAULT nullif(current_setting('app.acting_unit_id', true), '')::uuid on decorated tables (ADR-0029) resolves INSERTs that omit org_unit_id. Unset/empty → NULL → NOT NULL violation: an insert outside a scope fails loud, never silently unscoped.
  • the six app.* audit variables of crate::audit_context (actor, correlation id, request facts) when the request carries a RequestAuditContext — set by the audited twin with_org_request_scope_and_audit, on the same request-dedicated connection, so the auditlog capture function’s triggers read attribution off every write of the request.

During the module-by-module re-key both fences are live at once: some tables still read app.company_id (ADR-0008 equality fence), org-re-keyed tables read app.scope_unit_ids. with_org_request_scope therefore sets ALL THREE session variables on one request-dedicated connection. When the last company-fenced table is re-keyed, the legacy bridge retires with the old fence.

The scope binds the same REQUEST_CONN task-local as with_request_scope: every scoped execute helper in this crate — and therefore every generated repository — runs on that connection and inherits the fence variables without a single call-site change.

The task-local is not the fence. RLS is. Unscoped statements see the variables unset and match zero rows — fail-closed, identical to the ADR-0008 contract.

Structs§

OrgScope
A resolved session scope over the org tree (ADR-0028).

Enums§

OrgScopeError
Failures of resolve_org_scope — all of them mean “do NOT open a scoped session”.

Functions§

bind_org_scope_on
Bind an explicit org scope onto an already-open transaction/connection, transaction-locally.
current_org_scope
The resolved org scope of the current request, if one is bound.
execute_scoped
Tenant-agnostic execute for hand-written module SQL (ADR-0029): ride the request-dedicated connection when one is bound — carrying whatever fence variables the COMPOSING service’s scope set (with_org_request_scope / with_request_scope) — otherwise execute plainly on the pool.
execute_unit_scoped
Statement-level org fence for a write whose row belongs to one org node — the hand-written repository path for callers outside a request scope.
fetch_all_rows_scoped
Tenant-agnostic fetch_all for an untyped row query — the set-returning sibling of fetch_optional_row_scoped, same connection discipline: request-dedicated connection when bound, plain pool otherwise, no scope invented. Reads that must ride the request connection (a bare-pool read under a decorated deployment lands on a fresh connection with no fence variables and returns nothing) but owe no company predicate reach for this, not the company-scoped helpers.
fetch_one_row_scoped
Tenant-agnostic fetch_one for an untyped row query — the single-row sibling of fetch_optional_row_scoped, same connection discipline: request-dedicated connection when bound, plain pool otherwise, no scope invented.
fetch_optional_row_scoped
Tenant-agnostic fetch_optional for an untyped row query — the read twin of execute_scoped, same connection discipline: request-dedicated connection when bound, plain pool otherwise, no scope invented.
resolve_org_scope
Resolve a session’s scope from the org tree.
with_org_request_scope
Run f with a request-dedicated connection carrying the session’s org scope.
with_org_request_scope_and_audit
with_org_request_scope plus the request’s audit attribution (ADR-0025): the six RequestAuditContext variables are bound on the same request-dedicated connection, so every write of the request — including the ones that fire the auditlog capture function’s triggers — reads the same actor and request facts off the connection it rides.