How to change role hierarchy storage in Symfony2?

前端 未结 6 1237
醉话见心
醉话见心 2020-12-22 21:32

In my project I need to store role hierarchy in database and create new roles dynamically. In Symfony2 role hierarchy is stored in security.yml by default. What

6条回答
  •  臣服心动
    2020-12-22 21:46

    I had do the same thing like zIs (to store the RoleHierarchy in the database) but i cannot load the complete role hierarchy inside the Constructor like zIs did, because i had to load a custom doctrine filter inside the kernel.request event. The Constructor will be called before the kernel.request so it was no option for me.

    Therefore I checked the security component and found out that Symfony calls a custom Voter to check the roleHierarchy according to the users role:

    namespace Symfony\Component\Security\Core\Authorization\Voter;
    
    use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
    
    /**
     * RoleHierarchyVoter uses a RoleHierarchy to determine the roles granted to
     * the user before voting.
     *
     * @author Fabien Potencier 
     */
    class RoleHierarchyVoter extends RoleVoter
    {
        private $roleHierarchy;
    
        public function __construct(RoleHierarchyInterface $roleHierarchy, $prefix = 'ROLE_')
        {
            $this->roleHierarchy = $roleHierarchy;
    
            parent::__construct($prefix);
        }
    
        /**
         * {@inheritdoc}
         */
        protected function extractRoles(TokenInterface $token)
        {
            return $this->roleHierarchy->getReachableRoles($token->getRoles());
        }
    }
    

    The getReachableRoles Method returns all roles the user can be. For example:

               ROLE_ADMIN
             /             \
         ROLE_SUPERVISIOR  ROLE_BLA
            |               |
         ROLE_BRANCH       ROLE_BLA2
           |
         ROLE_EMP
    
    or in Yaml:
    ROLE_ADMIN:       [ ROLE_SUPERVISIOR, ROLE_BLA ]
    ROLE_SUPERVISIOR: [ ROLE_BRANCH ]
    ROLE_BLA:         [ ROLE_BLA2 ]
    

    If the user has the ROLE_SUPERVISOR role assigned the Method returns the roles ROLE_SUPERVISOR, ROLE_BRANCH and ROLE_EMP (Role-Objects or Classes, which implementing RoleInterface)

    Furthermore this custom voter will be disabled if there is no RoleHierarchy defined in the security.yaml

    private function createRoleHierarchy($config, ContainerBuilder $container)
        {
            if (!isset($config['role_hierarchy'])) {
                $container->removeDefinition('security.access.role_hierarchy_voter');
    
                return;
            }
    
            $container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']);
            $container->removeDefinition('security.access.simple_role_voter');
        }
    

    To solve my issue I created my own custom Voter and extended the RoleVoter-Class, too:

    use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
    use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    use Acme\Foundation\UserBundle\Entity\Group;
    use Doctrine\ORM\EntityManager;
    
    class RoleHierarchyVoter extends RoleVoter {
    
        private $em;
    
        public function __construct(EntityManager $em, $prefix = 'ROLE_') {
    
            $this->em = $em;
    
            parent::__construct($prefix);
        }
    
        /**
         * {@inheritdoc}
         */
        protected function extractRoles(TokenInterface $token) {
    
            $group = $token->getUser()->getGroup();
    
            return $this->getReachableRoles($group);
        }
    
        public function getReachableRoles(Group $group, &$groups = array()) {
    
            $groups[] = $group;
    
            $children = $this->em->getRepository('AcmeFoundationUserBundle:Group')->createQueryBuilder('g')
                            ->where('g.parent = :group')
                            ->setParameter('group', $group->getId())
                            ->getQuery()
                            ->getResult();
    
            foreach($children as $child) {
                $this->getReachableRoles($child, $groups);
            }
    
            return $groups;
        }
    }
    

    One Note: My Setup is similar to zls ones. My Definition for the role (in my case I called it Group):

    Acme\Foundation\UserBundle\Entity\Group:
        type: entity
        table: sec_groups
        id: 
            id:
                type: integer
                generator: { strategy: AUTO }
        fields:
            name:
                type: string
                length: 50
            role:
                type: string
                length: 20
        manyToOne:
            parent:
                targetEntity: Group
    

    And the userdefinition:

    Acme\Foundation\UserBundle\Entity\User:
        type: entity
        table: sec_users
        repositoryClass: Acme\Foundation\UserBundle\Entity\UserRepository
        id:
            id:
                type: integer
                generator: { strategy: AUTO }
        fields:
            username:
                type: string
                length: 30
            salt:
                type: string
                length: 32
            password:
                type: string
                length: 100
            isActive:
                type: boolean
                column: is_active
        manyToOne:
            group:
                targetEntity: Group
                joinColumn:
                    name: group_id
                    referencedColumnName: id
                    nullable: false
    

    Maybe this helps someone.

提交回复
热议问题