Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Symfony-针对(动态)分层角色的高效访问控制_Symfony_Acl_Roles - Fatal编程技术网

Symfony-针对(动态)分层角色的高效访问控制

Symfony-针对(动态)分层角色的高效访问控制,symfony,acl,roles,Symfony,Acl,Roles,我需要一些关于如何处理以下场景的访问控制的建议: 公司 有一家或多家公司 具有一个或多个角色\u公司\u管理员 公司 有一个或多个区域 有一个或多个角色\公司\管理员 地区: 有零个或多个商店 具有一个或多个角色\区域\管理员 商店: 拥有零或多个资产 具有一个或多个角色\门店\管理员 拥有零个或多个角色\门店\员工 拥有零个或多个角色\门店\客户(多个更好) 该应用程序应该支持许多公司 我的直觉是为每个实体的管理员创建多对多关系(例如region\u id,user\u i

我需要一些关于如何处理以下场景的访问控制的建议:

  • 公司
    • 有一家或多家公司
    • 具有一个或多个角色\u公司\u管理员
  • 公司
    • 有一个或多个区域
    • 有一个或多个角色\公司\管理员
  • 地区:
    • 有零个或多个商店
    • 具有一个或多个角色\区域\管理员
  • 商店:
    • 拥有零或多个资产
    • 具有一个或多个角色\门店\管理员
    • 拥有零个或多个角色\门店\员工
    • 拥有零个或多个角色\门店\客户(多个更好)
该应用程序应该支持许多公司

我的直觉是为每个实体的管理员创建多对多关系(例如
region\u id
user\u id
)。根据性能的不同,我可以使用一个更非规范化的表,其中包含
用户id
公司id
公司id
地区id
,以及
商店id
。然后我将创建一个选民类(一致策略):

由于权限是分层的,因此
getAdmins()
函数也将检查所有所有者的管理员。例如:
$region->getAdmins()
还将返回所属公司和公司的管理员

我觉得我错过了一些明显的东西。根据我如何实现
getAdmins()
函数,这种方法要求每次投票至少对db进行一次点击。有没有“更好”的办法


提前感谢您的帮助。

我做了上面摆的姿势,效果很好。投票人很容易按计划实施。多对多
\u所有者
表工作正常

为了处理分层权限,我在实体中使用了级联调用。不优雅,效率不高,但速度也不错。我确信重构后很快就会使用一个DQL查询,但是级联调用现在可以工作了:

class Store implements OwnableInterface
{
    ....

    /**
     * @ORM\ManyToMany(targetEntity="Person")
     * @ORM\JoinTable(name="stores_owners",
     *      joinColumns={@ORM\JoinColumn(name="store_id", referencedColumnName="id", nullable=true)},
     *      inverseJoinColumns={@ORM\JoinColumn(name="person_id", referencedColumnName="id")}
     *      )
     *
     * @var ArrayCollection|Person[]
     */
    protected $owners;

    ...

    public function __construct()
    {
        $this->owners = new ArrayCollection();
    }

    ...

    /**
     * Returns all people who are owners of the object
     * @return ArrayCollection|Person[]
     */
    function getOwners()
    {
        $effectiveOwners = new ArrayCollection();

        foreach($this->owners as $owner){
            $effectiveOwners->add($owner);
        }

        foreach($this->getRegion()->getOwners() as $owner){
            $effectiveOwners->add($owner);
        }

        return $effectiveOwners;
    }

    /**
     * Returns true if the person is an owner.
     * @param Person $person
     * @return boolean
     */
    function isOwner(Person $person)
    {
        return ($this->getOwners()->contains($person));
    }

    ...

}
Region
实体还将实现
OwnableInterface
,其
getOwners()
随后将调用
getCompany()->getOwners()
,等等

如果没有所有者(null),
array\u merge
会出现问题,因此新的
$effectiveOwners ArrayCollection
似乎工作正常

这是投票人。我从以下地方偷走了大部分投票人代码和
OwnableInterface
OwnerInterface


谢谢。一个问题:您使用
ArrayCollection::contains
作为
isOwner
方法。为了让
ArrayCollection::contains
返回true,它必须包含它正在查找的对象的相同实例。因此,如果您从
用户
对象中获取
个人
对象,并将其与另一个对象中的
个人
进行比较(例如:
存储
,其各代未连接),这怎么可能呢。
class Store implements OwnableInterface
{
    ....

    /**
     * @ORM\ManyToMany(targetEntity="Person")
     * @ORM\JoinTable(name="stores_owners",
     *      joinColumns={@ORM\JoinColumn(name="store_id", referencedColumnName="id", nullable=true)},
     *      inverseJoinColumns={@ORM\JoinColumn(name="person_id", referencedColumnName="id")}
     *      )
     *
     * @var ArrayCollection|Person[]
     */
    protected $owners;

    ...

    public function __construct()
    {
        $this->owners = new ArrayCollection();
    }

    ...

    /**
     * Returns all people who are owners of the object
     * @return ArrayCollection|Person[]
     */
    function getOwners()
    {
        $effectiveOwners = new ArrayCollection();

        foreach($this->owners as $owner){
            $effectiveOwners->add($owner);
        }

        foreach($this->getRegion()->getOwners() as $owner){
            $effectiveOwners->add($owner);
        }

        return $effectiveOwners;
    }

    /**
     * Returns true if the person is an owner.
     * @param Person $person
     * @return boolean
     */
    function isOwner(Person $person)
    {
        return ($this->getOwners()->contains($person));
    }

    ...

}
use Acme\AcmeBundle\Security\OwnableInterface;
use Acme\AcmeBundle\Security\OwnerInterface;
use Acme\AcmeUserBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;

class IsOwnerVoter implements VoterInterface
{

    const IS_OWNER = 'IS_OWNER';

    private $container;

    public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container) {
        $this->container = $container;
    }

    public function supportsAttribute($attribute)
    {
        return self::IS_OWNER === $attribute;
    }

    public function supportsClass($class)
    {
        if (is_object($class)) {
            $ref = new \ReflectionObject($class);

            return $ref->implementsInterface('Acme\AcmeBundle\Security\OwnableInterface');
        }

        return false;
    }

    public function vote(TokenInterface $token, $object, array $attributes)
    {
        foreach ($attributes as $attribute) {

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

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

            // Is the token a super user? This will check roles, not user.
            if ( $this->container->get('security.context')->isGranted('ROLE_SUPER_ADMIN') ) {
                return VoterInterface::ACCESS_GRANTED;
            }

            if (!$token->getUser() instanceof User) {
                return self::ACCESS_ABSTAIN;
            }

            // check to see if this token is a user.
            if (!$token->getUser()->getPerson() instanceof OwnerInterface) {
                return self::ACCESS_ABSTAIN;
            }

            // Is this person an owner?
            if ($this->isOwner($token->getUser()->getPerson(), $object)) {
                return self::ACCESS_GRANTED;
            }

            return self::ACCESS_DENIED;
        }

        return self::ACCESS_ABSTAIN;
    }

    private function isOwner(OwnerInterface $owner, OwnableInterface $ownable)
    {
        return $ownable->isOwner($owner);
    }
}