【问题标题】:Symfony2 ACL combined with another criteriaSymfony2 ACL 结合另一个条件
【发布时间】:2011-12-14 07:36:30
【问题描述】:

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

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

使其只能由ROLE_USERROLE_ADMIN 编辑很简单,只需为每个设置一个RoleSecurityIdentity

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

现在是困难的部分

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

我知道这有点长,如果有人知道如何实现这一点,非常感谢,因为 ACL 文档目前似乎有点稀疏。我的后备解决方案是简单地提供某种服务来检查评论是否可以编辑,并且根本不用 ACL。

【问题讨论】:

    标签: symfony acl


    【解决方案1】:

    我发布这个解决方案是为了让其他人可以看到我的最终代码,但这里是我在按照有问题的建议实施 Voter 时发现的缺陷。

    supportsAttribute:似乎当您在SecurityContext 上调用isGranted 方法时,它实际上并没有在将vote 调用委托给VoterInterface 之前检查此方法,所以在您的 vote 方法中,您实际上必须自己检查属性。

    supportsClass:在上面有问题的回答中,这种方法似乎是基于工厂的选择的关键,VoterInterfaces 可以投票,但实际上 symfony2 文档显示:

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

    因此,它实际上似乎与Voter 是否支持令牌类型有关。更糟糕的是,PHP Doc 似乎含糊不清:

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

    无论如何,主要问题是在将调用委托给任何选民的vote 方法之前,SecurityContext 永远不会检查此方法 - 即使此方法被硬编码为 @987654331 @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 为我指明了 Voters 的方向。

    【讨论】:

    【解决方案2】:

    您是否考虑过使用选民?有一个 cookbook recipe 用于实现 IP 黑名单投票器,但可以轻松修改它以处理对评论对象的编辑检查。

    您可以在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;
        }
    }
    

    【讨论】:

    • 这听起来像我需要的,很好地插入并且还可以通过安全上下文的 isGranted 访问(它也可以在我的前端用于有条件地显示控件以编辑评论)。今晚我会试一试,如果可行,请勾选答案,但看起来你是赢家:) 谢谢!
    • 好的,在实施此解决方案后,我会说这是要走的路,但我会在下面发布我的代码,以防它帮助任何人,因为我在创建选民时发现了很多陷阱,所以希望我的代码可以作为其他人的文档(尽管我不能保证正确性)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-17
    • 2016-10-11
    • 1970-01-01
    相关资源
    最近更新 更多