Worum es geht
Das hier ist der Weg, den jede Anfrage in einer CodamAI-Anwendung geht. Eine Person meldet sich an, klickt auf „Kunden“ und sieht eine Liste. Dazwischen liegen sechs Programme, und jedes tut genau eine Sache.
Wenn du diese Seite verstanden hast, kannst du jede andere Seite lesen: Alle anderen Abläufe sind Abzweigungen von diesem einen.
Wer welche Farbe hat und wer überhaupt beteiligt ist, steht unter Die Beteiligten einer Anfrage. Diese Seite spielt den Weg einmal von vorn bis hinten durch.
Die zwei Hälften
Der Ablauf zerfällt in zwei Teile, die nichts miteinander zu tun haben – außer dass der zweite den ersten voraussetzt:
- Browser, BFF und Keycloak
- endet damit, dass der BFF drei Tokens hat
- CDMS und CIAS sind daran nicht beteiligt
- Browser, BFF, CIAS, CDMS, Datenbank
- beginnt damit, dass der BFF das Access-Token hervorholt
- Keycloak wird höchstens noch für den Token-Tausch gefragt
Teil 1: Anmelden
sequenceDiagram
autonumber
participant B as Browser
participant F as BFF
participant K as Keycloak
B->>F: öffnet /kunden
F-->>B: keine Sitzung, Weiterleitung auf /login
B->>K: Login-Seite von Keycloak
K-->>B: Passwort, ggf. Einmalcode
B->>K: Eingaben
K-->>B: Weiterleitung zurück mit einem einmaligen Code
B->>F: /api/auth/callback/keycloak?code=…
F->>K: tauscht den Code gegen Tokens
K-->>F: Access-, Refresh- und ID-Token
F-->>B: verschlüsseltes Sitzungs-Cookie, zurück auf /kunden
Drei Dinge daraus, die den Rest der Seite tragen:
- Das Passwort sieht nur Keycloak. Die Anwendung bekommt es nie. Details unter Anmelden im Browser.
- Die Tokens liegen im BFF, im Browser liegt nur ein verschlüsseltes Cookie. Details unter Sitzung im BFF und Cookies.
- Das Access-Token ist der Ausweis für alles Weitere. Es gilt kurz (voreingestellt 5 Minuten) und wird vor Ablauf erneuert, siehe Token erneuern.
Teil 2: Der Klick auf „Kunden“
Jetzt kommt der Weg, um den es hier geht. Die Person ist angemeldet und klickt in der Oberfläche auf „Kunden“.
sequenceDiagram
autonumber
participant U as Benutzer
participant B as Browser
participant F as BFF
participant K as Keycloak
participant FK as Filterkette (CIAS)
participant T as Mandanten-Tor (CIAS)
participant C as CDMS
participant DB as Mandanten-DB
U->>B: klickt auf „Kunden“
B->>F: GET /api/kunden, Sitzungs-Cookie geht mit
F->>F: Cookie entschlüsseln, Access-Token herausnehmen
opt Token läuft bald ab
F->>K: POST /token, grant_type=refresh_token
K-->>F: neues Access-Token
end
F->>FK: POST /api/rest/crm/customer/query<br/>Authorization: Bearer …
FK->>FK: Signatur und Ablauf prüfen
FK->>K: Token tauschen (oder aus dem Cache)
K-->>FK: Token für den CIAS-Client
FK->>FK: Identität lesen, Mandant auflösen
FK->>T: wird nordbau bedient?
T-->>FK: ja (30 s gemerkt)
FK->>FK: effektive Rollen und Attribute bilden
FK->>C: RequestContext gefüllt, weiterreichen
C->>C: response auflösen (welche Felder?)
C->>C: Leserolle für customer prüfen
C->>C: Sicherheitsfilter anhängen (Besitzer, Attribute, eigene)
C->>DB: SELECT der erlaubten Spalten und Zeilen
DB-->>C: Zeilen
C->>C: READ-Hook je Zeile, dann in DTOs umwandeln
C-->>F: 200 mit data und meta
F-->>B: die Liste als JSON der eigenen Route
B-->>U: die Tabelle erscheint
Das ist der ganze Weg. Die folgenden Abschnitte gehen ihn in vier Etappen noch einmal langsam durch.
Etappe 1: Browser und BFF
Der Browser ruft nie CDMS oder CIAS direkt auf. Er ruft eine Route des eigenen Frontends auf, und die liegt auf dem BFF, dem Server-Teil der Oberfläche.
-
1Browser→BFFruft eine Route der eigenen Anwendung auf, z. B.
GET /api/kunden. Das Sitzungs-Cookie schickt der Browser automatisch mit -
2BFFentschlüsselt das Cookie mit dem Geheimnis des Portals und holt das Access-Token heraus
-
3BFF→Browserkein Access-Token oder ein Fehler in der Sitzung → 401, die Oberfläche startet einen neuen Login
-
4BFF→KeycloakToken bald abgelaufen? Dann erst ein neues holenDie Portale erneuern 90 Sekunden vor Ablauf. Der Hub prüft zusätzlich vor jedem API-Aufruf, ob das Token schon abgelaufen ist, und erneuert dann sofort.
-
5BFF→CDMSschickt
POST /api/rest/crm/customer/querymitAuthorization: Bearer <Access-Token>und einem Körper ausresponseundparameterErgebnis: Nur der BFF kennt das Token. Der Browser hat es nie gesehen.
Warum dieser Umweg? Weil das Token im Browser von jedem Skript der Seite lesbar wäre. Im BFF ist es das nicht. Die ganze Begründung steht unter Sitzung im BFF und Cookies.
Der BFF ist außerdem die Stelle, an der aus einer fachlichen Route („gib mir die Kunden“) eine CDMS-Anfrage wird: Er setzt die Feldauswahl (response) und die Suchparameter zusammen. Siehe Aufbau einer Suche.
Etappe 2: Die Filterkette von CIAS
Die Anfrage kommt beim Backend an – und läuft dort zuerst durch die Filterkette von CIAS, noch bevor eine einzige Zeile CDMS-Code läuft.
-
FilterketteOffener Pfad?Ein paar Pfade brauchen kein Token:
OPTIONS-Anfragen des Browsers und was ein Modul ausdrücklich veröffentlicht./v3/api-docsund/swagger-uihaben eine eigene Anmeldung mit Benutzer und Passwort. Alles andere braucht eines↳ nein 403, wenn kein Token da ist -
FilterketteToken prüfenIst die Signatur mit einem Schlüssel des Realms gültig und das Token nicht abgelaufen? Keycloak wird dafür nicht gefragt↳ nein 401 mit
WWW-Authenticate: Bearer error="invalid_token" -
CIASToken tauschenKeycloak tauscht das Token gegen eines für den Client von CIAS. Erst darin stehen alle Rollen und Attribute↳ nein die Anfrage läuft ohne Identität weiter, jede Rollenprüfung lehnt danach ab
-
CIASMandant auflösenWelcher Mandant ist gemeint? Genau einer, oder bewusst keiner?↳ nein 403
cias.authentication.tenant-unresolved -
CIASMandanten-TorWird dieser Mandant heute bedient?↳ nein 403
cias.authentication.tenant-not-served -
CIASRollen und AttributeWelche Rollen und welche Attributwerte gelten in genau diesem Mandanten?↳ nein Attributwerte nicht abrufbar: 403
cias.authentication.tenant-not-served -
CIASMandant Pflicht?Trennt die Installation nach Mandanten, ist die Person bekannt, hat aber keinen Mandanten?↳ nein 403
cias.authentication.tenant-required - Die Anwendung bekommt die Anfrage mit gefülltem RequestContext
Das Ergebnis heißt RequestContext: ein Merkzettel, der genau für diese eine Anfrage gilt. Darin stehen Benutzer-ID, Name, Mandant, erlaubte Mandanten, Rollen, Gruppen, Attribute – dazu IP-Adresse und Browser-Kennung, die die Filterkette aus der Anfrage gelesen hat. Alles danach liest nur noch dort nach, nie wieder im Token.
Zwei Punkte, die man leicht übersieht:
- Die Filterkette ist immer Teil des CDMS-Prozesses, auch wenn CIAS als eigener Dienst läuft. Sie ist ein Jar, kein Server.
- Sie räumt den Merkzettel nach der Antwort wieder ab. Der nächste Aufruf auf demselben Thread fängt leer an.
Jede Station im Einzelnen steht unter Was bei jeder Anfrage mit dem Token passiert, die Mandantenfrage unter Die Mandantenprüfung in beiden Betriebsarten.
Etappe 3: CDMS
Jetzt erst ist CDMS dran. Es sieht kein Token, nur den RequestContext.
-
1CDMSREST-Layer: liest den Körper, löst
responseauf – welche Felder, welche Referenzen, welche Listen sollen zurück?Ohneresponseweiß CDMS nicht, was es liefern soll, und lehnt ab. Siehe Feldauswahl mitresponse. -
2CDMSSystem-Layer: Erlaubt eine der effektiven Rollen das Lesen von
customer? -
3CDMS→BFFkeine passende Rolle → 403
missing-permission|<rolle> -
4CDMShängt die Sicherheitsfilter an: Owner-Filter, Attributfilter und eigene Filter des ProjektsDer Filter des Clients und die Sicherheitsfilter landen zusammen in einer UND-Klammer. Kein
ODERdes Clients kann sie deshalb aushebeln. -
5CDMS→Mandanten-DBPersistenz: wählt die Datenbank und liest nur die angeforderten Spalten der erlaubten ZeilenSystem-Modelle gehen in die System-Datenbank, Mandanten- und Benutzer-Modelle in die Datenbank des Mandanten aus dem RequestContext.
-
6HookREAD-Hook für jedes geladene Objekt, bevor es zur Antwort wird
-
7CDMS→BFFantwortet mit
data(die Liste) undmeta(Trefferzahl, Seite)Ergebnis: Keine Treffer sind kein Fehler: 200 mit leerer Liste.
Die Stationen innerhalb von CDMS haben eine eigene Seite: Der Weg einer Anfrage durch die Schichten. Welche Zeilen eine Person sieht, steht unter Die drei Ebenen im Überblick.
Etappe 4: Zurück zum Bildschirm
Der Rückweg ist kurz, hat aber zwei Besonderheiten:
- Vor dem Schreiben der Antwort schreibt CDMS seine Transaktion fest. Beim Lesen fällt das nicht auf, beim Schreiben ist es der entscheidende Punkt – siehe Ein Schreibvorgang über alle Schichten.
- Der BFF gibt die Daten nicht roh weiter. Er formt sie in das um, was die Oberfläche braucht, und übersetzt Fehler: Ein
missing-permission|projektwird zu „dir fehlt eine Rolle für dieses Modell“, eintenant-requiredzu „dein Konto gehört zu keinem Mandanten“. Die Oberfläche schickt jemanden damit an die richtige Stelle statt auf die Login-Seite.
Derselbe Weg in beiden Betriebsarten
CDMS und CIAS können in einem Prozess laufen oder als zwei Dienste. Der Weg der Anfrage ist derselbe – nur die Frage „wird der Mandant bedient?“ nimmt einen anderen Weg.
Wann: CDMS und CIAS laufen im selben Prozess, so wie das Hub-Backend.
sequenceDiagram
autonumber
participant F as BFF
participant FK as Filterkette (CIAS)
participant T as Mandanten-Tor (CIAS)
participant U as cias-user
participant C as CDMS
participant DB as Mandanten-DB
F->>FK: POST /api/rest/crm/customer/query + Token
FK->>FK: Token prüfen, tauschen, Mandant auflösen
FK->>T: darf nordbau bedient werden?
Note over FK,T: Methodenaufruf, kein Netz
T-->>FK: ja (30 s gemerkt)
FK->>U: welche Attributwerte gelten hier?
U-->>FK: regionen = [nord]
FK->>C: RequestContext gefüllt
C->>DB: SELECT der erlaubten Zeilen
DB-->>C: Zeilen
C-->>F: data + meta
Ergebnis: Ein Prozess, kein Netzwerkaufruf zwischen CDMS und CIAS. CIAS kann nicht allein ausfallen.
Wann: CIAS läuft als eigener Dienst (cias-runtime), CDMS als zweiter.
sequenceDiagram
autonumber
participant F as BFF
participant FK as Filterkette (im CDMS-Dienst)
participant TC as cias-tenancy-client
participant S as CIAS-Dienst
participant C as CDMS
participant DB as Mandanten-DB
F->>FK: POST /api/rest/crm/customer/query + Token
FK->>FK: Token prüfen, tauschen, Mandant auflösen
FK->>TC: darf nordbau bedient werden?
TC->>S: GET /cias/lookup/tenants/nordbau<br/>mit dem Dienst-Token von CDMS
S-->>TC: 200 served: true
TC-->>FK: ja (30 s gemerkt)
FK->>TC: welche Attributwerte gelten hier?
TC->>S: GET /cias/lookup/users/{id}/attributes?tenantKey=nordbau
S-->>TC: regionen = [nord]
TC-->>FK: Werte
FK->>C: RequestContext gefüllt
C->>DB: SELECT der erlaubten Zeilen
DB-->>C: Zeilen
C-->>F: data + meta
Ergebnis: Zwei zusätzliche HTTP-Aufrufe – aber nur, wenn nichts gemerkt ist. Das Token dafür ist ein Dienst-Token von CDMS, nie das der Person.
Die Unterschiede in voller Breite stehen unter Eingebettet und getrennt im Vergleich. Für den Anfrageweg zählt vor allem: Die Filterkette läuft in beiden Fällen im CDMS-Prozess, und die Antwort der beiden Lookups wird 30 Sekunden gemerkt. Was passiert, wenn der CIAS-Dienst schweigt, steht unter Wenn CIAS nicht erreichbar ist.
Was in welcher Anfrage steckt
Drei Anfragen hintereinander, und jede sieht anders aus:
| Abschnitt | Was geht mit | Wer prüft es |
|---|---|---|
| Browser → BFF | Sitzungs-Cookie (verschlüsselt, httpOnly) | der BFF, mit dem Geheimnis des Portals |
| BFF → Keycloak (nur beim Erneuern) | Refresh-Token, Client-ID, Client-Geheimnis | Keycloak |
| BFF → CDMS | Authorization: Bearer <Access-Token>, dazu response und parameter im Körper | die Filterkette von CIAS, dann CDMS |
| CDMS → CIAS-Dienst (nur getrennt) | ein eigenes Dienst-Token von CDMS | der CIAS-Dienst, über eine eigene Leserolle |
| CDMS → Datenbank | keine Anmeldedaten der Person, sondern die Verbindung des Mandanten | die Datenbank selbst |
Wo der Weg endet, wenn etwas fehlt
| Sitzung im BFF | Token gültig | Mandant zulässig | Leserolle für das Modell | Was die Person sieht |
|---|---|---|---|---|
| nein | – | – | – | Die Oberfläche schickt zur Anmeldung |
| ja | nein | – | – | 401 – das Token wird erneuert, die Anfrage wiederholt |
| ja | ja | nein | – | 403 aus der Filterkette, mit error: cias.authentication.… |
| ja | ja | ja | nein | 403 aus CDMS, mit messageKey: missing-permission|<rolle> |
| ja | ja | ja | ja | 200 mit den Zeilen, die die Filter durchlassen – vielleicht auch mit keiner |
Zwei Arten von 403 also, und sie sehen verschieden aus: Die Filterkette schreibt { "error": …, "message": … }, CDMS schreibt { "messageKey": …, "code": …, "layer": … }. Am Feldnamen erkennst du sofort, wer abgelehnt hat. Siehe 401, 403 oder 404?.
Eine leere Liste ist übrigens kein Fehler und sieht genauso aus wie „es gibt nichts“. Das ist gewollt: Wer eine Zeile nicht sehen darf, soll nicht daraus schließen können, dass es sie gibt. Siehe Warum Unsichtbares 404 liefert.
Fallen
Wie es weitergeht
- Der Weg des Tokens: wie Mandant, Rollen und Attribute ins Token kommen
- Die Mandantenprüfung in beiden Betriebsarten
- Ein Schreibvorgang über alle Schichten: derselbe Weg, aber mit Speichern
- Wenn CIAS nicht erreichbar ist
- Der Weg einer Anfrage durch die Schichten: die CDMS-Hälfte im Detail
- Was bei jeder Anfrage mit dem Token passiert: die CIAS-Hälfte im Detail