CodamAIDocs
Themafertig

Mandantenwechsel per Header

Wie eine berechtigte Person mit dem Header tenant für einen anderen Mandanten arbeitet und warum ein Wechsel ohne Rolle still ignoriert wird.

Ausprägungen
Auswahl unter eigenen Organisationenprivilegierter Wechselohne Rolle → still ignoriertZiel nicht erlaubt → 403Ziel gesperrt → 403Betriebsart SINGLE

Worum es geht

Normalerweise arbeitet eine Anfrage im Mandanten aus dem Token. Manchmal muss jemand gezielt in einem anderen Mandanten arbeiten: eine Person, die zu zwei Firmen gehört, oder der Support, der einem Kunden hilft. Dafür schickt der Client den Header tenant mit dem gewünschten Mandantenschlüssel:

Anfrage
POST /api/rest/crm/customer/query
Authorization: Bearer <Token>
tenant: globex
{ "response": ["id", "name"] }
Antwort
Die Suche läuft in der Datenbank von globex,
sofern der Wechsel erlaubt ist.

Der Header ist nicht vertrauenswürdig, denn jeder Client kann ihn setzen. Er ist deshalb nur ein Wunsch. Ob er wirkt, entscheiden Angaben aus dem signierten Token.

Es gibt zwei Arten von Wechsel:

  • Auswahl: Die Person ist selbst Mitglied im Ziel-Mandanten, also in dieser Organisation. Dafür braucht sie keine besondere Rolle.
  • Privilegierter Wechsel: Die Person ist nicht Mitglied im Ziel-Mandanten. Dafür braucht sie die Realm-Rolle allowed-tenant-context-switch und das Ziel muss in ihrer Liste der erlaubten Mandanten stehen. Eine Realm-Rolle ist eine Rolle, die in Keycloak für die ganze Plattform gilt, nicht nur für eine Anwendung.

Die Entscheidungstabelle

Was der Header tenant bewirkt (Betriebsart MULTI)
Ziel ist eine eigene OrganisationZiel in der Liste der erlaubten MandantenRealm-Rolle allowed-tenant-context-switchZiel wird bedientErgebnis
ja––jaAnfrage läuft im Ziel-Mandanten (Auswahl)
neinjajajaAnfrage läuft im Ziel-Mandanten (privilegierter Wechsel)
neinjanein–Header wird still ignoriert, Anfrage läuft im eigenen Mandanten
neinnein––Wechsel findet nicht statt; der erste Zugriff auf ein Mandanten-Modell endet mit 403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED
–jajanein403 cias.authentication.tenant-not-served

„–“ heißt: spielt in dieser Zeile keine Rolle.

Der Ablauf

sequenceDiagram
    participant C as Client
    participant F as CIAS-Filterkette
    participant G as Mandanten-Tor (CIAS)
    participant P as CDMS-Persistenz
    participant DB as Datenbank globex
    C->>F: Anfrage mit Token (Mandant acme) und Header tenant: globex
    F->>F: Mandant aus dem Token bestimmen: acme
    F->>G: Wird acme bedient?
    G-->>F: ja
    F->>F: Rolle allowed-tenant-context-switch? globex erlaubt?
    F->>F: Mandant der Anfrage := globex
    F->>G: Wird globex bedient?
    G-->>F: ja
    F->>P: Anfrage weiterreichen
    P->>P: globex in der Liste der erlaubten Mandanten? ja
    P->>DB: lesen und schreiben
    P-->>C: Antwort

Zwei Prüfungen fallen auf:

  • CIAS fragt das Mandanten-Tor zweimal: einmal für den Mandanten aus dem Token und nach dem Wechsel noch einmal für das Ziel. Ein gesperrter Mandant bleibt auch für jemanden gesperrt, der hineinwechselt, Administratoren eingeschlossen.
  • Die Persistenz prüft die Liste der erlaubten Mandanten ein zweites Mal, unabhängig von CIAS. Zwei getrennte Prüfungen sichern denselben Sachverhalt.

Varianten

Die Ausprägungen des Wechsels

Wann: Die Person ist Mitglied in den Organisationen acme und globex und schickt tenant: globex.

  1. 1
    CIAS
    erkennt globex als eigene Organisation und wählt sie aus
  2. 2
    CIAS
    bestimmt Rollen und Attribute für globex
  3. 3
    CDMS→Database
    arbeitet in der Datenbank globex

Ergebnis: Keine besondere Rolle nötig. Die Person hat in globex genau die Rollen, die sie dort als Mitglied hat.

Wann: Eine Support-Person mit Mandant acme, Rolle allowed-tenant-context-switch und globex im Attribut allowedTenants schickt tenant: globex.

  1. 1
    CIAS
    bestimmt acme aus dem Token, Tor sagt ja
  2. 2
    CIAS
    Rolle vorhanden, Ziel erlaubt → Mandant der Anfrage wird globex
  3. 3
    CIAS
    fragt das Tor für globex: ja
  4. 4
    CDMS→Database
    arbeitet in der Datenbank globex

Ergebnis: Die Person nimmt ihre eigenen Rollen und Attribute mit. Sie bekommt keine Rollen, die jemand in globex hat.

Wann: globex steht in der Liste der erlaubten Mandanten, die Rolle fehlt.

  1. 1
    CIAS
    Rolle fehlt → der Wunsch wird nicht angewendet
  2. 2
    CDMS→Database
    arbeitet in der Datenbank acme

Ergebnis: Kein Fehler, kein Hinweis in der Antwort. Die Daten kommen aus dem eigenen Mandanten.

Wann: globex steht nicht in der Liste der erlaubten Mandanten, mit oder ohne Rolle.

  1. 1
    CIAS
    Ziel nicht erlaubt → der Wunsch wird nicht angewendet
  2. 2
    CDMS
    sieht beim ersten Zugriff auf ein Mandanten-Modell den abgelehnten Wunsch
  3. 3
    CDMS
    403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED

Ergebnis: Greift die Anfrage nur auf System-Modelle zu, gibt es keinen Fehler, denn die brauchen keinen Mandanten.

Wann: Der Wechsel wäre erlaubt, aber globex ist gesperrt, geschlossen oder unbekannt.

  1. 1
    CIAS
    wechselt nach globex und fragt das Tor
  2. 2
    CIAS→Client
    403 cias.authentication.tenant-not-served

Ergebnis: Die Anfrage erreicht CDMS nicht. Einen gesperrten Mandanten verwaltest du über die Verwaltungs-API von CIAS, nicht über den Wechsel.

Wann: CODAMAI_PERSISTENCE_TENANT_MODE=SINGLE

Es gibt keine Mandanten und die Liste der erlaubten Mandanten ist leer. Der Header tenant bewirkt nichts, auch mit Rolle nicht, und führt auch zu keinem Fehler.

Ergebnis: Alles bleibt in der einen Datenbank.

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • CIAS/cias-authentication – JwtSessionFilter (Header tenant als userTenantSwitchRequest), OrganizationTenantResolver (Auswahl unter eigenen Organisationen), ContextSwitch (allowed-tenant-context-switch, isAllowedTarget), TokenParser.admit (zweite Tor-Prüfung nach dem Wechsel, Rollen bleiben)
  • commons-persistence – DatabaseRequestContext.requireAllowedTenant (Abweichung A-3), PersistenceErrorCode.CDMS_TENANT_SWITCH_NOT_AUTHORIZED
  • CIAS/cias-authentication – RequestAdmission (tenant-not-served)
  • documentation/30-daten-und-persistenz/01-mandantentrennung.md (Schritt 2)
Suchen