Worum es geht
CDMS muss bei jeder Anfrage wissen, für welchen Mandanten sie läuft. Nur so findet es die richtige Datenbank. Der Mandant kommt aus dem Token, das Keycloak ausgestellt und signiert hat. Der Client schickt ihn nicht als eigenes Feld mit, und CDMS nimmt ihn auch aus keinem Feld der Daten.
Die Filterkette von CIAS liest das Token, bevor CDMS irgendetwas tut. Sie legt das Ergebnis im RequestContext ab. Das ist ein Speicher, der genau eine Anfrage lang lebt und am Ende gelöscht wird. Alles Spätere liest den Mandanten nur noch von dort.
Der Weg des Mandanten
flowchart LR
T["Token<br/>organization: acme<br/>tenant: acme<br/>allowedTenants: …"] --> F["CIAS-Filterkette<br/>liest und prüft"]
H["Header tenant<br/>(nur ein Wunsch)"] -.-> F
F --> RC["RequestContext<br/>Mandant: acme<br/>erlaubte Mandanten: acme, …"]
RC --> P["CDMS-Persistenz<br/>wählt die Datenbank"]
P --> DB[("Datenbank acme")]
Wo der Mandant im Token steht
Keycloak kann den Mandanten auf zwei Arten ins Token schreiben:
- Organisation: Die Person ist Mitglied einer Organisation in Keycloak. Das Token trägt sie im Claim
organization. Der Alias der Organisation, ihr Kurzname in Keycloak, ist der Mandantenschlüssel. So entstehen Mandanten, die CIAS zur Laufzeit anlegt. - Attribut
tenant: Die Person hat ein Benutzerattributtenantmit dem Mandantenschlüssel. Das ist der fest zugeordnete Mandant.
Ein Mandantenschlüssel besteht nur aus Kleinbuchstaben, Ziffern und Bindestrichen, zum Beispiel acme oder stadtwerke-nord. Er ist zugleich der Name der Datenbank des Mandanten.
Wie Mandanten und Organisationen in CIAS entstehen, steht unter Statische und dynamische Mandanten.
Welcher Mandant gilt
| Organisationen im Token | Attribut tenant | Header tenant nennt eine eigene Organisation | Mandant der Anfrage |
|---|---|---|---|
| keine | fehlt | – | kein Mandant |
| keine | acme | – | acme |
| eine oder mehrere | nennt keine davon | – | 403 cias.authentication.tenant-unresolved: die zwei Quellen widersprechen sich |
| mehrere | – | ja, z. B. globex | globex: Auswahl unter den eigenen Organisationen |
| mehrere | nennt eine davon | nein | die Organisation aus dem Attribut |
| genau eine | fehlt | nein | diese eine Organisation |
| mehrere | fehlt | nein | 403 cias.authentication.tenant-unresolved: unklar, wessen Daten gemeint sind |
Die Zeilen werden von oben nach unten gelesen. Einen Standard-Mandanten, auf den CIAS zurückfällt, gibt es nicht. Ein falscher Mandant wäre schlimmer als keine Antwort: Er würde einem Kunden die Daten eines anderen zeigen.
Was im RequestContext landet
Ist der Mandant bestimmt, prüft CIAS noch, ob er bedient wird. Danach schreibt es in den RequestContext:
| Eintrag | Inhalt |
|---|---|
| Mandant | der eben bestimmte Mandantenschlüssel |
| erlaubte Mandanten | der eigene Mandant, alle eigenen Organisationen und alle Einträge aus dem Benutzerattribut allowedTenants |
| Wechselwunsch | der Wert des Headers tenant, falls vorhanden |
| Benutzer, Rollen, Attribute | wie unter Die drei Ebenen der Sicherheit beschrieben |
Die Liste der erlaubten Mandanten begrenzt, wohin ein Mandantenwechsel überhaupt führen kann. Sie kommt aus dem signierten Token, nie aus einem Header. Das Attribut allowedTenants darf mehrere Werte haben oder eine durch Kommas getrennte Liste sein.
Varianten
Wann: MULTI, das Token nennt genau einen Mandanten
-
1Client→CDMSschickt die Anfrage mit
Authorization: Bearer … -
2CIASprüft das Token und bestimmt den Mandanten
acme -
3CIASfragt, ob
acmebedient wird: ja -
4CDMS→Databaseliest und schreibt Mandanten-Modelle in der Datenbank
acme
Ergebnis: Die Anfrage läuft vollständig im Mandanten acme.
Wann: Das Token gehört einer echten Person oder einem Dienstkonto, nennt aber weder Organisation noch Attribut tenant.
-
1CIASprüft das Token: gültig, aber ohne Mandant
-
2CIAS→Client403
cias.authentication.tenant-required, noch bevor CDMS die Anfrage sieht
Ergebnis: Der Fehler sagt: Dieser Person wurde nie ein Mandant zugeordnet. Das wird in Keycloak bzw. CIAS behoben, nicht im Client.
Wann: Eine Anfrage erreicht die Persistenz trotzdem ohne Mandanten, etwa aus eigenem Code.
-
1CDMSwill ein Mandanten-Modell lesen oder schreiben und findet keinen Mandanten
-
2CDMS400
CDMS_TENANT_REQUIRED, kein Rückfall auf die System-Datenbank
Ergebnis: System-Modelle sind davon nicht betroffen, sie brauchen keinen Mandanten.
Wann: CODAMAI_PERSISTENCE_TENANT_MODE=SINGLE
CIAS wertet Organisation und Attribut tenant gar nicht aus. Die Anfrage läuft ohne Mandanten, die Liste der erlaubten Mandanten bleibt leer, ein Header tenant bleibt wirkungslos. Alles landet in der einen Datenbank.
Ergebnis: Ein Token mit Mandant und eines ohne verhalten sich in SINGLE gleich.
Die Fehlerantworten der Filterkette
Lehnt die Filterkette ab, kommt die Antwort von CIAS und nicht von CDMS. Sie hat deshalb ein eigenes, kurzes Format:
POST /api/rest/crm/customer/query
Authorization: Bearer <Token mit zwei Organisationen, ohne Auswahl>HTTP 403
{ "error": "cias.authentication.tenant-unresolved", "message": "request refused" }| Schlüssel | Bedeutung |
|---|---|
cias.authentication.tenant-unresolved | Das Token nennt Mandanten, aber nicht eindeutig einen. |
cias.authentication.tenant-required | Das Token nennt gar keinen Mandanten, obwohl die Installation MULTI ist. |
cias.authentication.tenant-not-served | Der Mandant ist eindeutig, wird aber nicht bedient. Siehe Wird der Mandant bedient? |
Ein ungültiges oder abgelaufenes Token ist ein anderer Fall: Das ergibt 401, siehe Zugriff ohne Token.
Fallen
Wie es weitergeht
- Was mit dem Mandanten in der Persistenz passiert: Welche Datenbank? Das Persistenzziel
- Für einen anderen Mandanten arbeiten: Mandantenwechsel per Header
- Die Sicht von CIAS: Den Mandanten einer Anfrage bestimmen
- Wie ein Mandantenschlüssel aussieht: Der Mandantenschlüssel