ACL et contrôleurs
Tout d'abord: il s'agit le plus souvent de choses / couches différentes. Lorsque vous critiquez le code de contrôleur exemplaire, il met les deux ensemble - de toute évidence trop serré.
tereško a déjà décrit un moyen de dissocier cela davantage avec le motif de décoration .
Je ferais un pas en arrière pour rechercher le problème initial auquel vous êtes confronté et en discuter un peu ensuite.
D'une part, vous voulez avoir des contrôleurs qui ne font que le travail auquel ils sont commandés (commande ou action, appelons-le commande).
D'autre part, vous voulez pouvoir mettre ACL dans votre application. Le domaine de travail de ces ACL devrait être - si j'ai bien compris votre question - de contrôler l'accès à certaines commandes de vos applications.
Ce type de contrôle d'accès a donc besoin de quelque chose d'autre qui rapproche les deux. Sur la base du contexte dans lequel une commande est exécutée, l'ACL entre en action et des décisions doivent être prises pour savoir si une commande spécifique peut être exécutée ou non par un sujet spécifique (par exemple l'utilisateur).
Résumons à ce point ce que nous avons:
- Commander
- ACL
- Utilisateur
Le composant ACL est ici central: il a besoin de savoir au moins quelque chose sur la commande (pour identifier la commande pour être précis) et il doit être capable d'identifier l'utilisateur. Les utilisateurs sont normalement facilement identifiés par un identifiant unique. Mais souvent dans les applications Web, il y a des utilisateurs qui ne sont pas du tout identifiés, souvent appelés invités, anonymes, tout le monde etc. Pour cet exemple, nous supposons que l'ACL peut consommer un objet utilisateur et encapsuler ces détails. L'objet utilisateur est lié à l'objet de demande d'application et l'ACL peut le consommer.
Qu'en est-il de l'identification d'une commande? Votre interprétation du modèle MVC suggère qu'une commande est composée d'un nom de classe et d'un nom de méthode. Si nous regardons de plus près, il existe même des arguments (paramètres) pour une commande. Il est donc valable de se demander ce qui identifie exactement une commande? Le nom de la classe, le nom de la méthode, le nombre ou les noms d'arguments, même les données à l'intérieur de l'un des arguments ou un mélange de tout cela?
Selon le niveau de détail dont vous avez besoin pour identifier une commande dans votre ACL, cela peut beaucoup varier. Pour l'exemple, gardons-le simplement et spécifions qu'une commande est identifiée par le nom de la classe et le nom de la méthode.
Ainsi, le contexte de l'appartenance de ces trois parties (ACL, Commande et Utilisateur) est maintenant plus clair.
Nous pourrions dire qu'avec un composant ACL imaginaire, nous pouvons déjà faire ce qui suit:
$acl->commandAllowedForUser($command, $user);
Voyez simplement ce qui se passe ici: en rendant la commande et l'utilisateur identifiables, l'ACL peut faire son travail. Le travail de l'ACL n'est pas lié au travail de l'objet utilisateur et de la commande concrète.
Il ne manque qu'une partie, celle-ci ne peut pas vivre dans l'air. Et ce n'est pas le cas. Vous devez donc localiser l'endroit où le contrôle d'accès doit intervenir. Voyons ce qui se passe dans une application Web standard:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
Pour localiser cet endroit, nous savons qu'il doit l'être avant que la commande concrète ne soit exécutée, nous pouvons donc réduire cette liste et n'avons besoin de regarder que dans les endroits (potentiels) suivants:
User -> Browser -> Request (HTTP)
-> Request (Command)
À un moment donné de votre application, vous savez qu'un utilisateur spécifique a demandé à exécuter une commande concrète. Vous faites déjà une sorte de ACL ici: Si un utilisateur demande une commande qui n'existe pas, vous ne permettez pas à cette commande de s'exécuter. Donc, partout où cela se produit dans votre application, cela peut être un bon endroit pour ajouter les «vrais» contrôles ACL:
La commande a été localisée et nous pouvons créer son identification afin que l'ACL puisse la traiter. Dans le cas où la commande n'est pas autorisée pour un utilisateur, la commande ne sera pas exécutée (action). Peut-être qu'un CommandNotAllowedResponse
au lieu du CommandNotFoundResponse
pour le cas une demande ne pourrait pas être résolue sur une commande concrète.
L'endroit où le mappage d'une HTTPRequest concrète est mappé sur une commande est souvent appelé Routage . Comme le routage a déjà le travail de localiser une commande, pourquoi ne pas l'étendre pour vérifier si la commande est réellement autorisée par ACL? Par exemple , en étendant le Router
à un routeur en ACL: RouterACL
. Si votre routeur ne connaît pas encore le User
, alors le Router
n'est pas le bon endroit, car pour que l'ACL fonctionne non seulement la commande mais aussi l'utilisateur doit être identifié. Donc, cet endroit peut varier, mais je suis sûr que vous pouvez facilement localiser l'endroit que vous devez étendre, car c'est l'endroit qui remplit les exigences de l'utilisateur et de la commande:
User -> Browser -> Request (HTTP)
-> Request (Command)
L'utilisateur est disponible depuis le début, Commandez d'abord avec Request(Command)
.
Donc, au lieu de placer vos vérifications ACL dans l'implémentation concrète de chaque commande, vous la placez avant. Vous n'avez pas besoin de motifs lourds, de magie ou autre, l'ACL fait son travail, l'utilisateur fait son travail et surtout la commande fait son travail: juste la commande, rien d'autre. La commande n'a aucun intérêt à savoir si des rôles lui s'appliquent ou non, si elle est gardée quelque part ou non.
Alors gardez simplement les choses séparées qui n'appartiennent pas les unes aux autres. Utilisez une légère reformulation du principe de responsabilité unique (SRP) : Il ne devrait y avoir qu'une seule raison de changer une commande - parce que la commande a changé. Pas parce que vous introduisez maintenant ACL dans votre application. Pas parce que vous changez l'objet utilisateur. Pas parce que vous migrez d'une interface HTTP / HTML vers une interface SOAP ou de ligne de commande.
L'ACL dans votre cas contrôle l'accès à une commande, pas la commande elle-même.
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(sinon, affichez "Vous n'avez pas accès au profil de cet utilisateur" ou quelque chose comme ça? Je ne comprends pas.