Symfony2 ACL 结合另一个条件

Posted

技术标签:

【中文标题】Symfony2 ACL 结合另一个条件【英文标题】:Symfony2 ACL combined with another criteria 【发布时间】: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。

【问题讨论】:

【参考方案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 的方向。

【讨论】:

感谢@Problematic 和 kasheen,这将是 Symfony 食谱github.com/symfony/symfony-docs 的一个很好的补充;) 你应该提交 PR。【参考方案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 访问(它也可以在我的前端用于有条件地显示控件以编辑评论)。今晚我会试一试,如果可行,请勾选答案,但看起来你是赢家:) 谢谢! 好的,在实施此解决方案后,我会说这是要走的路,但我会在下面发布我的代码,以防它帮助任何人,因为我在创建选民时发现了很多陷阱,所以希望我的代码可以作为其他人的文档(尽管我不能保证正确性)。

以上是关于Symfony2 ACL 结合另一个条件的主要内容,如果未能解决你的问题,请参考以下文章

Symfony2 ACL 问题

symfony2 选民或 acl

Symfony2:如何获取标记有“编辑”ACL 权限的一种类型的所有实体?

为啥 Symfony2 ACL 使用用户名而不是 ID?

如何在 Symfony2 中制作高级 ACL?

Symfony 2 ACL 与选民