ACL und Controller
Zuallererst: Dies sind meistens verschiedene Dinge / Schichten. Wenn Sie den beispielhaften Controller-Code kritisieren, werden beide zusammengefügt - am offensichtlichsten zu eng.
tereško hat bereits einen Weg aufgezeigt , wie Sie dies mit dem Dekorationsmuster mehr entkoppeln können.
Ich würde zuerst einen Schritt zurückgehen, um nach dem ursprünglichen Problem zu suchen, mit dem Sie konfrontiert sind, und das dann ein wenig diskutieren.
Einerseits möchten Sie Controller haben, die nur die Arbeit erledigen, die ihnen befohlen wurde (Befehl oder Aktion, nennen wir es Befehl).
Auf der anderen Seite möchten Sie ACL in Ihre Anwendung einfügen können. Das Arbeitsfeld dieser ACLs sollte - wenn ich Ihre Frage richtig verstanden habe - darin bestehen, den Zugriff auf bestimmte Befehle Ihrer Anwendungen zu steuern.
Diese Art der Zugangskontrolle benötigt daher etwas anderes, das diese beiden zusammenbringt. Basierend auf dem Kontext, in dem ein Befehl ausgeführt wird, wird ACL aktiviert und es muss entschieden werden, ob ein bestimmter Befehl von einem bestimmten Betreff (z. B. dem Benutzer) ausgeführt werden kann oder nicht.
Fassen wir bis zu diesem Punkt zusammen, was wir haben:
Die ACL-Komponente spielt hier eine zentrale Rolle: Sie muss mindestens etwas über den Befehl wissen (um den Befehl genau zu identifizieren) und den Benutzer identifizieren können. Benutzer sind normalerweise leicht durch eine eindeutige ID zu identifizieren. In Webanwendungen gibt es jedoch häufig Benutzer, die überhaupt nicht identifiziert werden, häufig als Gast, anonym, alle usw. bezeichnet. In diesem Beispiel wird davon ausgegangen, dass die ACL ein Benutzerobjekt verwenden und diese Details kapseln kann. Das Benutzerobjekt ist an das Anwendungsanforderungsobjekt gebunden und kann von der ACL verwendet werden.
Was ist mit der Identifizierung eines Befehls? Ihre Interpretation des MVC-Musters legt nahe, dass ein Befehl aus einem Klassennamen und einem Methodennamen besteht. Wenn wir genauer hinschauen, gibt es sogar Argumente (Parameter) für einen Befehl. Es ist also gültig zu fragen, was genau einen Befehl identifiziert. Der Klassenname, der Methodenname, die Anzahl oder die Namen der Argumente, sogar die Daten in einem der Argumente oder eine Mischung aus all dem?
Je nachdem, welchen Detaillierungsgrad Sie benötigen, um einen Befehl in Ihrer ACL zu identifizieren, kann dies sehr unterschiedlich sein. Lassen Sie es uns für das Beispiel einfach halten und angeben, dass ein Befehl durch den Klassennamen und den Methodennamen identifiziert wird.
Der Kontext, in dem diese drei Teile (ACL, Befehl und Benutzer) zueinander gehören, ist jetzt klarer.
Wir könnten sagen, mit einem imaginären ACL-Inhalt können wir bereits Folgendes tun:
$acl->commandAllowedForUser($command, $user);
Sehen Sie einfach, was hier passiert: Indem Sie sowohl den Befehl als auch den Benutzer identifizierbar machen, kann die ACL ihre Arbeit erledigen. Der Job der ACL hängt nicht mit der Arbeit des Benutzerobjekts und des konkreten Befehls zusammen.
Es fehlt nur ein Teil, dieser kann nicht in der Luft leben. Und das tut es nicht. Sie müssen also den Ort finden, an dem die Zugriffskontrolle aktiviert werden muss. Schauen wir uns an, was in einer Standard-Webanwendung passiert:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
Um diesen Ort zu finden, müssen wir wissen, dass er vor der Ausführung des konkreten Befehls ausgeführt werden muss, damit wir diese Liste reduzieren können und nur die folgenden (potenziellen) Orte untersuchen müssen:
User -> Browser -> Request (HTTP)
-> Request (Command)
Irgendwann in Ihrer Anwendung wissen Sie, dass ein bestimmter Benutzer die Ausführung eines konkreten Befehls angefordert hat. Sie führen hier bereits eine Art ACL durch: Wenn ein Benutzer einen Befehl anfordert, der nicht vorhanden ist, lassen Sie diesen Befehl nicht ausführen. Wo immer dies in Ihrer Anwendung geschieht, ist dies möglicherweise ein guter Ort, um die "echten" ACL-Prüfungen hinzuzufügen:
Der Befehl wurde gefunden und wir können ihn identifizieren, damit die ACL damit umgehen kann. Falls der Befehl für einen Benutzer nicht zulässig ist, wird der Befehl nicht ausgeführt (Aktion). Möglicherweise konnte eine CommandNotAllowedResponse
anstelle der CommandNotFoundResponse
für den Fall geltenden Anfrage nicht auf einen konkreten Befehl aufgelöst werden.
Der Ort, an dem die Zuordnung einer konkreten HTTPRequest einem Befehl zugeordnet wird, wird häufig als Routing bezeichnet . Da das Routing bereits die Aufgabe hat, einen Befehl zu finden, können Sie ihn erweitern, um zu überprüfen, ob der Befehl tatsächlich pro ACL zulässig ist. ZB durch Erweitern des Router
auf einen ACL-fähigen Router : RouterACL
. Wenn Ihr Router das noch nicht kennt User
, Router
ist das nicht der richtige Ort, denn damit die ACL funktioniert, muss nicht nur der Befehl, sondern auch der Benutzer identifiziert werden. Dieser Ort kann also variieren, aber ich bin sicher, dass Sie den Ort, den Sie erweitern müssen, leicht finden können, da dieser Ort die Benutzer- und Befehlsanforderungen erfüllt:
User -> Browser -> Request (HTTP)
-> Request (Command)
Benutzer ist von Anfang an verfügbar, Befehl zuerst mit Request(Command)
.
Anstatt Ihre ACL-Prüfungen in die konkrete Implementierung jedes Befehls einzufügen, platzieren Sie sie davor. Sie brauchen keine schweren Muster, Magie oder was auch immer, die ACL erledigt ihren Job, der Benutzer erledigt ihren Job und insbesondere der Befehl erledigt seinen Job: Nur der Befehl, sonst nichts. Der Befehl hat kein Interesse daran zu wissen, ob Rollen für ihn gelten oder nicht, ob er irgendwo bewacht ist oder nicht.
Halten Sie also einfach Dinge auseinander, die nicht zueinander gehören. Verwenden Sie eine leichte Umformulierung des Single Responsibility Principle (SRP) : Es sollte nur einen Grund geben, einen Befehl zu ändern - da sich der Befehl geändert hat. Nicht, weil Sie jetzt ACL'ing in Ihre Anwendung einführen. Nicht, weil Sie das Benutzerobjekt wechseln. Nicht, weil Sie von einer HTTP / HTML-Schnittstelle zu einer SOAP- oder Befehlszeilenschnittstelle migrieren.
Die ACL in Ihrem Fall steuert den Zugriff auf einen Befehl, nicht den Befehl selbst.
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(sonst "Sie haben keinen Zugriff auf das Profil dieses Benutzers" oder so etwas? Ich verstehe es nicht.