CodamAIDocs
Topicdone

Support looks into a tenant

A platform employee switches into a customer tenant with a role and a header to reproduce a problem. What he sees and what he does not.

Variants
tenant switch onlytenant switch and user switch, own rolestenant switch and user switch, Ben's rolestenant role missing → silently ignoreduser role missing or Ben not in nordbau → 403Ben has not consented → 403requesting and approving a consenttarget not allowedtarget suspendedsupport is a member itself (selection)

What this is about

Ben from the tenant nordbau reports: “I cannot see my orders any more.” Lena works in platform support. Her own tenant is codamic, and she is not a member of nordbau. She should still be able to look, without the customer having to create an account for her.

There are two headers a client can send for this:

  • tenant: the request runs in a different tenant.
  • user: the request runs in the name of a different person. With the additional header user-roles: target, also with that person’s roles.

Anybody can set both headers, so it is not the header that decides but the signed token. Each of the two switches has its own realm role, that is, a role that applies in Keycloak across the whole platform and that no organization can grant.

What has to be set up first

WhatWhereWhy
realm role allowed-tenant-context-switchdirectly in Keycloak on Lena’s accountallows a switch into a foreign tenant at all
nordbau in the attribute allowedTenantson Lena’s accountsays which tenants she may reach
realm role allowed-user-context-switchdirectly in Keycloakonly needed if Lena should also switch the person
Ben’s consent for Lena in nordbauBen approves Lena’s request or gives it himself, see belowwithout it, every user switch to Ben ends with 403 user-switch-not-consented
Ben is a member of nordbauorganization nordbau, or Ben’s attribute tenant or allowedTenantsthere is no switch to a person outside the target tenant
lookup of the target persononly if CDMS and CIAS run separately, see User switch by headerwithout it, every user switch ends with 403
business roles for the models she wants to look intoin the role catalog, granted to Lenawithout them CDMS refuses every model

The two switch roles are in no role catalog and are not granted through CIAS. More on this under The platform’s realm roles.

Beforehand: Ben consents

The role allowed-user-context-switch allows Lena to switch the person at all. To whom is decided by the target person. Ben has to consent to Lena switching, for nordbau, for the mode and for a limited time.

Lena asks for consent
  1. 1
    Client→CIAS
    Lena sends POST /cias/me/switch-requests with target: 3f2a…, mode: target, validUntil (say, in two days) and reason: Ticket 4711. Her token runs in the tenant nordbau for this.
  2. 2
    CIAS
    creates the request and sends Ben a mail: who is asking, with whose permissions, until when and why
  3. 3
    Client→CIAS
    Ben approves in his portal: POST /cias/me/switch-consents/{id}/approve
    Result: From now on, until the end, Lena may work on Ben's behalf, with his roles. On the first switch Ben gets one more mail.

Ben can revoke the consent at any time. Lena’s very next request is then refused. All rules are under The target person’s consent.

One request, step by step

Lena’s tool sends a perfectly normal request to CDMS, plus the two headers:

Request
POST /api/rest/order/query
Authorization: Bearer <token of Lena, tenant codamic>
tenant: nordbau
user: 3f2a…
{ "response": ["id", "orderNr", "price"] }
Response
The search runs in the database of nordbau,
with Lena's roles and Ben's user id.
With an additional "user-roles: target" it would run with Ben's roles.
sequenceDiagram
    participant C as Client
    participant F as Filter chain
    participant G as Tenant gate
    participant D as CDMS
    participant DB as Database nordbau
    C->>F: token (codamic) + tenant nordbau + user 3f2a…
    F->>F: read headers, then check the token
    F->>F: resolve: nordbau is not an own organization → codamic
    F->>G: Is codamic served?
    G-->>F: yes
    F->>F: determine roles and attribute values for codamic
    F->>F: role present, nordbau allowed → tenant := nordbau
    F->>G: Is nordbau served?
    G-->>F: yes
    F->>F: determine Lena's values per tenant for nordbau again
    F->>F: role present, Ben known, member of nordbau
    F->>F: consent from Ben for Lena in nordbau? yes → person := Ben
    F->>D: pass the request on
    D->>DB: search, with Lena's roles and Ben's id
    DB-->>C: rows

Two things about this order stand out, and both are deliberate:

  • Roles are determined before the switch, for Lena’s originating tenant, and keep applying in the target unchanged. That is why nobody carries rights into a tenant switch that were granted in the target. Lena’s values per tenant, by contrast, are determined again for nordbau, because they say what she may see there. The only exception is the user switch with user-roles: target: then Ben’s roles and values apply, as they would appear in his own token for nordbau.
  • The user switch comes last, after the tenant switch and the second question to the gate. That is why Ben has to be a member of nordbau, not of codamic.
  • The gate is asked twice: once for the tenant from the token, once for the target. A suspended tenant stays suspended for support too.

What Lena sees and what she does not

After the switch to nordbau
She sees
comes from the target
  • the rows from the database of nordbau
  • every model her own roles are sufficient for
  • the history of an object, if her roles cover the read role and the history role
  • with a user switch: the rows that belong to Ben in user models
  • with user-roles: target: everything Ben's roles and attribute values are sufficient for
She does not see
stays hers or is missing
  • without user-roles: target: roles somebody holds in nordbau, she gets none of them
  • fields and models her own roles are not sufficient for
  • without a user switch: other people's rows in user models, because the owner filter works with her id
  • the description of the tenant, that is its kind and organization: after a privileged switch CIAS only returns the key

