CodamAIDocs
Themafertig

CIAS als eigener Service

CIAS läuft als eigener Dienst. Was der CDMS-Dienst dann selbst mitbringt, welche Aufrufe über HTTP gehen und mit welchem Token.

Ausprägungen
Mandantenprüfung per HTTPAttribut-Lookup per HTTPDeklaration per GET /cias/fetchDienst-Token statt Benutzer-Tokeneigene CIAS-DatenbankCIAS antwortet nicht

Worum es geht

Getrennt heißt: CIAS ist ein eigenes Programm mit eigener Adresse und eigener Datenbank. Der CDMS-Dienst und der CIAS-Dienst laufen nebeneinander und reden über HTTP.

Das ausgelieferte Programm für den eigenständigen Betrieb heißt cias-runtime. Es besteht aus denselben Modulen, die eingebettet im Gastgeber laufen – die Fachlichkeit ist dieselbe, nur die Verpackung ist anders.

Der Gegenstück-Betrieb steht unter CIAS eingebettet, alle Unterschiede nebeneinander unter Eingebettet und getrennt im Vergleich.

Das Prozessbild

flowchart LR
    F["Frontend mit BFF"]
    subgraph D1["Dienst 1: CDMS"]
        direction TB
        FK["Filterkette<br/>cias-authentication"]
        TC["cias-tenancy-client"]
        C["CDMS"]
        FK --> C
        FK --> TC
    end
    subgraph D2["Dienst 2: cias-runtime"]
        CI["CIAS-Module<br/>tenancy, user, authorization,<br/>registration, notification, audit"]
    end
    F -- "Bearer-Token<br/>/api/rest/…" --> FK
    F -- "Bearer-Token<br/>/cias/…" --> CI
    TC -- "HTTP + Dienst-Token<br/>Mandant? Attribute?" --> CI
    CI -- "HTTP + Dienst-Token<br/>GET /cias/fetch" --> C
    C --> SDB[("System-DB<br/>+ Mandanten-DBs")]
    CI --> CDB[("CIAS-Datenbank")]
    FK -- "Schlüssel, Token-Tausch" --> K[(Keycloak)]
    CI -- "Adapter" --> K
    classDef client fill:#475569,stroke:#475569,color:#fff
    classDef cdms fill:#1976d2,stroke:#1976d2,color:#fff
    classDef cias fill:#8e24aa,stroke:#8e24aa,color:#fff
    classDef idp fill:#c2410c,stroke:#c2410c,color:#fff
    classDef db fill:#4d7c0f,stroke:#4d7c0f,color:#fff
    class F client
    class C cdms
    class FK,TC,CI cias
    class K idp
    class SDB,CDB db

Zwei Pfeile gehen zwischen den Diensten hin und her, und sie gehen in verschiedene Richtungen. Das ist der Punkt, den man sich merken sollte: CIAS fragt auch bei CDMS nach.

Was der CDMS-Dienst mitbringt

Von CIAS liegen genau zwei Jars im CDMS-Dienst:

Die zwei CIAS-Bausteine im CDMS-Dienst
cias-authentication
die Filterkette
  • prüft bei jeder Anfrage das Token gegen Keycloak
  • tauscht das Token und löst den Mandanten auf
  • enthält das Mandanten-Tor und den Attribut-Lookup samt ihrem Gedächtnis
  • läuft in beiden Betriebsarten im CDMS-Prozess
cias-tenancy-client
der Weg zum CIAS-Dienst
  • beantwortet dieselben zwei Fragen wie eingebettet – nur über HTTP
  • absichtlich winzig: cias-kernel, der HTTP-Client des JDK, sonst nichts
  • kein eigener Zwischenspeicher, kein Wiederholversuch
  • existiert eingebettet gar nicht

Die Auswahl zwischen beiden Welten ist eine Eigenschaft ohne Standardwert:

codamai.cias.tenancy.lookup = local    # cias-tenancy,        Methodenaufruf
codamai.cias.tenancy.lookup = remote   # cias-tenancy-client, HTTP

Ein Dienst, der dazu nichts sagt, startet nicht. Das ist gewollt: raten hieße hier, sich eine Antwort auf „Darf dieser Kunde bedient werden?“ auszudenken.

Welche Aufrufe über HTTP gehen

RichtungAufrufWannAntwort
CDMS → CIASGET /cias/lookup/tenants/{key}bei jeder Anfrage mit Mandant, wenn nichts gemerkt ist200 {"key":"kunde-a","served":true} · 404 kein Mandant mit diesem Schlüssel · 403 der Aufrufer darf nicht fragen
CDMS → CIASGET /cias/lookup/users/{id}/attributes?tenantKey=kunde-abei jeder Anfrage, die einen Mandanten auflöst200 {"attributes":{"regionen":["nord"]}}, auch leer · 403 der Aufrufer darf nicht fragen
CIAS → CDMSGET /cias/fetchbeim Start von CIAS und wenn ein Administrator den Abgleich anstößt200 mit Rollen und Attributen · 403 ohne Leserolle

