Si estamos desarrollando nuestra aplicación web en Symfony 4 y hemos optado por usar como «bundle» de administración Sonata, llegará un momento si la aplicación es medianamente compleja que tendremos que definir la gestión de privilegios a aplicar a los usuarios que pueden acceder al entorno de administración o «backend». La documentación de Sonata nos da básicamente 2 alternativas para gestionar la seguridad: Gestión mediante ROLES (sonata.admin.security.handler.role) La gestión mediante ROLES consiste en definir roles a los que asociar a los usuarios y en función de dichos roles dar acceso a modificar, eliminar, etc las diferentes entidades de la aplicación. Esta opción es poco flexible ya que si le das permiso a un rol para modificar un tipo de objeto podrá modificar todos los objetos de ese tipo sin restricción. Gestión mediante Listas de control de acceso ACL (sonata.admin.security.handler.acl) La gestión mediante ACL es bastante compleja y en Symfony 4 se ha eliminado del desarrollo principal y se necesita instalar un «bundle» adicional para disponer de ella: ACL Bundle. Como alternativa a ACL en la documentación de Symfony se recomienda usar voters, combinándolos con los roles de usuario podemos abordar la gestión de privilegios en Sonata sin necesidad de implementar ACL. Puedes ver y descargar todo el código que comentaré a continuación desde los enlaces de interés. Combinando roles y voters en Symfony 4 y Sonata Vamos a plantear un ejemplo en el que ver el uso de voters de forma sencilla. Estamos desarrollando una aplicación web para la gestión de concesionarios de una marca de automóviles, los usuarios con el rol de «concesionario» tendrán acceso de edición a los vehículos vendidos o por vender. Los propietarios de los vehículos podrán ver aquellos vehículos que posean y los administradores de la plataforma tendrán todos esos privilegios y también la opción de eliminar los vehículos. Existirán por tanto estos roles en la aplicación: ROLE_ADMIN (Administradores) ROLE_CONCESIONARIO (trabajadores del concesionario) ROLE_PROPIETARIO (Propietario de vehículos) Por comodidad en la administración, usaremos los grupos de usuarios que proporciona Sonata User Bundle y crearemos 3 grupos de usuarios. Con el propio panel de administración de Sonata aparte de asignar los usuarios a grupos también podremos darle roles específicos si fuese necesario, los 3 grupos de usuarios serían: La estructura de la base de datos quedaría de la siguiente forma: La configuración de roles en config/packages/security.yaml para implementar la política de seguridad deseada quedaría de la siguiente forma: [crayon-67000fe666978778109906/] Con esta configuración de roles delimitamos los accesos por objetos, los administradores pueden listar, editar, crear, ver y eliminar vehículos, los concesionarios listar, editar y ver y los propietarios solo listar y ver. Sin el uso de voters estos privilegios son generales y da igual si los vehículos pertenecen o no a un concesionario que los usuarios con ese rol los podrán editar todos al igual que un propietario podrá ver cualquier vehículo sin necesidad de que le pertenezca. Mantengo el rol de superusuario (ROLE_SUPER_ADMIN) que tendrá acceso a todo por encima de nuestra política de seguridad. Para añadir el uso de voters debemos realizar los siguientes cambios en los archivos de configuración: config/packages/security.yaml [crayon-67000fe66697e272073999/] Añadimos la estrategia «unanimous» al determinar el acceso o no, ya que nosotros vamos a definir unas políticas mediante voters pero aparte tenemos los Roles, al obligar a que la decisión sea unánime, todas las políticas deben permitir el acceso para que éste se conceda. Se pueden ver las diferentes estrategias disponibles en la documentación de Symfony. config/packages/sonata_admin.yaml [crayon-67000fe66698b195057400/] Definimos nuestro propio gestor de seguridad en la configuración de Sonata Admin, y vamos a implementarlo, creamos el archivo src/Security/Handler/VoterSecurityHandler.php con el siguiente contenido: [crayon-67000fe66698e797190391/] Esta clase con ligeras variaciones la obtuve de este gran artículo (Usando Voters con Sonata) de Sergio Gómez, que explica e implementa el sistema de voters con Sonata en versiones de Symfony anteriores a la 4. Te recomiendo que consultes en artículo de Sergio para obtener más información del funcionamiento de la clase. Damos de alta el servicio en config/services.yaml: [crayon-67000fe666990688849009/] Ahora vamos a crear nuestro voter para el objeto vehículo que determinará quien puede hacer qué con él según los criterios que hemos establecido. Creamos el archivo src/Voter/VehiculoVoter.php con el siguiente contenido: [crayon-67000fe666993100139335/] Con el método «supports» nos aseguramos que el objeto se del tipo deseado (Vehículo) y que las acciones a comprobar sean las que tiene definidas dicho objeto. Luego con el método «votoOnAttribute» implementamos la lógica que queremos, en la línea 30 damos acceso completo a los usuarios que tienen los roles SUPER_ADMIN y/o ADMIN, de la 34 a la 39 comprobamos para aquellos usuarios con rol CONCESIONARIO que el vehículo en cuestión esté dado de alta en al menos uno de los concesionarios en los que están dados de alta los usuarios, y por último de la línea 41 a la 44 comprobamos que los usuarios con el rol PROPIETARIO además sean propietarios del vehículo. Un tema importante a tener en cuenta es que es recomendable ir de roles con más privilegios a menos, así aquellos usuarios que tengan varios roles por ejemplo un usuario que esté a la vez en el grupo de administradores y concesionarios, con la primera comprobación ya se le concede el acceso si lo hacemos al revés habría que pasar por varias comprobaciones. Ahora ya tenemos implementada la política de seguridad deseada, salvo un detalle, Sonata en los listados no aplica los voters y muestra todos los resultados, por ejemplo si accedemos con el usuario «propietario1» al listado de vehículos vemos los siguiente: El usuario «propietario1» solo puede entrar en la ficha del vehículo del que es propietario tal y como hemos definido en el voter, pero sí puede ver el resto de vehículos aunque no sean suyos, para solventarlo debemos modificar la consulta encargada de generar el listado, modificamos la clase Admin de vehículo para Sonata: [crayon-67000fe666995015753280/] Si volvemos a acceder al listado de vehículos con el usuario «propietario1», tenemos el listado que buscamos: Este sería el listado
Más info