Symfony 2 ACL and Role Hierarchy

2019-05-06 16:30发布

问题:

I'm a little stuck and unable to find the answer to this.

In my app test I've created two Entities User and Comment both are mapped correctly.

I have created a small controller which depending on the user will add the comment and the data to the ACL tables, if I create my comment as a standard user with the associated for of 'ROLE_USER', and Try to access it as user with the role 'ROLE_ADMIN' I get access denied, it seems to completely ignore the security.yml hierarchy.

I know this works by adding instead of the userid the ROLE_USER etc but I don't want to do it this way.

Examples of my code are below.

CommentController

    <?php

    namespace ACL\TestBundle\Controller;

    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    use Symfony\Component\HttpFoundation\Request;
    use ACL\TestBundle\Forms\Type\commentType;
    use ACL\TestBundle\Entity\Comment;
    use Symfony\Component\Security\Core\Exception\AccessDeniedException;
    use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
    use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
    use Symfony\Component\Security\Acl\Permission\MaskBuilder;

    class DefaultController extends Controller
    {
        /**
         * @Route("/", name="_default")
         * @Template()
         */
        public function indexAction()
        {
            die('success');
        }

        /**
         * @Route("/comment/new/")
         * @Template()
         */
        public function newAction(Request $request)
        {
            $comment = new Comment();

            $form = $this->createForm(new commentType(), $comment);

            $form->handleRequest($request);

            if ($form->isValid()) {
                $comment->setUsers($this->getUser());
                $em = $this->getDoctrine()->getManager();
                $em->persist($comment);
                $em->flush();

                // creating the ACL
                $aclProvider = $this->get('security.acl.provider');
                $objectIdentity = ObjectIdentity::fromDomainObject($comment);
                $acl = $aclProvider->createAcl($objectIdentity);

                // retrieving the security identity of the currently logged-in user
                $securityIdentity = UserSecurityIdentity::fromAccount($this->getUser());

                // grant owner access
                $acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER);
                $aclProvider->updateAcl($acl);
            }

            return array(
                'form' => $form->createView(),
            );
        }

        /**
         * @Route("/comment/{id}/", requirements={"id":"\d+"})
         * @Template()
         */
        public function editAction(Request $request,$id)
        {
            $em = $this->getDoctrine()->getManager();
            $comment = $em->find('ACLTestBundle:Comment', $id);

            $securityContext = $this->get('security.context');

            // check for edit access
            if (false === $securityContext->isGranted('EDIT',$comment)) {
                throw new AccessDeniedException();
            }

            $form = $this->createForm(new commentType(), $comment);

            $form->handleRequest($request);

            if($form->isValid()){
                $em->persist($comment);
                $em->flush();
            }

            return array('form' => $form->createView());
        }
    }

security.yml

 security:
        encoders:
            ACL\TestBundle\Entity\User: plaintext
        acl:
            connection: default

        providers:
            database:
                entity: { class: ACLTestBundle:User }

        role_hierarchy:
            ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]

        firewalls:
            dev:
                pattern:  ^/(_(profiler|wdt)|css|images|js)/
                security: false
            main:
                pattern:     ^/
                provider:    database
                anonymous:   true
                logout:      true
                switch_user: true
                form_login:
                    login_path: _security_login

        access_control:
            - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

I appreciate any advice!

回答1:

The problem is that you are adding adding ACL base on UserIdentity and want to check the gran base on RoleIdentity. If you want to do it Role base change the creating ACL as below

// creating the ACL
$aclProvider = $this->get('security.acl.provider');
$objectIdentity = ObjectIdentity::fromDomainObject($comment);
$acl = $aclProvider->createAcl($objectIdentity);

// retrieving the security identity of the currently logged-in user
$securityIdentity = UserSecurityIdentity::fromAccount($this->getUser());

// grant owner access
$acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER);

// grant EDIT access to ROLE_ADMIN
$securityIdentity = new RoleSecurityIdentity('ROLE_ADMIN');
$acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_EDIT);
$aclProvider->updateAcl($acl);

As you see I kept the owner access for the specific user then I added Edit access for ROLE_ADMIN. You can keep the controller as is.

If you don't want to make it Role base but just want to give an exception for admin users you can change your controller as

// check for edit access
if (false === $securityContext->isGranted('EDIT',$comment) && false === $securityContext->isGranted('ROLE_ADMIN') ) {
   throw new AccessDeniedException();
}