Die beiden ersten Aufrufe liegen auf dem Anfrageweg. Deshalb sind ihre Zeitlimits kurz (voreingestellt 2 Sekunden, Verbindung und Antwort getrennt) und absichtlich kein Stellknopf: ein langsamer CIAS-Dienst wäre sonst eine langsame Plattform. Lieber scheitern als warten, denn für „gescheitert“ gibt es eine festgelegte Antwort.

Der dritte Aufruf liegt nicht auf dem Anfrageweg. Er läuft beim Start und auf Zuruf, darf deshalb länger dauern und merkt sich nichts.

Mit welchem Token

Der CDMS-Dienst fragt als er selbst, nie im Namen des angemeldeten Benutzers.

Das Token für die Lookups
  1. 1
    CDMS
    holt sein eigenes Dienst-Token bei Keycloak (codamai.cias.tenancy.client.credentials)
    Mit Client-ID und Secret seines eigenen Clients, per Client Credentials. Das Token wird für drei Viertel seiner Laufzeit behalten und dann neu geholt. Es wird nie protokolliert.
  2. 2
    CDMS→CIAS
    schickt es als Authorization: Bearer … an beide Lookup-Endpunkte
  3. 3
    CIAS
    prüft die Rolle des Aufrufers – eine eigene Rolle je Endpunkt, ohne Standardwert
    Weder die Plattform-Admin-Rolle noch dieselbe Rolle für beide Fragen. Der eine Endpunkt sagt, ob ein Schlüssel zu einem bedienten Kunden gehört, der andere gibt die Attributwerte heraus, nach denen ein Zeilenfilter aussiebt.
  4. 4
    CIAS
    Rolle fehlt → 403, der CDMS-Dienst lehnt die Anfrage ab
  5. 5
    CIAS
    Rolle passt → Antwort
    Ergebnis: Der CDMS-Dienst merkt sich die Antwort für die eingestellte Zeit (voreingestellt 30 s)

Warum nicht einfach das Benutzer-Token weiterreichen? Zwei Gründe: der angemeldete Mensch hat keinen Anlass, eine Mandanten-Lookup-Rolle zu tragen, und es gibt Anfragen ganz ohne Benutzer-Token – ein Zeitgeber, eine Bereitschaftsprüfung –, die dann nichts mitzuschicken hätten.

Die Einstellungen dafür:

codamai:
  cias:
    tenancy:
      client:
        credentials:
          token-uri: https://iam.example.com/realms/codamai/protocol/openid-connect/token
          client-id: cdms-node
          client-secret: ${CIAS_LOOKUP_CLIENT_SECRET}
  • Ein Client, dem ein Teil fehlt, bricht den Start ab.
  • Ohne Client nimmt der Dienst ein festes Token aus codamai.cias.tenancy.client.token. Das erneuert sich nicht.
  • Was passiert, wenn Keycloak nicht antwortet oder den Client abweist, steht unter Wenn CIAS nicht erreichbar ist.

Mehr zu Dienstkonten unter Anmelden als Dienst.

In der Gegenrichtung gilt dasselbe Muster: CIAS liest GET /cias/fetch mit einem eigenen Leser-Token, das es ebenso bei Keycloak holt. Welche Realm-Rolle dort reicht, sagt die CDMS-Seite (codamai.cdms.cias.reader-roles, vorbelegt mit declaration-reader). Die Antwort ist die komplette Rechtekarte der Anwendung, also nichts, was öffentlich sein dürfte.

Wie CIAS die Deklaration holt

Getrennt steht in der CIAS-Konfiguration statt einer Bean eine URL:

codamai:
  cias:
    authorization:
      declarations:
        reader-token: ${CIAS_DECLARATION_TOKEN}
        modules:
          - name: cias
            client: cias-backend
            bean: ciasIdentityRegistry
          - name: cdms
            client: cdms-backend
            url: https://cdms.internal/cias/fetch

Beide Formen kommen nebeneinander vor: CIAS ist selbst ein Modul und liest seine eigene Deklaration als Bean, während CDMS ein Dienst woanders ist. Jeder Eintrag nennt entweder bean oder url, und jeder nennt den Keycloak-Client, auf dem seine Rollen landen. Getrennt hat jeder Dienst seinen eigenen Client.

Nur 200 gilt als Antwort. Ein 404 heißt hier nicht „deklariert nichts“, sondern „der Endpunkt steht nicht, wo die Konfiguration sagt“ – sonst würde ein Tippfehler in der URL beim nächsten Abgleich alle Rollen dieses Moduls stilllegen. Was der Abgleich dann tut, steht unter Module melden ihre Rollen an und Der Abgleich mit Keycloak.

Eine Anfrage von vorn bis hinten

