CodamAIDocs
Topicdone

Rules that are never broken

The security invariants of tenant isolation: no fallback to the system DB, separate type sets, only three places may set a tenant.

What this is about

Tenant isolation is the most important security promise of CDMS: a customer never sees the data of another. For that to hold, there are a few rules that apply everywhere and have no exception. Such rules are called invariants.

This page collects them in one place. Every rule has a reason. Whoever writes custom code, a hook, a job or an extension, must not bypass any of them.

The checklist

No.RuleWhy
1No fallback to the system database. If the tenant is missing, the RequestContext is missing, or the tenant database cannot be opened, the access is aborted.Otherwise tenant data ends up in the system database and is reachable there for all tenants.
2System models always go to the system database, and this is decided before any check of the context.A missing or manipulated context can neither redirect nor block system data.
3Separate type sets. The system database knows only system models, a tenant database only tenant and user models.A misrouted object fails in Hibernate instead of being stored in the wrong database. That is a second safeguard, independent of routing.
4Only three places set a tenant: reading the token, the allowed tenant switch, and the frame for work without a request.Every further place would be a way to change the tenant past the checks.
5The tenant comes from a signed source. Headers are only wishes; the list of allowed tenants comes from the token.Any client can set a header.
6A switch is checked twice: in CIAS and once more in the persistence.If one check falls victim to a bug, the other holds.
7The gate asks about the tenant actually used, so once more after a switch.A switch must not bring back a suspended tenant, not even for administrators.
8No unconfirmed answer during an outage. If CIAS cannot be reached, only an already remembered answer counts; without one the request is refused.An outage must not let in anyone who was not in before.
9A database is created only with approval, and “unreachable” never counts as “missing”.A short outage or a typo in the key must not create a database.
10Nothing in CDMS deletes a database.A lost database cannot be recovered. An empty, orphaned one is the lesser evil.
11The tenant key is fixed: only a-z, 0-9 and -, at most 64 characters, not system or single, never renamed. It is checked, not sanitized.It is the database name and the directory name. Sanitizing could map two tenants to the same name.
12The operating mode has no default, and contradictory settings stop the start.A guessed value would silently switch a separation off or invent one.
13The RequestContext is cleared after every request.Otherwise the next job on the same thread carries tenant, person and roles of the previous request.

Forbidden fallbacks

These shortcuts do not exist and must not exist in custom code either:

Situation and what happens instead
SituationWhat happens
Tenant missing400 CDMS_TENANT_REQUIRED, or 403 tenant-required before that; never the system database
RequestContext missing500 CDMS_PERSISTENCE_CONTEXT_MISSING; never the system database
Tenant unknown or suspended403 tenant-not-served; never create automatically
Switch not allowed403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED; not silently continuing in the own tenant
Tenant database missing or unreachable500 or 503; never the system database
Token names several tenants without selection403 tenant-unresolved; never the first one or a default tenant

One exception is intended and is not a shortcut: if only the role is missing for a switch whose target is allowed, the request silently continues in the own tenant. The token did not demand a switch then; the client only wished for one. See Tenant switch by header.

The three places that set a tenant

Who may set the tenant of a request
Reading the token
every request with a token
  • determines the tenant from the organization or the attribute tenant
  • asks the tenant gate
  • fills the list of allowed tenants
Tenant switch
header tenant
  • only with the realm role allowed-tenant-context-switch
  • only to a target from the list of allowed tenants
  • afterwards the gate asks again
Work without a request
jobs, listeners
  • code names the tenant and the technical account it acts as
  • asks the tenant gate first
  • runs without roles and only in this one tenant

What the third way looks like is described in Working for a tenant without a request.

What this means for custom code

Where to go next

Sources in the code and the knowledge base
  • commons-persistence – DatabaseRequestContext.resolveTenant (system model first, no fallback, A-3 check), DataSourceManager (approval, unreachable ≠ missing), TenantModeConsistencyCheck, TenantProperties (@NotNull), TenantProvisioningPort (nothing deletes)
  • CDMS/cdms-persistence-database – EntityClassFilterService (separate type sets), EntityClassificationValidator (startup check)
  • CIAS/cias-authentication – TokenParser, ContextSwitch, TenantScope (the three places that call setUserTenant), TenantGate, JwtSessionFilter (context is cleared after every request)
  • CIAS/cias-kernel – TenantKey (^[a-z0-9-]+$, at most 64 characters, immutable)
  • documentation/30-daten-und-persistenz/01-mandantentrennung.md (forbidden fallbacks, separate type sets)
Search