33 lines
2.3 KiB
Markdown
33 lines
2.3 KiB
Markdown
# Tenant Model and Row-Level Security
|
|
|
|
## Global vs Tenant-Private
|
|
|
|
- **Global objects:** Public or shared data that can be read across tenants (e.g. BIC, LEI, BIN range metadata). Stored with `tenant_id` null or a dedicated "global" tenant. Used for cross-tenant lookup when the rail has a public identifier scheme.
|
|
- **Tenant-private objects:** Participant-specific data, merchant IDs, terminal IDs, contractual endpoints. Always scoped by `tenant_id`. Queries must supply tenant (from auth or request) so that only that tenant's rows are visible.
|
|
|
|
## Enforcement
|
|
|
|
- **Postgres Row Level Security (RLS):** Enable RLS on `participants`, `identifiers`, `endpoints`, `capabilities`, `credentials`, `policies`, and optionally `routing_artifacts`. Policies: `tenant_id = current_setting('app.current_tenant_id')` or equivalent. Global rows: allow when `tenant_id IS NULL` or when reading public identifiers.
|
|
- **Application layer:** Resolver and Admin API must set tenant context (e.g. from JWT or request parameter) before querying. Never return rows from another tenant.
|
|
- **Per-tenant encryption:** For Tier 2+ data (see [data-classification](../security/data-classification.md)), use per-tenant encryption keys so that key compromise affects only one tenant.
|
|
|
|
## Caching
|
|
|
|
- Cache key **includes tenant**: same request for different tenants must not share a cache entry.
|
|
- Optional per-tenant TTL or invalidation rules (e.g. shorter TTL for high-churn tenants).
|
|
- Negative cache: key includes tenant; invalidate on any change for that tenant.
|
|
|
|
## RLS Policy Summary
|
|
|
|
| Table | Policy (conceptual) |
|
|
| ---------------- | -------------------------------------------------------- |
|
|
| participants | WHERE tenant_id = current_tenant OR tenant_id IS NULL |
|
|
| identifiers | JOIN participants; same tenant or global |
|
|
| endpoints | JOIN participants; same tenant |
|
|
| capabilities | JOIN participants; same tenant |
|
|
| credentials | JOIN participants; same tenant |
|
|
| policies | WHERE tenant_id = current_tenant |
|
|
| routing_artifacts| WHERE tenant_id = current_tenant OR tenant_id IS NULL |
|
|
|
|
Apply `current_tenant` from connection/session (e.g. set by API after auth).
|