sequenceDiagram
    participant B as BFF
    participant F as Filterkette (in CDMS)
    participant R as cias-tenancy-client
    participant S as CIAS-Dienst
    participant C as CDMS
    participant DB as Mandanten-DB
    B->>F: POST /api/rest/crm/customer/query + Token
    F->>F: Token prüfen, tauschen, Mandant auflösen
    F->>R: darf "kunde-a" bedient werden?
    R->>S: GET /cias/lookup/tenants/kunde-a (Dienst-Token)
    S-->>R: 200 served true
    R-->>F: ja (30 s gemerkt)
    F->>R: Attributwerte der Person in "kunde-a"?
    R->>S: GET /cias/lookup/users/…/attributes
    S-->>R: 200 regionen nord
    R-->>F: Werte (30 s gemerkt)
    F->>C: RequestContext gefüllt
    C->>DB: SELECT … (nur erlaubte Zeilen)
    DB-->>C: Zeilen
    C-->>B: data + meta

Beide Aufrufe entfallen, solange die Antwort im Gedächtnis liegt. Das Gedächtnis sitzt in cias-authentication, also im CDMS-Dienst und je Knoten – nicht im kleinen Client und nicht bei CIAS.

Die eigene Datenbank von CIAS

Der CIAS-Dienst hat seine eigene Datenbank (CIAS_DATABASE_URL). Er leitet nichts nach Mandanten um: alle CIAS-Tabellen sind Systemtabellen, es gibt keine Datenbank je Kunde.

Wer hält was
CDMS-Dienst
  • System-Datenbank mit den eigenen Tabellen
  • eine Datenbank je Mandant, je nach Persistenzziel
  • gegebenenfalls einen Dateispeicher
CIAS-Dienst
  • eine Datenbank, ohne Mandantenaufteilung
  • je Modul ein eigener Migrationslauf mit eigener Historientabelle
  • keine Geschäftsdaten – Benutzer, Mandanten, Rollen, Gruppen, Vorgänge

Eine Installation kann beide auf dieselbe Datenbank zeigen lassen – die CIAS-Tabellen sind genau die Tabellen, die eingebettet in der System-Datenbank liegen. Nötig ist das nicht.

Wenn CIAS nicht antwortet

Nur getrennt kann CIAS allein ausfallen. Dann entscheidet das Gedächtnis:

Mandanten-Tor und Attribut-Lookup bei Ausfall
CIAS antwortet?schon gemerkt?Ergebnis für die Anfrage
ja–CIAS entscheidet, die Antwort wird gemerkt
neinjadie letzte bekannte Antwort gilt weiter, so alt sie ist
neinneinabgelehnt – im Zweifel zu

In einem Satz: ein Ausfall darf niemanden hinauswerfen, der schon arbeitete, und niemanden hereinlassen, der es nicht tat. Eine gemerkte Ablehnung bleibt dabei eine Ablehnung – „weiterlaufen“ heißt, die letzte Antwort zu behalten, nicht eine günstige anzunehmen.

Als Ausfall zählt dabei mehr, als man denkt:

  • eine Zeitüberschreitung oder eine abgelehnte Verbindung,
  • ein 401 oder 403 – das sind die eigenen Zugangsdaten dieses Dienstes, kein Urteil über den Kunden,
  • eine Antwort ohne das Feld served, denn ein fehlendes Feld darf nicht als „nicht bedient“ gelesen werden,
  • beim Attribut-Lookup auch ein 404, denn eine Person ohne Eintrag wird mit 200 und leerer Liste beantwortet.

Details stehen unter Wenn etwas ausfällt und Den Mandanten zulassen.

Fallen

Weiter

Quellen im Code und in der Wissensdatenbank
  • CIAS/cias-tenancy-client – CLAUDE.md, pom.xml, CiasTenancyClientConfiguration, CiasTenancyClientProperties (base-url, token, 2 s), RemoteTenantLookupAdapter, RemoteTenantBoundAttributeAdapter, ClientCredentialsTenantLookupCredentials, CiasTenancyClientCredentialsProperties, StaticTenantLookupCredentials, TenantLookupCredentials
  • CIAS/cias-tenancy – TenantLookupController (GET /cias/lookup/tenants/{key}), TenantLookupRoles, CiasTenancyConfiguration (lookup-rest)
  • CIAS/cias-user – AttributeLookupController (GET /cias/lookup/users/{id}/attributes), AttributeLookupRoles
  • CIAS/cias-authentication – TenantGate (Ausfallregel, TTL 30 s), AttributeLookup, TenantGateProperties
  • CIAS/cias-authorization – RemoteModuleDeclarationAdapter, DeclarationReaderCredentials; CIAS/cias-spring-boot-starter – CiasDeclarationAutoConfiguration
  • CDMS/cdms-authorization – CiasApi (GET /cias/fetch), CiasReaderRoles
  • CIAS/cias-runtime – pom.xml, application.yml (CIAS_DATABASE_URL, lookup local, lookup-rest, declarations.modules)
  • CDMS/cdms-scaffold – cdms-version-registry.yaml (ciasDependencies REMOTE), CdmsScaffoldService, CdmsReadmeWriter
  • CIAS/cias-kernel/docs/adr/adr-022-tenant-lookup-port.md; CIAS/CLAUDE.md §6, §38
Suchen