【问题标题】:Symfony 2 - ACL check permission based on 'separate' rolesSymfony 2 - 基于“单独”角色的 ACL 检查权限
【发布时间】:2014-01-08 04:10:51
【问题描述】:

假设我们有 3 个主要角色直接绑定到数据库表 userROLE_USERROLE_MODERATORROLE_ADMIN

但是,我们还有一些其他角色,用于Crews 组件(请参阅下面的 UML)。我对Crew 中执行的操作使用以下角色:ROLE_CREW_BOSSROLE_CREW_LEFTHANDROLE_CREW_RIGHTHANDROLE_CREW_MEMBER

+----------------+ +------------------+ |用户 | |工作人员 | |----------------| |-----------------| |编号 | |编号 | |用户名 现金 | |角色 | | +-------------------+ | | ... | | ... | | |船员 | | | | | | | |-------------------| | | | +----------------+ | |船员ID +--------------+ | | +----+ 用户 ID | +--------^----------+ |角色 | | | ... | +------------+ | | | | | | +------------------+ | | | |论坛主题 | | | | |-----------------| | | | |编号 | +-------------------+ +---+ 船员 ID | |标题 | |说明 | | ... | | | | | | | +------------------+

那是基础结构,我希望这部分清楚。现在问题来了……

问题

每个具有ROLE_MODERATOR 角色的用户都可以创建ForumTopic 对象,但不能创建设置crew_id 的对象,因为该对象对于特定工作人员来说是私有的。此外,只有具有ROLE_CREW_BOSSROLE_CREW_LEFTHANDROLE_CREW_RIGHTHAND 角色的工作人员(也是用户)可以编辑其工作人员的论坛主题。我如何检查这种复杂性?可能是Voter

更新 1

我已经解决了 50% 的问题,但它并不可靠。我已经为 Entity\\ForumTopic 对象创建了一个特定的选民。

public function vote(TokenInterface $token, $object, array $attributes)
{
    if ($object instanceof ObjectIdentityInterface) {
        if ($object->getType() == 'Entity\\ForumTopic') {

            /**
             * @var Member $member
             */
            $member = $token->getUser();

            $userTable = new UserTable();
            $user = $userTable->getByMember($member);

            $userInCrewTable = new UserInCrewTable();
            $crewMember = $userInCrewTable->getByUser($user);

            if ($crewMember && in_array($crewMember->getRole(), array('boss', 'lefthand', 'righthand'))) {
                return self::ACCESS_GRANTED;
            }
        }
    }

    return self::ACCESS_ABSTAIN;
}

这里唯一的问题是我不使用各自的角色,所以我不能使用角色层次结构功能。有人对我当前的解决方案有更好的解决方案或改进吗?

谢谢!

史蒂芬

【问题讨论】:

  • +1 表示数据库图 XD
  • 请告诉我,有一个工具可以创建这样的关系图:)
  • @V-Light 现在确定我使用的是什么,但只需谷歌“ASCII 图”,例如asciiflow.com

标签: symfony acl


【解决方案1】:

我会使用 Symfony acl:

// creating the ACL
$aclProvider = $this->get('security.acl.provider');
$objectIdentity = ObjectIdentity::fromDomainObject($comment);
$acl = $aclProvider->createAcl($objectIdentity);

$roleSecurityIdentity = new RoleSecurityIdentity('ROLE_CREW');
$securityIdentity = $roleSecurityIdentity;

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

