Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use class-scope aces in Symfony2?

Tags:

symfony

acl

I've got a problem with class-scope aces. I've created an ace for a class like this :

$userIdentity = UserSecurityIdentity::fromAccount($user);
$classIdentity = new ObjectIdentity('some_identifier', 'Class\FQCN');
$acl = $aclProvider->createAcl($classIdentity);
$acl->insertClassAce($userIdentity, MaskBuilder::MASK_CREATE);
$aclProvider->updateAcl($acl);

Now, I'm trying to check the user's permissions. I've found this way of doing things, which is not documented, but gives the expected results on a class basis :

$securityContext->isGranted('CREATE', $classIdentity);  // returns true
$securityContext->isGranted('VIEW', $classIdentity);    // returns true
$securityContext->isGranted('DELETE', $classIdentity);  // returns false

This method is well adapated to the "CREATE" permission check, where there's no available object instance to pass to the method. However, it should be possible to check if another permission is granted on a particular instance basis :

$entity = new Class\FQCN();
$em->persist($entity);
$em->flush();
$securityContext->isGranted('VIEW', $entity);  // returns false

This is where the test fails. I expected that an user who has a given permission mask on a class would have the same permissions on every instance of that class, as stated in the documentation ("The PermissionGrantingStrategy first checks all your object-scope ACEs if none is applicable, the class-scope ACEs will be checked"), but it seems not to be the case here.

like image 549
Stefk Avatar asked Nov 07 '11 14:11

Stefk


3 Answers

you are doing it right. and according to the bottom of this page, it should work, but it does not.

the easiest way to make it work is creating an AclVoter class:

namespace Core\Security\Acl\Voter;

use JMS\SecurityExtraBundle\Security\Acl\Voter\AclVoter as BaseAclVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Doctrine\Common\Util\ClassUtils;

class AclVoter extends BaseAclVoter
{
    public function vote( TokenInterface $token , $object , array $attributes )
    {   
    //vote for object first
    $objectVote =   parent::vote( $token , $object , $attributes ); 

    if( self::ACCESS_GRANTED === $objectVote )
    {
        return self::ACCESS_GRANTED;
    }
    else
    {
        //then for object's class
        $oid    =   new ObjectIdentity( 'class' , ClassUtils::getRealClass( get_class( $object ) ) );
        $classVote  =   parent::vote( $token , $oid , $attributes );                

        if( self::ACCESS_ABSTAIN === $objectVote )
        {       
        if( self::ACCESS_ABSTAIN === $classVote )
        {          
            return self::ACCESS_ABSTAIN;
        }
        else
        {
            return $classVote;
        }
        }
        else if( self::ACCESS_DENIED === $objectVote )
        {
        if( self::ACCESS_ABSTAIN === $classVote )
        {
            return self::ACCESS_DENIED;
        }
        else
        {
            return $classVote;
        }
        }       
    }

    return self::ACCESS_ABSTAIN;
    }
}

then in security.yml set this:

jms_security_extra:
    voters:
        disable_acl: true  

and finally set up the voter as a service:

core.security.acl.voter.basic_permissions:
    class: Core\Security\Acl\Voter\AclVoter
    public: false
    arguments: 
      - '@security.acl.provider'
      - '@security.acl.object_identity_retrieval_strategy'
      - '@security.acl.security_identity_retrieval_strategy'
      - '@security.acl.permission.map' 
      - '@?logger'   
    tags:
        - { name: security.voter , priority: 255 }           
        - { name: monolog.logger , channel: security }    
like image 63
kor3k Avatar answered Oct 18 '22 20:10

kor3k


You need to ensure each object has its own ACL (use $aclProvider->createAcl($entity)) for class-scope permissions to work correctly.

See this discussion: https://groups.google.com/forum/?fromgroups=#!topic/symfony2/pGIs0UuYKX4

like image 21
Salman von Abbas Avatar answered Oct 18 '22 21:10

Salman von Abbas


If you don't have an existing entity, you can check against the objectIdentity you created. Be careful to use "double-backslashes", because of the escaping of the backslash.

$post = $postRepository->findOneBy(array('id' => 1));

$securityContext = $this->get('security.context');
$objectIdentity = new ObjectIdentity('class', 'Liip\\TestBundle\\Entity\\Post');

// check for edit access
if (true === $securityContext->isGranted('EDIT', $objectIdentity)) {
    echo "Edit Access granted to: <br/><br/> ";
    print_r("<pre>");
    print_r($post);
    print_r("</pre>");
} else {
    throw new AccessDeniedException();
}

That should work!

If you would check for "object-scope" you could just use $post instead of $objectIdentity in the isGranted function call.

like image 2
rryter Avatar answered Oct 18 '22 20:10

rryter