Symfony 2 - 基于'分离'的ACL检查权限角色

时间:2013-12-19 14:29:48

标签: symfony acl

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

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



      +----------------+                                     +------------------+
      | users          |                                     | crews            |
      |----------------|                                     |------------------|
      | id             |                                     | id               |
      | username       <---+                                 | name             |
      | password       |   |                             +---> cash             |
      | roles          |   |    +-------------------+    |   | ...              |
      | ...            |   |    | crew_members      |    |   |                  |
      |                |   |    |-------------------|    |   |                  |
      +----------------+   |    | crew_id +--------------+   |                  |
                           +----+ user_id           |        +--------^---------+
                                | roles             |                 |
                                | ...               |    +------------+
                                |                   |    |
                                |                   |    |   +------------------+
                                |                   |    |   | forum_topics     |
                                |                   |    |   |------------------|
                                |                   |    |   | id               |
                                +-------------------+    +---+ crew_id          |
                                                             | title            |
                                                             | description      |
                                                             | ...              |
                                                             |                  |
                                                             |                  |
                                                             |                  |
                                                             +------------------+

这是基础结构,我希望这部分是清楚的。现在问题出现了......

问题

具有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;
}

这里唯一的问题是我没有使用相应的角色,因此我不能使用角色层次结构功能。任何人都能获得更好的解决方案或改进我目前的解决方案吗?

谢谢!

斯特芬

3 个答案:

答案 0 :(得分:3)

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

您想要的是基于授权的用户 on the crew ,因此您应该使用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;
            }
        }
    }

答案 1 :(得分:0)

我会使用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 :(得分:0)

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

在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;
    }
}

现在,你有一个选民,你必须在一个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
}

我希望它对你有所帮助!

祝你好运!

干杯。