Symfony2:创建一个新的SecurityIdentity

时间:2013-04-18 12:17:24

标签: symfony acl symfony-2.1

我在Symfony 2.1中使用ACL,我需要创建一个新的SecurityIdentity,以便我的ACL可以设置为某种组的功能。

请注意以下情况:存在具有用户(具有不同角色)的组,每个组都具有用户信息。在组1中,具有ROLE_ADMIN的用户无法从同一组的信息中编辑其他用户,但在组2中,具有ROLE_ADMIN的用户可以编辑其他用户信息。

所以基本上我的ACL会根据用户所在的组别而有所不同。

我以为我会通过创建新的“GroupSecurityIdentity”来解决这个问题。然而,这个类本身是不够的,因为当我使用它时会遇到这个异常:

$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.

我的问题是:如何“注册”我的新SecurityIdentity,以便将其用作RoleSecurityIdentity和UserSecurityIdentity?

有哪些更好的方法可以实现类似于我想要的系统?

2 个答案:

答案 0 :(得分:2)

2年前,我沿着那条路走下去,结果证明这是一个糟糕的决定。修改ACL系统很困难,在更新Symfony时可能会出现问题。至少有2个更好的解决方案。我会列出所有这些,以便您决定哪种最适合您的需求。

新的安全标识

我正在使用FOSUserBundle中的GroupInterface,但我想你也可以使用自己的AclProvider。需要添加以下文件:

接下来:通过提供以下参数重新连接依赖注入容器:

User

交叉手指的时间,看它是否有效。由于这是旧代码,我可能忘记了一些东西。

使用组的角色

这个想法是让组名与角色相对应。

一种简单的方法是让您的UserInterface::getRoles实体重新实施public function getRoles() { $roles = parent::getRoles(); // This can be cached should there be any performance issues // which I highly doubt there would be. foreach ($this->getGroups() as $group) { // GroupInterface::getRole() would probably have to use its // canonical name to get something like `ROLE_GROUP_NAME_OF_GROUP` $roles[] = $group->getRole(); } return $roles; }

GroupInterface::getRole()

public function getRole() { $name = $this->getNameCanonical(); return 'ROLE_GROUP_'.mb_convert_case($name, MB_CASE_UPPER, 'UTF-8'); } 的可能实现:

<?php

namespace Acme\Bundle\DemoBundle\Authorization\Voter;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;

class MySecureObjectVoter implements VoterInterface
{
    /**
     * {@inheritDoc}
     */
    public function supportsAttribute($attribute)
    {
        $supported = array('VIEW');

        return in_array($attribute, $supported);
    }

    /**
     * {@inheritDoc}
     */
    public function supportsClass($class)
    {
        return $class instanceof GroupableInterface;
    }

    /**
     * {@inheritDoc}
     */
    public function vote(TokenInterface $token, $object, array $attributes)
    {
        $result = VoterInterface::ACCESS_ABSTAIN;

        if (!$object instanceof MySecureObject) {
            return VoterInterface::ACCESS_ABSTAIN;
        }

        foreach ($attributes as $attribute) {
            if (!$this->supportsAttribute($attribute)) {
                continue;
            }

            // Access is granted, if the user and object have at least 1
            // group in common.
            if ('VIEW' === $attribute) {
                $objGroups = $object->getGroups();
                $userGroups = $token->getUser()->getGroups();

                foreach ($userGroups as $userGroup) {
                    foreach ($objGroups as $objGroup) {
                        if ($userGroup->equals($objGroup)) {
                            return VoterInterface::ACCESS_GRANTED;
                        }
                    }
                }

                return voterInterface::ACCESS_DENIED;
            }
        }
    }
}

现在只需要创建cookbook article中所写的所需ACE-s。

创建选民

最后,您可以使用自定义选民来检查特定组的存在以及用户是否有权访问所述对象。可能的实施:

{{1}}

有关选民的更多详情,请参阅cookbook example


我会避免创建自定义安全标识。使用提供的其他两种方法。如果您将拥有大量记录并且每个记录必须具有不同的访问设置,则第二种解决方案效果最佳。选民可以用来设置简单的访问授权逻辑(大多数小型系统似乎都属于这种逻辑)或者需要灵活性。

答案 1 :(得分:0)

我在这里写下我的答案,以便跟踪此错误消息。

我使用ACL实现了组支持,我不得不破解symfony核心&#34; MutableAclProvider.php&#34;

protected function getSelectSecurityIdentityIdSql(SecurityIdentityInterface $sid)
{
    if ($sid instanceof UserSecurityIdentity) {
        $identifier = $sid->getClass().'-'.$sid->getUsername();
        $username = true;
    } elseif ($sid instanceof RoleSecurityIdentity) {
        $identifier = $sid->getRole();
        $username = false;
    }else {
        //throw new \InvalidArgumentException('$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.');
        $identifier = $sid->getClass().'-'.$sid->getGroupname();
        $username = true;
    }

    return sprintf(
        'SELECT id FROM %s WHERE identifier = %s AND username = %s',
        $this->options['sid_table_name'],
        $this->connection->quote($identifier),
        $this->connection->getDatabasePlatform()->convertBooleans($username)
    );
}

即使提供的对象不是UserSecurityIdentity或RoleSecurityIdentity的实例,它也会返回一个值。所以现在我可以使用自定义&#34; GroupSecurityIdentity&#34;

它不容易实施,但很适合我的系统。