User Tools

Site Tools


patterns:inheritancerolestragegyoverview:strategy

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

patterns:inheritancerolestragegyoverview:strategy [2013/01/10 19:08]
rtavassoli
patterns:inheritancerolestragegyoverview:strategy [2013/01/10 21:01] (current)
rtavassoli
Line 35: Line 35:
     account.AuthenticateWith(strategy); // ****** HIER WIRD DIE STRATEGIE DAFÜR VERWENDET, WOZU SIE ÜBERHAUPT GEBAUT WURDE ******     account.AuthenticateWith(strategy); // ****** HIER WIRD DIE STRATEGIE DAFÜR VERWENDET, WOZU SIE ÜBERHAUPT GEBAUT WURDE ******
  
-    _authenticationStrategyRepository.Save(strategy); // nur Änderungen in der Strategie. Ansonsten müsste ein Domein Service daraus gemacht werden.+    _authenticationStrategyRepository.Save(strategy); // nur Änderungen in der Strategie. Ansonsten müsste ein Domain Service daraus gemacht werden.
 } }
 </code> </code>
Line 48: Line 48:
 </code> </code>
 Viel passiert hier nicht, d.h. die Authentifizierung direkt über die AuthenticationStrategy reicht vielleicht aus. Wozu der Account an die AuthenticationStrategie in Authenticate übergeben wird wird hier nicht verdeutlicht. Im Sinne von //tell, don't ask// kann Account hier z.B. übergeben ob das Konto aktiv ist, die eigene ID, damit die Strategie die Relation auch nochmal prüfen kann, usw. D.h. Account und AuthenticationStrategie fragen sich nicht gegenseitig nach ihren Eigenschaften um eigene Entscheidungen zu treffen, es werden Befehle gesendet. Viel passiert hier nicht, d.h. die Authentifizierung direkt über die AuthenticationStrategy reicht vielleicht aus. Wozu der Account an die AuthenticationStrategie in Authenticate übergeben wird wird hier nicht verdeutlicht. Im Sinne von //tell, don't ask// kann Account hier z.B. übergeben ob das Konto aktiv ist, die eigene ID, damit die Strategie die Relation auch nochmal prüfen kann, usw. D.h. Account und AuthenticationStrategie fragen sich nicht gegenseitig nach ihren Eigenschaften um eigene Entscheidungen zu treffen, es werden Befehle gesendet.
 +==== Erstellen eines Kontos mit einer Authentifizierungsstrategie ====
 +Es gibt zwei Möglichkeiten. Entweder sendet man die Befehle zur Erstellung des Kontos und der Strategie gemeinsam an einen transaktionalen Command Service((was immer gehen wird, solange wir die Daten transaktional speichern können und die Aggregates auf einem gemeinsamen Rechner leben - was bei so eng zusammen hängenden Aggregaten der Fall sein wird; die werden niemals auf unterschiedliche Rechner aufgeteilt)). Dann weiß man, dass ein Konto immer mit einer Strategie erzeugt wird.
 +\\ \\
 +Die Alternative ist die, dass man immer erst die Strategie speichert, danach das Konto((das sollte generell so gemacht werden, dass immer erst das Objekt, das referenziert wird, gespeichert wird, dann das Objekt, das die Referenz hält - ähnlich wie in einer Datenbank)). Wenn auch nur das Konto die Strategie referenziert und nicht anders herum, dann ist sicher gestellt, dass diese Referenz auch vorhanden ist wenn das Konto gespeichert wird((der mögliche Fall, dass mehrere Konten dieselbe Strategie referenzieren, wird beim Authentifizieren abgefangen. Das kann aber praktisch nicht passieren, nicht in 1.000.000 Jahren. Und wenn doch, wird es ja, wie gesagt, später entdeckt und immer abgefangen, bevor es zu echten Problemen führen kann)).
 +\\ \\ 
 +Es kann bei dieser Alternative nur passieren, dass die Strategie gespeichert wurde, das Konto aber nicht. In dem Fall hat man eine verwaiste Strategie ohne Konto. Damit der angegebene Benutzername in der Strategie nicht als //verwendet// gilt, sollten die verwendeten Benutzernamen einer Strategie über eine Sicht geprüft werden, die ein Konto mit einer Strategie verknüpft. Dafür wird ein Event Handler auf AccountAdded und KerberosAuthenticationStrategyAdded Ereignisse lauschen, und diese Sicht entsprechend aufbauen. Solange AccountAdded nicht erhalten wurde, gilt die Strategie als //nicht-existent// und blockiert den Usernamen nicht.
 +\\ \\
 +Blöd nur, wenn der Event Handler mal eine halbe Stunde nicht am Start ist. Dann würde der Anwender das Konto mit dem Benutzernamen immer wieder anlegen. Die Befehle würden erfolgreich durch laufen, weil nichts gegen die Neuanlage spricht, das Konto für "Ramin Tavassoli" mit dem Benutzernamen "rtavassoli" wird in der Sicht ja nicht gefunden, also ist alles gut. Sobald der Handler wieder am Start ist, gibt es zu "Ramin Tavassoli" plötzlich 8 Konten mit "rtavassoli" als Benutzernamen.
 +\\ \\
 +Die Konsequenzen wären nicht schlimm. Mit "rtavassoli" könnte sich nicht angemeldet werden, weil es den Benutzernamen mehr als einmal gibt, und daher nicht klar ist, welche KerberosAuthenticationStrategy verwendet werden soll. Also muss man sieben der Acht Konten wieder löschen.
 +\\ \\
 +In diesem Fall wäre das Spielchen vielleicht besser umgedreht. Erst das Konto speichern, dann die Strategie. Wenn die Strategie nicht angelegt werden kann, zeigt die AuthenticationStrategyId in Account ins Leere. Dann wird halt eine neue Strategie angelegt, und vorher die AuthenticationStrategyId im Account darauf geändert. Das kann solange laufen, bis es geklappt hat. Zumindest werden in den 8 Versuchen, in denen das Login "rtavassoli" neu angelegt wird, die alten immer abgelöst, am Ende gibt es also wirklich nur ein Konto mit "rtavassoli".
 +\\ \\
 +Das Problem hierbei wäre nur, dass man erlauben muss, die Authentifizierungsstrategie im Konto ändern zu können. Das wollte ich eigentlich nicht erlauben, aber andererseits schadet es ja auch nicht.
 +\\ \\
 +Ich halte es aber generell für äußerst bedenklich, dass es die Möglichkeit gibt, dass Befehle erfolgreich durch laufen, mit der Möglichkeit, dass man das Ergebnis nicht gleich sieht. Was, wenn ich die Person "Ramin Tavassoli" anlege, das Ergebnis aber nicht sehe? Dann lege ich sie wieder an. Immer noch nicht da? Also nochmal anlegen. Am Ende gibt es "Ramin Tavassol" drei mal. Wie löse ich das? Oder besser noch, wie verhindere ich das?
 +=== Synchrone Deserialisierer ===
 +  - Solange das funktioniert, werden Ereignisse synchron((innerhalb derselben Transaktion, in der das Ereignis geschrieben wird)) denormalisiert,
 +  - Befehle, die zusammen gehören((z.B. einen Kontext samt Strategie, oder eine Akteur samt Rolle)) werden gemeinsam geschickt, um in einer gemeinsamen Transaktion gespeichert zu werden,
 +  - Der Dienst, der Befehle abarbeitet, startet zu Beginn eine Transaktion und beendet sie am Ende der Aktion. Dort kann ein sinnvolles Timeout gesetzt werden((z.B. 10 Sekunden)), damit Befehle nicht minutenlang((oder theoretisch tagelang)) unsichtbar weiter machen, und plötzlich Daten erscheinen, mit denen gar nicht mehr gerechnet wurde,
 +  - Alle Sichten, Listen, Reports, usw. auf die synchron denormalisierten Sichten bauen. Nur Daten asynchron denormalisieren, wenn es absolut notwendig ist, und wenn der einzelne Fall wasserdicht gelöst ist((d.h. Mechanismen bauen, die dazu führen, dass dem Anwender z.B. klar ist, dass er alte Daten sieht, und dass das auch nicht weiter schlimm ist)),
 +  - Asynchrone Denormalisierer fragen regelmäßig die Datenlieferanten nach Ereignissen ab. Die Lieferanten liefern neue Ereignisse zusammen mit ihrer eigene Aktualität der Daten((der Lieferant kann selbst Daten über Lieferanten erfragen, d.h. die Datenaktualität hangelt sich so durch)). Die Denormalisierer schreiben zu jeder Sicht die Aktualität((Als Uhrzeit)). Wenn eine Sicht abgefragt wird, wird immer die Datenaktualität mit abgefragt((als Zeitraum, z.B. 1.00:02:23. Die Uhrzeit muss so nicht synchronisiert werden. Sie sollte lediglich auf dem Server nicht verstellt werden)).
 +Wann werde ich schon Domänen partitionieren müssen? Daher sollte ich mit der Lösung, alles synchron zu halten((außer Reports, bei denen der Anwender auch dahin erzogen werden kann, auf die Aktualität zu achten)), im Grunde sehr weit kommen. Ich komme ansonsten mit Eventual Consistency absolut nicht klar, kann einfach nicht akzeptieren, dass der Anwender das versteht, und vor allem nicht, dass Daten potenziell Stunden oder Tage alt sein können, trotzdem Entscheidungen getroffen werden und Aktionen durchgeführt werden. Wenn sogar ständig Entwickler in den Foren ihre Fragen und Antworten reposten, weil sie nicht sofort zu sehen sind, wie soll man das dann von einem //nicht technisch versierten Anwender// verlangen können?
 ==== So würde eine vollständige Kerberos Authentifizierung aussehen ==== ==== So würde eine vollständige Kerberos Authentifizierung aussehen ====
 +In diesem Fall muss der Client wissen, welche Strategie er zur Authentifizierung verwenden möchte, d.h. er muss explizit eine Kerberos Authentifizierung starten.  
 +Die Kerberos Authentifizierung läuft in mehreren Schritten ab. Das sollte aber wie folgt möglich sein: 
 +  - über den Benutzernamen((z.B. "rtavassoli")) fragt der Client nach der AuthenticationStrategyID und der AccountID. Dafür wird die Sicht verwendet, die ein Konto und die Authentifizierungsstrategie gemeinsam denormalisiert. Wenn die Sicht zu dem Benutzernamen mehr als eine Antwort findet, oder keine, wird ein entsprechender Fehler ausgelöst. 
 +  - der Client sendet nun eine Anfrage mit der AuthenticationStrategyID nach dem verschlüsselten AuthenticationKey und dem AuthenticationTicket an die Query-Seite des Kontos((hierfür muss ein Kerberos Query Dienst eingerichtet sein)), 
 +  - Der Kerberos Dienst geht hier direkt zum Repository((KerberosAuthenticationStrategy wird nicht mit CQRS, sondern mit CQS umgesetzt, kann also auch Fragen beantworten)), lädt die AuthenticationStrategie, wandelt sie in eine KerberosAuthenticationStrategy, und fragt sie nach den angeforderten Daten((wobei eine ICryptTickets Schnittstelle in dem Methdenaufruf mitgegeben wird)), 
 +  - Der Client entschlüsselt mit dem Kennwort den AuthenticationKey, verschlüsselt damit die ID und das Timeout, das mit dem letzten Aufruf zurück erhalten wurde, und sendet diese beiden zusammen mit dem AuthenticationTicket((das vom Client nicht entschlüsselt werden kann)) als KerberosCredentials an das Konto als Authenticate Befehl, 
 +  - Hinweis((noch sauberer in den Workflow einbauen, ist mir gerade eingefallen)): Der AuthenticateCredentials Befehl ist abstrakt. Das ist für Befehle für Strategien generell sinnvoll. Es wird dann einen Command Handler geben, der AuthenticateKerberosCredentials Befehle behandelt. Dieser wird dann das Konto über das Repository laden, und eine entsprechende Methode zur Authentifizierung aufrufen. Dieser Methode können neben den Credentials dann weitere Dinge übergeben werden, wie z.B. die ICryptTickets Schnittstelle, 
 +  - Der Command Handler aus dem vorherigen Punkt lädt also das Konto, und ruft die entsprechende Authenticate Methode der Authenticate Strategie auf((wobei eine ICryptTickets Schnittstelle in dem Methdenaufruf mitgegeben wird)). Wenn das klappt, wird ein Authenticated Ereignis veröffentlicht, das einen SessionKey enthält, verschlüsselt mit dem AuthenticationKey, und ein neues SessionTicket((die Tickets sind durch ICryptTickes verschlüsselt, so dass es auch nicht vom Client entschlüsselt werden kann. Sie enthalten u.a. die UserId, das Timeout und den Key. Mit dem Key werden die vom Client verschlüsselten UserId und Session-Timeout Daten entschlüsselt, um zu prüfen, ob der Client das alles mit dem korrekten Schlüssel verschlüsselt hat - den Schlüssel kann der Client ja nur haben, wenn er initial das Passwort zur entschüsselung kannte.)), 
 +  - Wenn das nicht klappt, wird NotAuthenticated Ereignis ausgelöst. Nach 5 Versuchen wird das Konto gesperrt, 
 +  - Der Client pollt mitlerweile nach dem Ergebnis der Anmeldung. Erhält er ein positives Ergebnis, ist er angemeldet. Er verschlüsselt dann die UserId und das Timeout mit dem erhaltenen SessionKey, den er vorher mit dem AuthenticationKey entschlüsseln muss, und sendet dieses gemeinsam mit dem SessionTicket in jedem Aufruf an den Server mit, 
 +  - Der Server braucht jetzt das Konto nicht mehr zu laden um den Dienstaufruf zu authentifizieren. Anhand der mit gesendeten Credentials wird über Service Locator ein IAuthenticateCredentials Dienst geladen. Der Dienst für die Kerberos Credentials kann das von ICryptTickets verschlüsselte Ticket entschlüsseln((Er ist i.d.R. der Dienst, der als ICryptTickets an das Konto übergeben wurde)) und mit dem darin enthaltenen SessionKey die vom Client mit dem SessionKey verschlüsselten Daten ermitteln, und die UserId und das Timeout in beiden vergleichen. Stimmt alles, dann kennt der Client den SessionKey, was er ja nur kann, wenn er ihn mit dem AuthenticationKey entschlüsseln konnte, den er vorher mit dem Passwort((mit dem MD5Hash vom Passort)) entschlüsseln konnte.
  
 ====== Alles alt und ersetzt!!! ====== ====== Alles alt und ersetzt!!! ======
patterns/inheritancerolestragegyoverview/strategy.1357841283.txt.gz · Last modified: 2013/01/10 19:08 by rtavassoli