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
| SINGLE | MULTI | |
|---|---|---|
| CDMS: databases | one, called single | one system database plus one per tenant |
| CDMS: where tenant and user models go | into the same database as the system models | into the database of the request's tenant |
| CDMS: file storage | one directory tree, without a tenant level | one directory per tenant |
| CDMS: a new tenant | nothing to set up, the tenant is active right away | create and migrate a database |
| CIAS: the tenant in the token | skipped, neither checked nor refused | resolved, from an organization or an attribute |
| CIAS: tenant gate | never asked | asked for every resolved tenant |
| CIAS: request without a tenant | admitted | 403 tenant-required, except on /cias/** |
| CIAS: list of allowed tenants | stays empty | own tenant plus organizations |
CIAS: header tenant | does nothing, even with the role | allows a switch into a tenant on that list |
| CIAS: roles from an organization | do not apply | apply in the selected tenant |
| CIAS: keeping tenants | possible, but affects no request | possible and necessary |
The details are on the two module pages, and that is where they belong:
- The CDMS view of data storage: SINGLE and MULTI
- The CIAS view of the token: In SINGLE the tenant in the token does not count
- What the filter chain does with the token: What happens with the token on every request
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 asks | Answer with MULTI | Answer with SINGLE | Answer when nothing is set |
|---|---|---|---|
| Must an authenticated request name a tenant? | yes | no | no |
| Should the tenant in the token be skipped? | no | yes | no |
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.
| Value | CDMS persistence in the process? | What happens |
|---|---|---|
MULTI | yes | separation per tenant, a tenant in the token is mandatory |
SINGLE | yes | one database, the tenant in the token is skipped |
| not set | yes | start fails, the message names the setting |
| not set | no | tenants 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:
- Does this installation have tenants?
- stated in
codamai.persistence.tenant.mode - holds for the lifetime of the installation
- Does CIAS run in the same process as CDMS or as a separate service?
- stated in
codamai.cias.tenancy.lookupand in the system'sciasfield - 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.