2014-12-06 65 views
4

我正在尝试创建一个自定义选民来检查实体访问特定操作的访问权限。所以这个逻辑工作正常。但是,如果用户是该实体的“所有者”,或者他们是管理员,则我有一些允许的操作。Symfony2自定义选民角色层次

但是,我不能只检查用户的角色,因为我正在查看角色层次结构。在这个文档中的例子只是使用in_array,但这将不起作用(http://symfony.com/doc/current/best_practices/security.html

我的选民是这样的(为了清晰起见缩短)。我已经尝试了注入安全上下文(或2.6中的AuthorizationCheckerInterface),但由于这是一个选民,因此它具有循环依赖。

<?php 
// ... 
class ApplicationVoter extends AbstractVoter 
{ 
    const VIEW = 'view'; 

    /** 
    * @var AuthorizationCheckerInterface 
    */ 
    private $security; 

    /*public function __construct(AuthorizationCheckerInterface $security) 
    { 
     $this->security = $security; 
    }*/ 

    /** 
    * {@inheritdoc} 
    */ 
    protected function getSupportedAttributes() 
    { 
     return array(
      self::VIEW 
     ); 
    } 

    /** 
    * {@inheritdoc} 
    */ 
    protected function getSupportedClasses() 
    { 
     return array('Study\MainBundle\Entity\Application'); 
    } 

    /** 
    * {@inheritdoc} 
    */ 
    protected function isGranted($attribute, $application, $user = null) 
    { 
     if (!$user instanceof UserInterface) { 
      return false; 
     } 

     if ($attribute === self::VIEW) { 
      return $this->canView($application, $user); 
     } 

     return false; 
    } 

    /** 
    * Can view own application if not deleted 
    * Admin can view if submitted 
    * 
    * @param \Study\MainBundle\Entity\Application $application 
    * @param \Study\MainBundle\Entity\User $user 
    * 
    * @return boolean 
    */ 
    protected function canView(Application $application, User $user) 
    { 
     return ($application->isOwner($user) && !$application->isDeleted()) 
      || (!$application->isHiddenToAdmin() && $this->security->isGranted('ROLE_ADMIN_RO')); 
    } 
} 

我想在这里使用内置的RoleHiearchyVoter,但它是一个非公共服务。 有没有解决这个问题的方法?我希望避免重复框架代码,或者尽可能避免使用比字符串更复杂的角色。

编辑:注入整个容器的作品,但是不是我理想的解决方案。这是我可以从选民访问内置层次结构的唯一方法吗?

回答

9

有一个叫做security.role_hierarchy的服务,它有你需要的信息。基本上,安全上下文是如何检查角色的。需要几行包装代码,但它不是太糟糕。

# Need this because the service is not public 
# http://symfony.com/doc/current/components/dependency_injection/advanced.html 
cerad_core__role_hierarchy: 
    alias: security.role_hierarchy 

cerad_game__game_official__voter: 
    class: Cerad\Bundle\GameBundle\Action\GameOfficial\GameOfficialVoter 
    public: false 
    arguments: 
     - '@cerad_core__role_hierarchy' 
    tags: 
     - { name: security.voter } 

选民类:

class GameOfficialVoter implements VoterInterface 
{  
    public function __construct($roleHierarchy) 
    { 
     $this->roleHierarchy = $roleHierarchy; 
    } 

    protected function hasRole($token,$targetRole) 
    { 
     $reachableRoles = $this->roleHierarchy->getReachableRoles($token->getRoles()); 
     foreach($reachableRoles as $role) 
     { 
      if ($role->getRole() == $targetRole) return true; 
     } 
     return false; 
    } 

    protected function canViewOfficialName($official,$token) 
    {  
     // Pending is the only one protected against for now 
     if ($official->getAssignState() != 'Pending') return $this->accessGranted; 

     // Assignors can always see 
     if ($this->hasRole($token,'ROLE_ASSIGNOR')) return $this->accessGranted; 

     return $this->accessDenied; 
    } 
} 
+1

我想我只是被愚蠢和假设'公共=“假”'这些服务意味着我无法将它们注入。我不知道为什么我没有尝试这个。 – Matt 2014-12-06 08:17:16

+0

@Matt - 我想知道为什么我在我的代码中有一个别名。自从我看了它已经有一段时间了。如果不先创建别名,则无法访问角色层次结构服务。我的答案已更新。复制后编辑的危险。 http://symfony.com/doc/current/components/dependency_injection/advanced.html – Cerad 2014-12-06 13:57:22

+0

它看起来像服务不公开,你可以注入它的services.yml/xml配置,但你不能从它容器直接。别名允许你直接从容器中获取它('$ container-> get(...)')。我不需要使用别名通过配置注入服务。谢谢! – Matt 2014-12-08 02:35:41