Zum Inhalt springen

Attributbasierte Filter

Kurzantwort: Ein Attributfilter verknüpft ein Attribut des Benutzerprofils (z. B. projects) mit einem Datenfeld des Modells (z. B. projectId). Der Benutzer sieht nur Zeilen, deren Feldwert in seinen Attributwerten vorkommt. Ein * im Attribut hebt die Einschränkung auf; ein fehlendes oder leeres Attribut führt zum Fehler – niemals zu vollem Zugriff.


Rollen sagen „darf Kunden lesen”. Attributfilter sagen „darf diese Kunden lesen” – ohne für jedes Projekt, jede Filiale oder jeden Bereich eine eigene Rolle anzulegen.

Am Modell:

@AccessByAttribute(attribute = "projects", field = "projectId")

attribute ist der Name des Profilattributs, field der Feldpfad im Modell (auch company.id ist möglich).

Der Generator erzeugt daraus einen {Model}AttributeFilter, der von AbstractAttributeFilter<T> erbt und als Spring-Bean vorliegt. Der System-Layer sammelt alle CdmsFilterInterface-Beans des Modells ein und hängt ihre Filter an jede Abfrage.

flowchart TB
    A["Profilattribut lesen"] --> B{"vorhanden?"}
    B -->|nein| E1["422 missing-attribute-on-profile"]
    B -->|ja| C["Werte zerlegen:<br/>mehrere Einträge und/oder<br/>kommaseparierte Werte"]
    C --> D{"enthält '*'?"}
    D -->|ja| N["kein Filter – alle Zeilen sichtbar"]
    D -->|nein| F{"Anzahl Werte"}
    F -->|0| E2["422 empty-attribute-on-profile"]
    F -->|1| EQ["Filter: feld EQ wert"]
    F -->|">1"| IN["Filter: feld IN wert1,wert2,…"]
FallErgebnis
Attribut fehlt im ProfilAttributeValidationException (422) – kein Zugriffsrecht, also keine Daten
Attribut vorhanden, aber ohne brauchbaren WertAttributeValidationException (422) – ein leeres Recht ist ein kaputtes Profil, kein Vollzugriff
mindestens ein Wert ist *kein Filter – der Aufrufer sieht alles
genau ein WertEQ auf dem Datenfeld
mehrere WerteIN auf dem Datenfeld

Die Werte dürfen als mehrere Array-Einträge, als ein kommaseparierter Eintrag oder gemischt kommen; Leerzeichen werden getrimmt, Duplikate entfernt.

Aus dem Token: Keycloak bildet Benutzerattribute auf Claims ab. Der TokenParser übernimmt jeden Claim, der kein Protokoll-Claim ist, als Benutzerattribut nach RequestContext.effectiveUserAttributes – immer als String[].

// Ausschnitt aus dem Token
{ "projects": ["a1b2", "c3d4"], "region": "nord" }

Was der Server tatsächlich sieht, zeigt GET /cias/fetch.

Alle Filter werden mit AND an die Wurzel der Abfrage gehängt:

(Client-Query) AND _userId = <benutzer> AND projectId IN (a1b2, c3d4)

Sie sind als mandatory markiert – ein Filter, der sich nicht in ein Prädikat übersetzen lässt, bringt die Anfrage zum Scheitern statt weggelassen zu werden. Ein weggelassener Sicherheitsfilter würde alle Zeilen liefern.

Der Filter wirkt außerdem beim Schreiben: assertVisibleForWrite(id) prüft mit demselben Filtersatz, ob die Zeile überhaupt sichtbar ist – sonst 404.

Für Logik jenseits eines einfachen Attributvergleichs lässt sich CdmsFilterInterface<T> direkt implementieren; siehe Eigene Filter.