Я публикую это решение, чтобы другие могли увидеть мой окончательный код, но вот подводные камни, которые я обнаружил при реализации Избирателя в качестве проблематичного предложения.
поддерживает атрибут : похоже, что когдаВы вызываете метод isGranted
для SecurityContext
, чтобы он на самом деле не проверял этот метод, прежде чем делегировать вызов vote
для VoterInterface
, поэтому внутри вашего метода vote
вы фактически должны проверить атрибуты самостоятельно.
supportClass : в ответе проблемной части выше, казалось, что этот метод может быть ключом для выбора на основе Фабрики, за который VoterInterface
s может голосовать, но фактически документация symfony2 гласит:
Метод supportsClass () используется для проверки того, поддерживает ли избиратель текущий класс токенов пользователя.
Поэтому на самом деле кажется, что на самом деле Voter
поддерживает токен или нет.тип.Что еще хуже, PHP Doc кажется расплывчатым:
Проверяет, поддерживает ли избиратель данный класс.
В любом случае, главная проблема в том, что этот метод никогда не проверяетсяSecurityContext
перед передачей вызова методу vote
любого избирателя - , даже если этот метод жестко закодирован в return false
vote
, и все равно будет вызываться!
Таким образом, в основном мораль этой истории казалась следующей: проверьте $attributes
и $object
, входящие в метод vote
, вручную.
Мой код:
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 }
и класс избирателей:
<?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;
}
}
и, наконец, шаблон:
{% if is_granted('EDIT', comment) %}<a href="#">Edit</a>{% endif %}
Я надеюсь, что это поможет кому-то еще в будущем и большое спасибо Problematic за то, что он указал мне в сторону Избирателей.