User Tools

Site Tools


concepts:identityandaccesscontrol:authorization

Autorisation

“Authorization is orthogonal to the domain”. In anderen Worten ist die Autorisation in den meisten Domänen1) nicht expliziter Bestandteil der Domäne, und sollte so gebaut werden, dass sie so wenig intrusiv ist wie möglich.

In den meisten Fällen kann einfach der Befehl genommen werden, zusammen mit dem authentifizieren Anwender, und es wird geprüft, ob der Anwender das Recht hat, den Befehl auszuführen. Ein Problem besteht, wenn der Befehl nicht alle notwendigen Informationen beinhalten, um ihn autorisieren zu können. Der Prozess der Autorisierung kann in dem Fall das read model bemühen, um die notwendigen Informationen zu ermitteln.

Key Koncepts

Fälle in denen das Read Model versagt

Angenommen es gibt folgende Befehle

  • Lege Termin für Mitarbeiter in Projekt an
  • Ändere Termin auf einen anderen Mitarbeiter
  • Ändere Termin auf ein anderes Projekt
  • Ändere die Termin Details

Angenommen es gibt folgende Rechte

  • Mitarbeiter dürfen eigene Termine anlegen und beliebig ändern
  • Projektleiter dürfen Termine in ihren Projekten für alle Mitarbeiter anlegen und beliebig ändern

Jetzt passiert folgendes

  1. Mitarbeiter ABC legt einen Termin in Projekt P1 für sich selbst an
  2. Mitarbeiter XYZ, Projektleiter im Projekt P1, bucht den Termin auf Mitarbeiter DEF um, und ändert dabei einige Details des Termins
  3. Mitarbeiter ABC ändert die Details des Termins

Schritt 3 sollte nicht mehr möglich sein wenn Mitarbeiter ABC nicht Projektleiter im Projekt P1 ist. Wenn die Autorisierung aber lediglich das read model verwendet, und die Umbuchung des Termins auf Mitarbeiter DEF im read model noch nicht sichtbar ist, wenn der dritte Befehl abgeschickt wird, würde der dritte Befehl autorisiert werden, weil der Absender und Termininhaber2) beide Mitarbeiter ABC sind. Mitarbeiter ABC würde somit fälschlicherweise einen Termin für Mitarbeiter XYZ ändern.

Das Modell ist das Problem

Der Workflow von oben zeigt, dass das Modell problematisch ist. Wenn ich Termindetails ändern möchte, schicke ich zwar meine Logindaten mit, damit der Service weiß, dass ich, Mitarbeiter ABC, die Änderungen abschicke. Aber weder in den Logindaten, noch im Befehl, steht drin, wem der Termin gehört. Der Befehl lautet z.B. “Ändere die Terminlaufzeit auf 08:00-12:00 für den Termin mit der ID 355DD55321EE”.

Im Grunde müsste ich den Befehl darüber autorisieren, indem ich mir die Entität(en), für die der Befehl gilt, hole, sie analysiere, und dann prüfe, ob der Befehlsgeber die nötigen Rechte für die gewünschten Änderungen hat. Ich würde mir also den Termin mit der Nummer 355DD55321EE holen, gucken für wen er ist, und dann prüfen, ob der Aufrufer das Recht für die gewünschte Änderung hat.

Das Modell ist also nur problematisch, wenn ich mit einer Kopie des Termins arbeite3), und den Befehl genehmige, ohne zu wissen, ob die Kopie überhaupt noch aktuell ist. In manchen Fällen mag das in Ordnung sein, z.B. wenn die relevanten Daten unveränderlich sind. In anderen Fällen kann es problematisch sein. In diesen Fällen müsste man sich die Entitäten direkt über Repositories geben lassen, in dem Wissen, dass der Befehl dann auch auf genau diese Entität angewendet wird4). Zudem müssen die relevanten Eigenschaften des Befehls abfragbar sein. CQS sollte immer zur Anwendung kommen, CQRS nur bedingt.

Zusammenfassend braucht der Autorisierer ein konsistentes Lesemodell um konsistent autorisieren zu können. Dafür wird die Domäne5) am einfachsten unter Einhaltung von CQS Eigenschaften veröffentlichen. Wenn weitere Eigenschaften für einen neuen Autorisierer benötigt werden, muss die Domäne halt erweitert werden. Zudem muss der Autorisierer sich daran halten, nur die Query Methoden der Aggregates zu verwenden, und keine Command Methoden.

Wüsste man im Vorfeld immer, welche Daten zum Autorisieren benötigt würden, würde der Autorisierer feste Methoden in der Domäne aufrufen, z.B. Aggregate.AuthorizeSetStatusWith(StatusId, this). Das Aggregate würde in der Methode im Autorisierer wiederum dieselbe Methode aufrufen6), dabei7) alle weiteren Eigenschaften übergeben.

Da das Autorisieren aber flexibel eingehängt werden können soll, und mal vom Mitarbeiter des Zeitdatensatzes abhängen kann, mal vom Projektvorgang, mal vom Kunden, usw…..

Ahhhhhh!!! Der Kunde kann ja niemals konsistent geprüft werden. Angenommen, ich darf einen Datensatz des Kunden ABC genehmigen, den von XYZ nicht - das darf niemand. Wenn ich nun einen Datensatz von Kunden ABC genehmige, und gleichzeitig der Kunde des Zeitdatensatzes auf XYZ geändert wird, das Lesemodell8) aber noch ABC hat, würde die Änderung genehmigt werden - und es gibt eine Zustand “Zeitdatensatz für Kunden XYZ ist genehmigt”, den es gar nicht geben dürfte.

Das muss somit noch anders gelöst werden. Veröffentlichen von Eigenschaften aus dem Aggregate heraus ist nur eine Teillösung, die das eigentliche Problem überhaupt nicht angeht!!!

Es gibt somit keine Königslösung. Wie in so vielen Fällen hängt die Lösung davon ab, was gefordert ist.

1) mit Ausnahme der Autorisations-Domäne
2) im Lesemodell, nicht in der Domäne!
3) aus dem read model
4) Geladene Entitäten im Aufruf müssen also gecached werden können
5) die Aggregates
6) this wäre in diesem Fall IAuthorizeSetStatus
7) konsistent
8) der Kunde ist nicht Teil des Zeitdatensatzes und muss über das Lesemodell ermittelt werden
concepts/identityandaccesscontrol/authorization.txt · Last modified: 2014/01/19 17:54 by rtavassoli