Symfony2 ACL与另一个标准相结合

时间:2011-10-24 17:04:08

标签: symfony acl

我想知道是否有人知道使用Symfony2 ACL系统实现这一目标的优雅方法。

我有一个Comment实体(我的域名对象)需要ROLE_USER可编辑,但这只能在发布评论的5分钟内允许 - 否则评论只能由ROLE_ADMIN

只有ROLE_USERROLE_ADMIN才能对其进行修改很简单,只需为每个RoleSecurityIdentity制作一个ROLE_USER

现在,当我想要合并ObjectIdentity的时间因素时,我的问题就出现了。我的第一个问题是它需要来自域对象的信息,而不仅仅是ACL表,但我认为这可以通过创建一个自定义Comment类来解决,该类也可以保留PermissionGrantingStrategy发布的时间。

现在是困难的部分

我想我需要创建一个自定义Comment,它也知道创建时间。在检查PermissionGrantingStrategy类型时必须加载它,但我不知道如何加载它。有谁知道是否有某种工厂可以配置这种东西?因此,如果某个实体有一个与之关联的特定{{1}},那么它会被使用,否则会使用默认值?

我知道这有点长,非常感谢有人知道如何实现这一点,因为ACL文档目前看起来有点稀疏。我的后备解决方案是简单地提供某种服务来检查是否可以编辑注释,而不是根本不打扰ACL。

2 个答案:

答案 0 :(得分:36)

我发布这个解决方案,以便其他人可以看到我的最终代码,但这是我在实施有问题的Voter时发现的陷阱。

supportsAttribute :当您在isGranted上调用SecurityContext方法时,它似乎在委派vote来电之前未实际检查此方法到VoterInterface所以在vote方法中你实际上必须自己检查属性。

supportsClass :在上面的问题答案中,似乎这个方法可能是基于工厂的选择的关键,VoterInterface可以投票,但实际上symfony2文档读取:

  

supportsClass()方法用于检查选民是否支持当前用户令牌类。

因此,它实际上似乎与Voter是否支持令牌类型有关。更糟糕的是,PHP Doc看起来很模糊:

  

检查选民是否支持给定的班级。

无论如何,主要问题是,在将调用委托给任何选民的SecurityContext方法之前,vote永远不会检查此方法 - 即使此方法是硬编码到return false vote仍然会被调用!

所以基本上这个故事的寓意似乎是:手动检查$attributes和[{1}}进入$object方法。

我的代码:

services.yml

vote

和选民班:

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

}

我希望这对未来的其他人有所帮助,并且非常感谢有问题的我指向选民的方向。

答案 1 :(得分:24)

您是否考虑过使用选民?有一个cookbook recipe用于实现IP黑名单选民,但可以很容易地修改它以处理对Comment对象的编辑检查。

你可以在Symfony\Component\Security\Acl\Voter\AclVoter(在线here)查看默认的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;
    }
}