What this is about
Several parts take part in every request, and each answers different questions. If you know which part is responsible for what, you look for a bug in the right place and put a rule where it belongs.
The chain
flowchart LR
K["Keycloak<br/>logs in,<br/>issues token"] --> T(["Token<br/>carries tenant,<br/>roles, attributes"])
C["CIAS<br/>manages tenants, roles,<br/>groups, attributes"] -- "writes permissions" --> K
T --> F["CIAS filter chain<br/>checks token and tenant"]
F --> D["CDMS<br/>role → operation,<br/>attribute → rows"]
Read it like this: CIAS writes roles, groups and attributes to Keycloak. Keycloak puts them into the token at login. On every request the CIAS filter chain checks the token and the tenant. After that CDMS decides what the roles allow on the data.
The filter chain is CIAS code, but it runs in front of CDMS in the same program, even when CIAS itself runs as a separate service. See Embedded and standalone compared.
The four parts
- shows the login page
- checks password and MFA
- holds the session
- issues and signs tokens
- manages users, tenants, roles, groups, attributes
- decides who may grant a permission
- is the only one that writes permissions into Keycloak
- checks token and tenant on every request
- carries person, tenant, roles, attributes
- is valid until it expires
- its roles are not re-checked with Keycloak on a request
- checks whether a role allows an operation on a model
- filters rows by attributes and owner
- picks the tenant's database
- calls the project's hooks
What a role allows in CDMS is defined in the model. You set it in the hub, and the build generates the code from it. CIAS deliberately does not know this table. See The two permission matrices.
Which question each part answers
Logging in
| Question | Responsible | More |
|---|---|---|
| Is the password right, is MFA satisfied? | Keycloak | Logging in with the browser |
| Who issues and signs the token? | Keycloak | Logging in with the browser |
| Is the token genuine and not expired? | CIAS filter chain, without asking Keycloak | What happens to the token on every request |
| Does the account exist, is it enabled? | Keycloak, CIAS aligns with it | CIAS as the bridge to the identity provider |
| Who is the person in business terms, which tenant do they belong to? | CIAS | The user record |
Tenant
| Question | Responsible | More |
|---|---|---|
| Which tenants exist, and what state are they in? | CIAS | The life of a tenant |
| Which tenant does this request run in? | CIAS filter chain, from the token | Determining the tenant of a request |
| Is this tenant currently being served? | tenant gate in the filter chain, CIAS gives the answer | Admitting the tenant (tenant gate) |
| May the person switch to another tenant by header? | CIAS filter chain checks the realm role, CDMS checks the target against the allowed tenants | Tenant switch by header |
| Which database does the access go to? | CDMS, from model level and tenant | Which database? The persistence target |
Permissions
| Question | Responsible | More |
|---|---|---|
| Which roles and attributes exist? | the module declares them, CIAS keeps the catalog | Modules declare their roles |
| Who may grant a role? | CIAS: delegation and ceiling | Granting a role |
| Who writes roles, groups and attributes into Keycloak? | only CIAS, through its adapter | CIAS as the bridge to the identity provider |
| Which roles apply to exactly this request? | CIAS filter chain builds the effective roles from the token | Effective roles: global or in the tenant |
| Which value does an attribute have in this tenant? | token for USER, CIAS for USER_IN_TENANT | One value per person or per tenant |
| When does a revoked permission take effect? | the token: with the next new token | Why revoking a permission takes effect with a delay |
Data
| Question | Responsible | More |
|---|---|---|
| Which role does an operation on a model require? | the model in the hub, and the code generated from it | How role names are built |
| May this role read, create, change, delete the model? | CDMS, model role | Model roles |
| May the person go through this field into another model? | CDMS, role of the target model or field role | Permissions on relations (field roles) |
| Which rows does the person see? | CDMS: owner filter, attribute filter, custom filters | The three levels at a glance |
| What else should happen in business terms when saving? | the hook of your project | Hooks: kinds and points in time |
Tracing back
| Question | Responsible | More |
|---|---|---|
| Who gave whom which role and when, who suspended the tenant? | CIAS, audit | One trail for everything |
| What did an object look like earlier, and who changed it? | CDMS, history | Reading the history |
One request, station by station
The same split shows up along the path of a read request. Each station shows which part decides:
-
Filter chainCheck tokenIs Keycloak's signature genuine, is the token not expired?↳ no 401
-
Filter chainResolve tenantWhich tenant is in the token, is it unambiguous?↳ no 403
cias.authentication.tenant-unresolved -
CIASTenant gateIs this tenant being served?↳ no 403
cias.authentication.tenant-not-served -
Filter chainEffective roles and attributesWhich roles and attribute values apply in this tenant?
-
CDMSModel roleDoes one of the roles allow reading
employee?↳ no 403missing-permission|<role> -
CDMSRow filterWhich rows pass the owner filter, attribute filter and custom filters?↳ no row is missing from the list
-
HookHookafter hook with operation
READ, before the response - The allowed rows come back
Keycloak itself appears only once on this path: the filter chain exchanges the token at Keycloak for one for the CIAS client, or takes it from its cache. It does not ask again whether password and roles are right.