symfony acl

Symfony2 ACL combinado con otros criterios



(2)

¿Has considerado usar un votante? Hay una receta de libro de cocina para implementar un votante de lista negra de IP, pero podría modificarse fácilmente para controlar la edición de los objetos de Comentario.

Puede ver el AclVoter predeterminado en Symfony/Component/Security/Acl/Voter/AclVoter (en línea here ), aunque el suyo obviamente puede aumentar en lugar de reemplazarlo y ser mucho más simple.

Como una prueba rápida de concepto:

class CommentTimestampVoter implements VoterInterface { public function supportsAttribute($attribute) { return ''edit'' === $attribute; } public function vote(TokenInterface $token, $object, array $attributes) { // 1. check if $token->getUser() has ROLE_ADMIN and return VoterInterface::ACCESS_GRANTED if so // 2. check if $token->getUser() equals $object->getAuthor() and return VoterInterface::ACCESS_DENIED if not // 3. check that $object->getCreatedAt() is within the window allowed for editing and return VoterInterface::ACCESS_GRANTED if so // 4. return VoterInterface::ACCESS_DENIED } public function supportsClass($class) { return ''Acme/CommentBundle/Entity/Comment'' === $class; } }

Me pregunto si alguien sabe de una manera elegante de lograr esto usando el sistema Symfony2 ACL.

Tengo una entidad Comment (mi objeto de dominio) que debe ser editable por ROLE_USER pero solo se permite dentro de los 5 minutos ROLE_USER del comentario; de lo contrario, ROLE_ADMIN solo podrá editar el ROLE_ADMIN .

Hacer que solo pueda ser editado por ROLE_USER y ROLE_ADMIN es simple, solo haga una RoleSecurityIdentity para cada uno.

Ahora mi problema ocurre cuando quiero incorporar el factor de tiempo para ROLE_USER . Mi primer problema es que necesita información del objeto de dominio, no solo de la tabla de ACL, pero creo que esto se puede resolver haciendo una clase ObjectIdentity personalizada que también puede contener el tiempo en que se publicó el Comment .

Ahora para la parte difícil

Creo que necesito crear una estrategia de PermissionGrantingStrategy personalizada que sepa mirar también la hora de creación. Esto tiene que cargarse cuando se comprueba un tipo de Comment , pero no sé cómo hacerlo cargar. ¿Alguien sabe si hay algún tipo de fábrica a través de la cual se pueda configurar este tipo de cosas? ¿De modo que si una entidad tiene una PermissionGrantingStrategy específica asociada a ella, entonces se usa o, de lo contrario, se utiliza el valor predeterminado?

Sé que este es un poco largo, muchas gracias si alguien sabe cómo lograr esto ya que la documentación de ACL parece un poco escasa en este momento. Mi solución alternativa es simplemente hacer algún tipo de servicio para verificar si un Comentario puede ser editado y no molestar con ACL en absoluto.


Estoy publicando esta solución para que otros puedan ver mi código final, pero aquí están las trampas que encontré al implementar un Voter como sugirió Problematic.

supportsAttribute : parece que cuando llamas al método isGranted en SecurityContext que no comprueba realmente este método antes de delegar una vote a una VoterInterface dentro de tu método de vote debes verificar los atributos tú mismo.

supportsClass : en la respuesta anterior del problema parecía que este método podría ser una clave para una selección basada en Factory de la cual VoterInterface puede votar, pero en realidad la documentación de symfony2 dice:

El método supportsClass () se usa para verificar si el votante admite la clase de token de usuario actual.

Por lo tanto, en realidad parece pertenecer a si el Voter admite o no el tipo de token. Para empeorar las cosas, el PHP Doc parece vago:

Verifica si el votante apoya la clase dada.

En cualquier caso, el principal problema es que este método nunca es verificado por el SecurityContext antes de delegar la llamada al método de vote de cualquier votante, incluso si este método está codificado para return false vote return false , ¡aún se llamará!

Así que, básicamente, la moraleja de la historia parecía ser: compruebe los $attributes y $object ingresan en el método de vote forma manual.

Mi código:

services.yml

parameters: comment_voter.class: Acme/Bundle/CommentBundle/Security/Authorization/Voter/CommentVoter services: comment_voter: class: %comment_voter.class% arguments: [@service_container] public: false tags: - { name: security.voter }

y la clase de votante:

<?php namespace Acme/Bundle/CommentBundle/Security/Authorization/Voter; use Symfony/Component/Security/Core/Authorization/Voter/VoterInterface; use Symfony/Component/Security/Core/Authentication/Token/TokenInterface; use Acme/Bundle/CommentBundle/Entity/Comment; use Symfony/Component/Security/Core/User/UserInterface; /** * A class to check editing privileges for Comments. */ class CommentVoter implements VoterInterface { const AUTHOR_EDIT_TIME_LIMIT = 300; private $container; public function __construct($container) { $this->container = $container; } public function supportsAttribute($attribute) { return $attribute === ''EDIT''; } public function supportsClass($class) { return true; } /** * Checks whether or not the current user can edit a comment. * * Users with the role ROLE_COMMENT_MODERATOR may always edit. * A comment''s author can only edit within 5 minutes of it being posted. * * {@inheritdoc} */ public function vote(TokenInterface $token, $object, array $attributes) { if ( !($object instanceof Comment) ) { return VoterInterface::ACCESS_ABSTAIN; } // Only supports ''EDIT'' for now. if ( !$this->supportsAttribute($attributes[0]) ) { return VoterInterface::ACCESS_ABSTAIN; } $user = $token->getUser(); if ( !($user instanceof UserInterface) ) { return VoterInterface::ACCESS_DENIED; } // Is the token a comment moderator? if ( $this->container->get(''security.context'')->isGranted(''ROLE_COMMENT_MODERATOR'') ) { return VoterInterface::ACCESS_GRANTED; } // Is the token the author of the post and within the edit window. $originalRevision = $object->getOriginalRevision(); if ( $originalRevision->getAuthor()->equals($user) ) { if ( (time() - $originalRevision->getCreationDate()->getTimestamp()) <= self::AUTHOR_EDIT_TIME_LIMIT ) { return VoterInterface::ACCESS_GRANTED; } } return VoterInterface::ACCESS_DENIED; } }

y finalmente plantilla:

{% if is_granted(''EDIT'', comment) %}<a href="#">Edit</a>{% endif %}

Espero que esto ayude a alguien más en el futuro y un gran agradecimiento a Problematic por apuntarme en la dirección de los votantes.