symfony2 acl groups

时间:2012-04-27 08:56:43

标签: symfony acl usergroups

通常我有以下商业模式:

有用户和群组。每个用户只属于一个组,并且未预先确定组的数量(以及大多数站点的用户数量)。 还有几个不同的繁忙对象,可能属于用户。

组不是单独的对象,它们应该由ACL本身控制,但是它们应该像unix组一样影响其他实体的控制方式。

有3个基本角色:SUPERADMIN,ADMIN和USER。

  • SUPERADMIN能够对任何实体做任何事情。
  • USER通常能够读/写自己的实体(包括他/她自己)并阅读 来自他/她的团队。
  • ADMIN应完全控制 他集团内的实体,但不是其他集团的实体。我不 了解如何在此处应用ACL继承(以及是否可以 完全适用。)

我也很感兴趣,如何在ACL中应用拒绝访问。用户对除登录以外的所有字段都具有读/写访问权限。用户只应阅读他的登录信息。 即提供对自己的配置文件的读/写访问是合乎逻辑的,但拒绝写入登录,而不是直接定义对其所有字段(登录除外)的读/写访问权。

2 个答案:

答案 0 :(得分:2)

好的,这是。代码根本不完美,但它比没有代码更好。

选民服务。

<?php
namespace Acme\AcmeBundle\Services\Security;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;

class GroupedConcernVoter implements VoterInterface {

    public function __construct(ContainerInterface $container)
    {   
        $this->container = $container;
        $rc = $this->container->getParameter('grouped_concern_voter.config');
        // some config normalization performed
        $this->rightsConfig = $rc;
    }   

    // even though supportsAttribute and supportsClass methods are required by interface,
    // services that I saw, leaves them empty and do not use them

    public function supportsAttribute($attribute)
    {   
        return in_array($attribute, array('OWNER', 'MASTER', 'OPERATOR', 'VIEW', 'EDIT', 'CREATE', 'DELETE', 'UNDELETE', 'DEPLOY'))
            // hacky way to support per-attribute edit and even view rights.
            or preg_match("/^(EDIT|VIEW)(_[A-Z]+)+$/", $attribute);
    }           

    public function supportsClass($object)
    {   
        $object = $object instanceof ObjectIdentity ? $object->getType() : $object;
        // all our business object, which should be manageable by that code have common basic class.
        // Actually it is a decorator over Propel objects with some php magic... nevermind.
        // If one wants similar solution, interface like IOwnableByUserAndGroup with
        // getUserId and getGroupId methods may be defined and used
        return is_subclass_of($object, "Acme\\AcmeBundle\\CommonBusinessObject");
    }       

    function vote(TokenInterface $token, $object, array $attributes)
    {   

        if (!$this->supportsClass($object)) {
            return self::ACCESS_ABSTAIN;
        }
        if ($object instanceof ObjectIdentity) $object = $object->getType();

        if (is_string($object)) {
            $scope = 'own';
            $entity = $object;
        } else {
            if ($object->getUserId() == $this->getUser()->getId()) {
                $scope = 'own';
            } else if ($object->getGroupId() == $this->getUser()->getGroupId()) {
                $scope = 'group';
            } else {
                $scope = 'others';
            }
            $entity = get_class($object);
        }

        $user = $token->getUser();
        $roles = $user->getRoles();
        $role = empty($roles) ? 'ROLE_USER' : $roles[0];

        $rights = $this->getRightsFor($role, $scope, $entity);
        if ($rights === null) return self::ACCESS_ABSTAIN;

        // some complicated logic for checking rights...
        foreach ($attributes as $attr) {
            $a = $attr;
            $field = '';
            if (preg_match("/^(EDIT|VIEW)((?:_[A-Z]+)+)$/", $attr, $m)) list(, $a, $field) = $m;
            if (!array_key_exists($a, $rights)) return self::ACCESS_DENIED;
            if ($rights[$a]) {
                if ($rights[$a] === true
                or  $field === '')
                    return self::ACCESS_GRANTED;
            }
            if (is_array($rights[$a])) {
                if ($field == '') return self::ACCESS_GRANTED;
                $rfield = ltrim(strtolower($field), '_');
                if (in_array($rfield, $rights[$a])) return self::ACCESS_GRANTED;
            }

            return self::ACCESS_DENIED;
        }
    }

    private function getRightsFor($role, $scope, $entity)
    {
        if (array_key_exists($entity, $this->rightsConfig)) {
            $rc = $this->rightsConfig[$entity];
        } else {
            $rc = $this->rightsConfig['global'];
        }
        $rc = $rc[$role][$scope];
        $ret = array();
        foreach($rc as $k => $v) {
            if (is_numeric($k)) $ret[$v] = true;
            else $ret[$k] = $v;
        }
        // hacky way to emulate cumulative rights like in ACL
        if (isset($ret['OWNER'])) $ret['MASTER'] = true;
        if (isset($ret['MASTER'])) $ret['OPERATOR'] = true;
        if (isset($ret['OPERATOR']))
            foreach(array('VIEW', 'EDIT', 'CREATE', 'DELETE', 'UNDELETE') as $r) $ret[$r] = true;
        return $ret;
    }

    private function getUser() {
        if (empty($this->user)) {
            // Not sure, how this shortcut works. This is a service (?) returning current authorized user.
            $this->user = $this->container->get('acme.user.shortcut');
        }
        return $this->user;
    }

}

并且配置......实际上,它是特定于实现的,其结构完全是任意的。

grouped_concern_voter.config:
    global:
        ROLE_SUPERADMIN:
            own: [MASTER]
            group: [MASTER]
            others: [MASTER]
        ROLE_ADMIN:
            own: [MASTER]
            group: [MASTER]
            others: []
        ROLE_USER:
            own: [VIEW, EDIT, CREATE]
            group: [VIEW]
            others: []
    "Acme\\AcmeBundle\\User":
        # rights for ROLE_SUPERADMIN are derived from 'global'
        ROLE_ADMIN:
            own:
                VIEW: [login, email, real_name, properties, group_id]
                EDIT: [login, password, email, real_name, properties]
                CREATE: true
            group:
                VIEW: [login, email, real_name, properties]
                EDIT: [login, password, email, real_name, properties]
            # rights for ROLE_ADMIN/others are derived from 'global'
        ROLE_USER:
            own:
                VIEW: [login, password, email, real_name, properties]
                EDIT: [password, email, real_name, properties]
            group: []
            # rights for ROLE_USER/others are derived from 'global'
    "Acme\\AcmeBundle\\Cake":
        # most rights are derived from global here.
        ROLE_ADMIN:
            others: [VIEW]
        ROLE_USER:
            own: [VIEW]
            others: [VIEW]

最后用法示例。控制器中的某个地方:

$cake = Acme\AcmeBundle\CakeFactory->produce('strawberry', '1.3kg');
$securityContext = $this->get('security.context');
if ($securityContext->isGranted('EAT', $cake)) {
    die ("The cake is a lie");
}

答案 1 :(得分:0)

创建组时,创建角色ROLE_GROUP_(组ID),使用此角色推广组,并使用rolesecurityidentity授予权限