CodamAIDocs
Topicdone

SINGLE and MULTI across both modules

What the data storage operating mode does in CDMS and in CIAS, and that the same switch also decides whether the tenant in the token is checked.

Variants
SINGLEMULTIswitch missingone switch for data storage and tenant check

What this is about

An installation either has tenants or it does not. Which of the two applies is stated in one setting:

codamai.persistence.tenant.mode = SINGLE | MULTI

as the environment variable CODAMAI_PERSISTENCE_TENANT_MODE. A tenant is a customer whose data has to stay separate from the data of other customers.

Why not two separate settings? Because two settings could contradict each other. A contradiction would then look like “some requests are refused and some are not” instead of the configuration error it is.

One switch, several readers

flowchart TB
    S{{"codamai.persistence.tenant.mode"}}
    S --> P["Database access (CDMS)<br/>which database a request reaches"]
    S --> F["File storage (CDMS)<br/>one directory per tenant or not"]
    S --> V["Provisioning (commons-persistence)<br/>create a database or do nothing"]
    S --> K["Filter chain (CIAS)<br/>resolve tenant, ask the gate, refuse without one"]
    S --> M["/cias/me (CIAS)<br/>reports whether a tenant is required"]

Note that the operating mode says nothing about whether one particular tenant may be served right now. That is the answer of the tenant gate and is fetched again on every request.

The matrix

What the operating mode does where
SINGLEMULTI
CDMS: databasesone, called singleone system database plus one per tenant
CDMS: where tenant and user models gointo the same database as the system modelsinto the database of the request's tenant
CDMS: file storageone directory tree, without a tenant levelone directory per tenant
CDMS: a new tenantnothing to set up, the tenant is active right awaycreate and migrate a database
CIAS: the tenant in the tokenskipped, neither checked nor refusedresolved, from an organization or an attribute
CIAS: tenant gatenever askedasked for every resolved tenant
CIAS: request without a tenantadmitted403 tenant-required, except on /cias/**
CIAS: list of allowed tenantsstays emptyown tenant plus organizations
CIAS: header tenantdoes nothing, even with the roleallows a switch into a tenant on that list
CIAS: roles from an organizationdo not applyapply in the selected tenant
CIAS: keeping tenantspossible, but affects no requestpossible and necessary

The details are on the two module pages, and that is where they belong:

The same switch checks the tenant in the token

This is the part that surprises people. The setting is named after persistence (codamai.persistence.tenant.mode), yet CIAS reads it too, although CIAS has no dependency on the CDMS persistence at all. It reads it as plain text and compares it against two words.

From it CIAS derives two different questions:

What CIAS asksAnswer with MULTIAnswer with SINGLEAnswer when nothing is set
Must an authenticated request name a tenant?yesnono
Should the tenant in the token be skipped?noyesno

The third column is not a typo. Not set is not the same as SINGLE. An installation without this setting is one that carries no CDMS persistence at all — a CIAS administering identities for somebody else’s storage. It evaluates tenants in the token as usual and asks the gate, but it does not require every request to have a tenant. There is no database choice a missing tenant could violate.

Where the CDMS persistence is present, the setting cannot be absent: it is declared mandatory there, and without it the application does not start.

The standalone CIAS service sets the value itself, to SINGLE, unless the installation says otherwise (CDMS_TENANT_MODE). The reason: an installation usually administers across all of its tenants with one Keycloak. MULTI is for the operator who runs several instances and wants to tie each one to its tenant.

The three states of the setting
ValueCDMS persistence in the process?What happens
MULTIyesseparation per tenant, a tenant in the token is mandatory
SINGLEyesone database, the tenant in the token is skipped
not setyesstart fails, the message names the setting
not setnotenants are evaluated but not required

Operating mode and topology are two different things

This page sits in the chapter about operating modes, and the word “operating mode” carries two meanings here. Keep them apart:

SINGLE / MULTI
operating mode of the data storage
  • Does this installation have tenants?
  • stated in codamai.persistence.tenant.mode
  • holds for the lifetime of the installation
embedded / standalone
topology of the deployment
  • Does CIAS run in the same process as CDMS or as a separate service?
  • stated in codamai.cias.tenancy.lookup and in the system's cias field
  • changes only how CDMS and CIAS talk to each other

All four combinations are possible. An embedded CIAS in MULTI checks the tenant through a method call, a standalone one over HTTP — what is checked is the same in both. And in SINGLE neither topology asks at all, because there is nothing to ask. See Embedded and standalone compared.

Watch out

Next

Sources in the code and the knowledge base
  • commons-persistence – TenantProperties (codamai.persistence.tenant.mode, @NotNull), TenantMode, DataSourceManager.getConnection, TenantEntityManagerFactory.cacheKey (single), DatabaseRequestContext.resolveTenant, TenantProvisioningConfiguration (NoOp with SINGLE), TenantModeConsistencyCheck
  • CDMS/cdms-localfs-storage – LocalFSFileController.getPathForCurrentTenant (isMultiTenant)
  • CIAS/cias-kernel – TenantRequirement (required, ignoresTenants, binds codamai.persistence.tenant)
  • CIAS/cias-authentication – TokenParser.admit (tenantless → TenantResolution.none), JwtSessionFilter.tenantMissing, TenantGate.admit
  • CIAS/cias-tenancy – CiasTenancyConfiguration (provisioning comes from commons-persistence)
  • CDMS/cdms-scaffold – CdmsReadmeWriter (MULTI/SINGLE in the generated project scaffold)
  • CIAS/cias-authentication/docs/adr – ADR-035; CIAS/cias-kernel/docs/adr – ADR-022
Search