【讨论】:

    【解决方案2】:

    Symfony 的默认角色系统是角色绑定到用户。从这个角度来看,在你的 manyToMany 表中包含角色字段是没有意义的。

    您想要的是基于用户船员的授权,因此您可能应该使用 ACL 功能,并且仅将角色用于全局权限。

        $objectIdentity = ObjectIdentity::fromDomainObject($forumTopic);
        $acl = $aclProvider->createAcl($objectIdentity);
    
        $securityIdentity = UserSecurityIdentity::fromAccount($user);
    
        // grant owner access
        $acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_EDIT);
        $aclProvider->updateAcl($acl);
    

    (您可以在http://symfony.com/doc/current/cookbook/security/acl.html 上查看更多文档。您也可以使用优秀的https://github.com/Problematic/ProblematicAclManagerBundle

    你把它和一个选民结合起来:

    function vote(TokenInterface $token, $object, array $attributes)
    {
        if ($object instanceof ObjectIdentityInterface) {
            if ($object->getType() == 'Entity\\ForumTopic') {
    
                /**
                 * @var Member $member
                 */
                $member = $token->getUser();
    
                if(in_array('ROLE_MODERATOR', $member->getRoles() && empty($object->getCrew()) {
                    return self::ACCESS_GRANTED;
                }
    
                // inject security component via dependecy injection
                // delegate further check to ACL
                if ($this->container['security']->isGranted('EDIT', $object)) {
                    return self::ACCESS_GRANTED;
                }
            }
        }
    

    【讨论】:

      【解决方案3】:

      您可以触摸解决方案! 如果你想检查角色,你只需要做一些事情。 首先,将您的 Voter 注册为服务,以便使用安全上下文构建它:

      将此添加到您的 services.yml 文件中:

      services:
          your_app.security.voter.forum_topic_owner:
              class: Your\AppBundle\Security\Authorization\Voter\ForumTopicOwnerVoter
              arguments: ["@security.context"]
              tags:
                  - { name: security.vote
      

      现在,您必须定义一个构造函数来获取 securityContext 并在投票方法中使用它:

      <?php
      
      namespace Your\AppBundle\Security\Authorization\Voter;
      
      use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
      use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
      use Symfony\Component\Security\Core\SecurityContext;
      
      class ForumTopicOwnerVoter implements VoterInterface
      {
          /** @var SecurityContext */
          protected $securityContext;
      
          /**
           * @param SecurityContext     $securityContext SecurityContext is the main entry point of the Security component.
      
           */
          public function __construct(SecurityContext $securityContext)
          {
              $this->securityContext = $securityContext;
          }
      
          /**
           * {@inheritDoc}
           */
          public function supportsAttribute($attribute)
          {
              return 'FORUM_TOPIC_OWNER' === $attribute;
          }
      
          /**
           * {@inheritDoc}
           */
          public function supportsClass($class)
          {
              return $class->getType() == 'Entity\\ForumTopic';
          }
      
          /**
           * {@inheritDoc}
           */
          public function vote(TokenInterface $token, $forumTopic, array $attributes)
          {
              foreach ($attributes as $attribute) {
                  if ($this->supportsAttribute($attribute) && $this->supportsClass($forumTopic)) {
                      $user = $token->getUser();
                      if ($user->hasRole('ROLE_CREW_BOSS')
                          or $this->securityContext->isGranted('ROLE_LEFTHAND')
                          ) {
                              return VoterInterface::ACCESS_GRANTED;
                      }
                  }
              }
      
              return VoterInterface::ACCESS_DENIED;
          }
      }
      

      现在,你有一个 Voter,你必须在 ForumTopic 对象上调用它,显然你知道如何做到这一点,这不是你的问题,我可以建议你看看众所周知的 Jms 的 SecureParam 注释/SecurityExtraBundle。这是在控制器操作中使用它的一种方法:

      namespace Your\AppBundle\Controller;
      
      use Symfony\Bundle\FrameworkBundle\Controller\Controller;
      use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
      use JMS\SecurityExtraBundle\Annotation\Secure;
      use JMS\SecurityExtraBundle\Annotation\SecureParam;
      
      /**
       * ForumTopic controller.
       *
       */
      class ForumTopicController extends Controller
      
      /**
       * Edit an existing forum topic entity.
       *
       * @param Request    $request    An HTTP request.
       * @param ForumTopic $forumTopic A forumTopic entity.
       *
       * @Secure(roles="ROLE_CREW")
       * @SecureParam(name="forumTopic", permissions="FORUM_TOPIC_OWNER")
       * @ParamConverter("forumTopic", class="YourAppBundle:ForumTopic")
       */
      public function editAction(Request $request, ForumTopic $forumTopic)
      {
          //Add here your logic
      }
      

      希望对你有所帮助!

      祝你好运!

      干杯。

      【讨论】:

        猜你喜欢
        • 2014-04-21
        • 1970-01-01
        • 2017-04-08
        • 2018-05-29
        • 2023-03-15
        • 2014-06-17
        • 1970-01-01
        • 2017-12-24
        • 2021-09-16
        相关资源
        最近更新 更多