ACL y controladores
En primer lugar: estas son cosas / capas diferentes con mayor frecuencia. A medida que critica el código ejemplar del controlador, une ambos, obviamente demasiado estricto.
tereško ya describió una forma de desacoplar esto más con el patrón de decorador.
Primero retrocedería un paso para buscar el problema original al que se enfrenta y luego lo discutiría un poco.
Por un lado, desea tener controladores que solo hagan el trabajo que se les ordenó (comando o acción, llamémoslo comando).
Por otro lado, desea poder poner ACL en su aplicación. El campo de trabajo de estas ACL debería ser, si entendí bien su pregunta, controlar el acceso a ciertos comandos de sus aplicaciones.
Por lo tanto, este tipo de control de acceso necesita algo más que los una. Según el contexto en el que se ejecuta un comando, ACL se activa y es necesario tomar decisiones sobre si un sujeto específico puede ejecutar o no un comando específico (por ejemplo, el usuario).
Resumamos hasta este punto lo que tenemos:
El componente ACL es fundamental aquí: necesita saber al menos algo sobre el comando (para identificar el comando para ser precisos) y necesita poder identificar al usuario. Normalmente, los usuarios se identifican fácilmente mediante una identificación única. Pero a menudo en las aplicaciones web hay usuarios que no están identificados en absoluto, a menudo llamados invitados, anónimos, todos, etc. Para este ejemplo asumimos que la ACL puede consumir un objeto de usuario y encapsular estos detalles. El objeto de usuario está vinculado al objeto de solicitud de la aplicación y la ACL puede consumirlo.
¿Qué hay de identificar un comando? Su interpretación del patrón MVC sugiere que un comando está compuesto por un nombre de clase y un nombre de método. Si miramos más de cerca, incluso hay argumentos (parámetros) para un comando. Entonces, ¿es válido preguntar qué identifica exactamente un comando? ¿El nombre de la clase, el nombre del método, el número o los nombres de los argumentos, incluso los datos dentro de cualquiera de los argumentos o una mezcla de todo esto?
Dependiendo del nivel de detalle que necesite para identificar un comando en su ACL, esto puede variar mucho. Para el ejemplo, hagámoslo simple y especifiquemos que un comando se identifica por el nombre de la clase y el nombre del método.
Entonces, el contexto de cómo estas tres partes (ACL, Comando y Usuario) se pertenecen entre sí ahora es más claro.
Podríamos decir, con un componente ACL imaginario ya podemos hacer lo siguiente:
$acl->commandAllowedForUser($command, $user);
Solo vea lo que sucede aquí: al hacer que tanto el comando como el usuario sean identificables, la ACL puede hacer su trabajo. El trabajo de la ACL no está relacionado con el trabajo tanto del objeto de usuario como del comando concreto.
Solo falta una parte, esto no puede vivir en el aire. Y no es así. Por lo tanto, debe ubicar el lugar donde debe activarse el control de acceso. Echemos un vistazo a lo que sucede en una aplicación web estándar:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
Para ubicar ese lugar, sabemos que debe ser antes de que se ejecute el comando concreto, por lo que podemos reducir esa lista y solo necesitamos buscar en los siguientes lugares (potenciales):
User -> Browser -> Request (HTTP)
-> Request (Command)
En algún momento de su aplicación, sabe que un usuario específico ha solicitado realizar un comando concreto. Ya realiza algún tipo de ACL aquí: si un usuario solicita un comando que no existe, no permite que ese comando se ejecute. Entonces, donde sea que suceda en su aplicación, podría ser un buen lugar para agregar las verificaciones de ACL "reales":
El comando ha sido localizado y podemos crear la identificación del mismo para que la ACL pueda manejarlo. En caso de que el comando no esté permitido para un usuario, el comando no se ejecutará (acción). Tal vez en CommandNotAllowedResponse
lugar de CommandNotFoundResponse
en el caso de que una solicitud no se pueda resolver en un comando concreto.
El lugar donde la asignación de una HTTPRequest concreta se asigna a un comando a menudo se llama Enrutamiento . Dado que el enrutamiento ya tiene la tarea de localizar un comando, ¿por qué no extenderlo para verificar si el comando está realmente permitido por ACL? Por ejemplo, mediante la ampliación de la Router
a un router consciente ACL: RouterACL
. Si su enrutador aún no conoce el User
, entonces Router
no es el lugar correcto, porque para que el ACL funcione no solo el comando sino también el usuario debe estar identificado. Entonces, este lugar puede variar, pero estoy seguro de que puede ubicar fácilmente el lugar que necesita para extender, porque es el lugar que cumple con los requisitos de usuario y comando:
User -> Browser -> Request (HTTP)
-> Request (Command)
El usuario está disponible desde el principio, Comando primero con Request(Command)
.
Entonces, en lugar de poner sus comprobaciones de ACL dentro de cada la implementación concreta de comando, lo coloca antes. No necesita ningún patrón pesado, magia o lo que sea, la ACL hace su trabajo, el usuario hace su trabajo y especialmente el comando hace su trabajo: solo el comando, nada más. El comando no tiene interés en saber si se le aplican roles o no, si está protegido en algún lugar o no.
Así que mantén las cosas separadas que no se pertenecen entre sí. Utilice una nueva redacción leve del principio de responsabilidad única (SRP) : debe haber solo una razón para cambiar un comando: porque el comando ha cambiado. No porque ahora introduzca ACL en su aplicación. No porque cambie el objeto Usuario. No porque migre de una interfaz HTTP / HTML a una interfaz SOAP o de línea de comandos.
La ACL en su caso controla el acceso a un comando, no el comando en sí.
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(de lo contrario, mostrar "No tienes acceso al perfil de este usuario" o algo así? No lo entiendo.