问题 如何在Symfony2中使用类范围的aces?


我有类范围的问题。我为a创造了一个王牌 像这样的类:

$userIdentity = UserSecurityIdentity::fromAccount($user);
$classIdentity = new ObjectIdentity('some_identifier', 'Class\FQCN');
$acl = $aclProvider->createAcl($classIdentity);
$acl->insertClassAce($userIdentity, MaskBuilder::MASK_CREATE);
$aclProvider->updateAcl($acl);

现在,我正在尝试检查用户的权限。我发现了这种方式 做事,没有记录,但给出了预期的 课程结果:

$securityContext->isGranted('CREATE', $classIdentity);  // returns true
$securityContext->isGranted('VIEW', $classIdentity);    // returns true
$securityContext->isGranted('DELETE', $classIdentity);  // returns false

此方法很好地适应了“CREATE”权限检查,其中 传递给方法没有可用的对象实例。然而, 应该可以检查是否授予了另一个权限 特定实例基础:

$entity = new Class\FQCN();
$em->persist($entity);
$em->flush();
$securityContext->isGranted('VIEW', $entity);  // returns false

这是测试失败的地方。我期望有一个给定的用户 一个类上的权限掩码对每个都具有相同的权限 该类的实例,如文档中所述(“The 如果,PermissionGrantingStrategy首先检查所有对象范围的ACE none是适用的,类范围的ACE将被检查“),但它 似乎不是这里的情况。


4957
2017-11-07 14:10


起源

此链接澄清了有关此事的一些细节:groups.google.com/forum/#!topic/symfony-devs/UR8PmwCAr40 - user2268997


答案:


你做得对。并根据底部 这一页它应该有效,但事实并非如此。

使其工作的最简单方法是创建一个AclVoter类:

namespace Core\Security\Acl\Voter;

use JMS\SecurityExtraBundle\Security\Acl\Voter\AclVoter as BaseAclVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Doctrine\Common\Util\ClassUtils;

class AclVoter extends BaseAclVoter
{
    public function vote( TokenInterface $token , $object , array $attributes )
    {   
    //vote for object first
    $objectVote =   parent::vote( $token , $object , $attributes ); 

    if( self::ACCESS_GRANTED === $objectVote )
    {
        return self::ACCESS_GRANTED;
    }
    else
    {
        //then for object's class
        $oid    =   new ObjectIdentity( 'class' , ClassUtils::getRealClass( get_class( $object ) ) );
        $classVote  =   parent::vote( $token , $oid , $attributes );                

        if( self::ACCESS_ABSTAIN === $objectVote )
        {       
        if( self::ACCESS_ABSTAIN === $classVote )
        {          
            return self::ACCESS_ABSTAIN;
        }
        else
        {
            return $classVote;
        }
        }
        else if( self::ACCESS_DENIED === $objectVote )
        {
        if( self::ACCESS_ABSTAIN === $classVote )
        {
            return self::ACCESS_DENIED;
        }
        else
        {
            return $classVote;
        }
        }       
    }

    return self::ACCESS_ABSTAIN;
    }
}

然后在security.yml中设置:

jms_security_extra:
    voters:
        disable_acl: true  

最后将选民设置为服务:

core.security.acl.voter.basic_permissions:
    class: Core\Security\Acl\Voter\AclVoter
    public: false
    arguments: 
      - '@security.acl.provider'
      - '@security.acl.object_identity_retrieval_strategy'
      - '@security.acl.security_identity_retrieval_strategy'
      - '@security.acl.permission.map' 
      - '@?logger'   
    tags:
        - { name: security.voter , priority: 255 }           
        - { name: monolog.logger , channel: security }    

6
2017-08-23 18:52



+1谢谢你。我在类似的轨道上,并没有完全让我的“基于类”的ACL工作。通过OP的问题和答案,我能够让我的ACL完美运行。 - lifo
我发现当我尝试将服务@ security.acl.object_identity_retrieval_strategy注入我的自定义选民时,我得到一个例外,说该服务不可用。这个服务没有在容器中注册吗? - calumbrodie
+1非常聪明的解决方案:)谢谢! - Steffen Brem


您需要确保每个对象都有自己的ACL(使用 $aclProvider->createAcl($entity))使类范围权限正常工作。

看到这个讨论: https://groups.google.com/forum/?fromgroups=#!topic/symfony2/pGIs0UuYKX4


5
2017-11-01 16:57



我必须创建对象范围和类范围acls以使类范围工作?你能用一个小小的例子说明你的想法吗? - metalvarez


如果您没有现有实体,则可以检查您创建的objectIdentity。 小心使用“双反斜杠”,因为反斜杠的逃逸。

$post = $postRepository->findOneBy(array('id' => 1));

$securityContext = $this->get('security.context');
$objectIdentity = new ObjectIdentity('class', 'Liip\\TestBundle\\Entity\\Post');

// check for edit access
if (true === $securityContext->isGranted('EDIT', $objectIdentity)) {
    echo "Edit Access granted to: <br/><br/> ";
    print_r("<pre>");
    print_r($post);
    print_r("</pre>");
} else {
    throw new AccessDeniedException();
}

这应该工作!

如果你要检查“对象范围”,你可以在isGranted函数调用中使用$ post而不是$ objectIdentity。


2
2018-02-19 13:14





我试图找到解决这个问题的最佳解决方案,我认为最好的答案是Bart的rryter /编辑。我只是想扩展解决方案。

假设您想要为特定用户提供特定对象类型的访问权限,但不能访问具体对象实例(例如id = 1)。

然后你可以做以下事情:

    $aclProvider = $this->get('security.acl.provider');
    $objectIdentity = new ObjectIdentity('class',    'someNamspace\\SeperatedByDoubleSlashes');
    $acl = $aclProvider->createAcl($objectIdentity);

    // retrieving the security identity of the currently logged-in user
    $securityContext = $this->get('security.context');
    $user = $securityContext->getToken()->getUser();
    $securityIdentity = UserSecurityIdentity::fromAccount($user);

    // grant owner access
    $acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER);
    $aclProvider->updateAcl($acl);

    $securityContext = $this->get('security.context');
    // check for edit access
    if (false === $securityContext->isGranted('EDIT', $objectIdentity)) {
        throw new AccessDeniedException();
    }

与symfony cookbook给出的示例的不同之处在于您使用的是类范围,而不是对象范围。 只有一条线有所不同:

$objectIdentity = new ObjectIdentity('class',    'someNamspace\\SeperatedByDoubleSlashes');

代替:

$objectIdentity = ObjectIdentity::fromDomainObject($object);

如果您的类acl中至少有一个类范围权限,您仍然可以为特定对象实例添加特定权限。


0
2018-01-27 11:27