Symfony2 ACL в сочетании с другими критериями - PullRequest
27 голосов
/ 24 октября 2011

Мне интересно, знает ли кто-нибудь об элегантном способе достижения этого с помощью системы ACL Symfony2.

У меня есть объект Comment (мой доменный объект), который необходимо редактировать с помощью ROLE_USERно это разрешено только в течение 5 минут после публикации комментария - в противном случае комментарий может быть отредактирован только с помощью ROLE_ADMIN.

, поэтому его можно редактировать только с помощью ROLE_USER и ROLE_ADMINпросто, просто сделайте RoleSecurityIdentity для каждого.

Теперь моя проблема возникает, когда я хочу включить фактор времени для ROLE_USER.Моя первая проблема заключается в том, что ему нужна информация из объекта домена, а не только из таблицы ACL, но я думаю, что это можно решить, создав собственный класс ObjectIdentity, который также может содержать время публикации Comment.

Теперь о сложной части

Я думаю, мне нужно создать пользовательский PermissionGrantingStrategy, который знает, как посмотреть время создания.Это должно быть загружено, когда проверяется тип Comment, но я не знаю, как заставить его загружаться.Кто-нибудь знает, есть ли какая-то фабрика, с помощью которой можно настроить такие вещи?Так что, если у сущности есть определенный PermissionGrantingStrategy, связанный с ним, тогда он используется, в противном случае используется значение по умолчанию?

Я знаю, что это немного длинно, большое спасибо, если кто-нибудь знает, как этого добитьсяпоскольку документация ACL кажется немного редкой в ​​данный момент.Мое альтернативное решение состоит в том, чтобы просто сделать какую-то услугу, чтобы проверить, можно ли редактировать комментарий, и вообще не беспокоиться о ACL.

Ответы [ 2 ]

36 голосов
/ 28 октября 2011

Я публикую это решение, чтобы другие могли увидеть мой окончательный код, но вот подводные камни, которые я обнаружил при реализации Избирателя в качестве проблематичного предложения.

поддерживает атрибут : похоже, что когдаВы вызываете метод 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 за то, что он указал мне в сторону Избирателей.

24 голосов
/ 25 октября 2011

Рассматривали ли вы использование избирателя? Существует рецепт поваренной книги для реализации избирателя, занесенного в черный список IP-адресов, но его можно легко изменить, чтобы выполнить проверку на наличие изменений в объектах комментариев.

Вы можете посмотреть на AclVoter по умолчанию на Symfony\Component\Security\Acl\Voter\AclVoter (онлайн здесь ), хотя ваш может явно увеличить вместо того, чтобы заменить его, и будет намного проще.

В качестве быстрого доказательства концепции:

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;
    }
}
...