Attribute values are values on a person that CDMS uses to sift rows, for example regions. Unlike the roles, values per tenant are determined again for nordbau after the switch; all others come from Lena’s token. So an attribute filter sifts in nordbau with Lena’s values for nordbau, not Ben’s, unless user-roles: target is set. See One value per person or per tenant and Attribute filters.

The variants

What happens in which situation

When: Lena sends tenant: nordbau and no user header.

She works in the database of nordbau with her own roles and her own id. In tenant models she sees the customer's data; in user models she sees nothing, because no row there belongs to her.

Result: The right way to look at a customer's data.

When: Lena sends tenant: nordbau and user: 3f2a….

Each switch needs its own realm role. The tenant is switched first, then the person. Id and name are then Ben's, and the owner filter works with Ben's id. Roles and attribute values stay Lena's.

Result: The way to see a person's own data.

When: As above, plus user-roles: target.

Lena gets Ben's realm roles, business roles, groups and attribute values, completely, including ones she does not have herself. If Ben has roles in the organization nordbau, these replace his global ones, as in his own token. CIAS fetches per-tenant values for Ben.

Result: The way to reproduce what Ben sees.

When: allowed-user-context-switch is missing from the token, the id is unknown, or Ben does not belong to nordbau.

The user switch is not silently skipped. The whole request is refused; why exactly is only in the log. If CIAS cannot look up who Ben is at all, the key is user-switch-unavailable.

Result: 403 cias.authentication.user-switch-denied.

When: Role present, Ben a member of nordbau, but no valid consent for Lena: never given, expired, revoked, for another tenant, or only for own roles while user-roles: target is sent.

The request is refused before anything is switched. The key is a separate one, so that Lena's tool can offer to ask Ben for consent.

Result: 403 cias.authentication.user-switch-not-consented.

When: allowed-tenant-context-switch is missing from the token.

The header is silently ignored. There is no error and no hint in the response: the request keeps running in codamic.

Result: Lena sees her own tenant's data and takes it for the customer's.

When: nordbau is not in Lena's list of allowed tenants.

CIAS does not switch. The persistence layer sees the refused wish at the first access to a tenant model.

Result: 403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED. If the request only touches system models, there is no error.

When: The switch would be allowed, but nordbau is suspended, closed or expired.

The second gate refuses, with no exemption for support.

Result: 403 cias.authentication.tenant-not-served. See A customer cancels.

When: Lena is a member of the organization nordbau.

Then the header is not a privileged action but a selection: she needs no switch role, and her roles in nordbau apply.

Result: Different path, different outcome. See Switch between tenants.

When does which header take effect?
Headerrealm role presenttarget allowed or knownResult
tenantno–silently ignored, the request stays in the own tenant
tenantyestarget in the list of allowed tenantsthe tenant of the request becomes the target, roles stay the own ones
tenantyestarget not in the listno switch; 403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED at the first access to tenant data
userno–403 cias.authentication.user-switch-denied
useryesperson unknown or not a member of the request's tenant403 cias.authentication.user-switch-denied
useryesperson known and a member, but no matching consent403 cias.authentication.user-switch-not-consented
useryesperson known, a member, and the consent fitsid and name become Ben's; roles Lena's, or Ben's with user-roles: target

What a switch leaves behind

Traces of a support session
  1. 1
    CIAS
    The tenant switch publishes no event. For the user switch, the request, the consent and the first use of the consent appear as SwitchConsentEvent.* in the CIAS audit, not every single request
  2. 2
    CDMS
    Plain reading leaves no revision. Only a change writes one
  3. 3
    CDMS→Database
    If Lena changes something, the revision records user id, user name, IP address and browser of the request
    Result: After a user switch the revision holds Ben's user id and name, plus Lena as the acting person. In the history her name appears in actingUsername.

More on this under What a revision records and Which events are logged.

Traps

Next

Sources in the code and the knowledge base
  • CIAS/cias-authentication – JwtSessionFilter (headers `tenant` and `user` before the token), TokenParser.admit (order: resolution, gate, roles, attributes, switch, second gate), TokenParser.switchUser (allowed-user-context-switch, header user-roles, membership in the target tenant), SwitchTargetLookup, RequestAdmission (USER_SWITCH_DENIED, USER_SWITCH_UNAVAILABLE, USER_SWITCH_NOT_CONSENTED), UserSwitchPolicy, ContextSwitch (allowed-tenant-context-switch, isAllowedTarget), EffectiveRoles.resolve, EffectiveAttributes.resolve, ResolvedTenantHolder
  • CIAS/cias-authentication – TenantGate (30 s cache), RequestAdmission.TENANT_NOT_SERVED
  • CIAS/cias-authorization – ConferralCeiling.resolve (person from the caller's subject); CIAS/cias-audit – DomainEventAuditListener (actor from the CallerContext); CIAS/cias-spring-boot-starter – RequestContextCallerContextProvider
  • commons-persistence – DatabaseRequestContext.requireAllowedTenant, PersistenceErrorCode.CDMS_TENANT_SWITCH_NOT_AUTHORIZED
  • CDMS/cdms-system-layer – AbstractLayer (owner filter via getUserId); CDMS/cdms-authorization – AbstractAttributeFilter; CDMS/cdms-persistence-database – AuditRevisionListener (userId, username, acting_user_id, acting_username)
  • CIAS/cias-user – SwitchConsentService, SwitchConsentController (/cias/me/switch-requests, /cias/me/switch-consents); CIAS/cias-notification – SWITCH_CONSENT_REQUESTED, SWITCH_CONSENT_USED
  • CIAS/cias-authentication/docs/adr – ADR-006, ADR-021, ADR-042; CIAS/cias-user/docs/adr – ADR-050
Search