Skip to content

Instantly share code, notes, and snippets.

@CodingNinja
Created December 30, 2011 13:09
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save CodingNinja/1539784 to your computer and use it in GitHub Desktop.
Save CodingNinja/1539784 to your computer and use it in GitHub Desktop.
Grant / Revoke ACL Permissions
<?php
/*
* Copyright (C) 2011 David Mann
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace ApplicationBundle\Security;
use Symfony\Component\Security\Acl\Domain\Entry;
use Symfony\Component\Security\Acl\Domain\Acl;
use Symfony\Component\Security\Acl\Dbal\MutableAclProvider;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use ApplicationBundle\Security\MaskBuilder;
/**
* Easily work with Symfony ACL
*
* This class abstracts some of the ACL layer and
* gives you very easy "Grant" and "Revoke" methods
* which will update existing ACL's and create new ones
* when required
*
* @author CodinNinja
*/
class Manager {
protected $provider;
protected $context;
/**
* Constructor
*
* @param AclProviderInterface $provider
* @param SecurityContextInterface $context
*/
public function __construct(AclProviderInterface $provider, SecurityContextInterface $context) {
$this->provider = $provider;
$this->context = $context;
}
/**
* Grant a permission
*
* @param Object $entity The DomainObject to add the permissions for
* @param integer|string $mask The initial mask
* @return Object The original Entity
*/
public function grant($entity, $mask = MaskBuilder::MASK_OWNER) {
$acl = $this->getAcl($entity);
// retrieving the security identity of the currently logged-in user
$securityContext = $this->context;
$user = $securityContext->getToken()->getUser();
$securityIdentity = UserSecurityIdentity::fromAccount($user);
// grant owner access
$this->addMask($securityIdentity, $mask, $acl);
return $entity;
}
/**
* Get or create an ACL object
*
* @param object $entity The Domain Object to get the ACL for
*
* @return Acl The found / craeted ACL
*/
protected function getAcl($entity) {
// creating the ACL
$aclProvider = $this->provider;
$objectIdentity = ObjectIdentity::fromDomainObject($entity);
try {
$acl = $aclProvider->createAcl($objectIdentity);
}catch(\Exception $e) {
$acl = $aclProvider->findAcl($objectIdentity);
}
return $acl;
}
/**
* Revoke a permission
*
* <pre>
* $manager->revoke($myDomainObject, 'delete'); // Remove "delete" permission for the $myDomainObject
* </pre>
*
* @param Object $entity The DomainObject that we are revoking the permission for
* @param int|string $mask The mask to revoke
*
* @return \ApplicationBundle\Security\Manager Reference to $this for fluent interface
*/
public function revoke($entity, $mask = MaskBuilder::MASK_OWNER) {
$acl = $this->getAcl($entity);
$aces = $acl->getObjectAces();
$user = $this->context->getToken()->getUser();
$securityIdentity = UserSecurityIdentity::fromAccount($user);
foreach($aces as $i => $ace) {
if($securityIdentity->equals($ace->getSecurityIdentity())) {
$this->revokeMask($i, $acl, $ace, $mask);
}
}
$this->provider->updateAcl($acl);
return $this;
}
/**
* Remove a mask
*
* @param Acl $acl The ACL to update
* @param Entry $ace The ACE to remove the mask from
* @param unknown_type $mask The mask to remove
*
* @return \ApplicationBundle\Security\Manager Reference to $this for fluent interface
*/
protected function revokeMask($index, Acl $acl, Entry $ace, $mask) {
$acl->updateObjectAce($index, $ace->getMask() & ~$mask);
return $this;
}
/**
* Add a mask
*
* @param SecurityIdentityInterface $securityIdentity The ACE to add
* @param integer|string $mask The initial mask to set
* @param ACL $acl The ACL to update
*
* @return \ApplicationBundle\Security\Manager Reference to $this for fluent interface
*/
protected function addMask($securityIdentity, $mask, $acl) {
$acl->insertObjectAce($securityIdentity, $mask);
$this->provider->updateAcl($acl);
return $this;
}
}
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="application.acl_manager.class">ApplicationBundle\Security\Manager</parameter>
</parameters>
<services>
<service id="application.acl_manager" class="%application.acl_manager.class%">
<argument type="service" id="security.acl.provider" />
<argument type="service" id="security.context" />
</service>
</services>
</container>
<?php
/*
* Copyright (C) 2011 David Mann
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace ApplicationBundle\Controller;
use ApplicationBundle\Security\MaskBuilder;
use ApplicationBundle\Entity\Entity;
use JMS\SecurityExtraBundle\Annotation as Secure;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class TestController extends Controller
{
/**
* @param Entity $entity
* @Template()
* @Route("/test/{id}/join", name="test_grant")
* @Secure\Secure(roles="ROLE_USER")
*/
public function grantAction(Entity $trip) {
$this->get('application.acl_manager')->grant($trip, MaskBuilder::MASK_VIEW);
}
/**
* @param Entity $entity
* @Template()
* @Route("/test/{id}/leave", name="test_leave")
* @Secure\SecureParam(name="entity", permissions="VIEW")
*/
public function revokeAction(Entity $entity) {
$this->get('application.acl_manager')->revoke($entity, MaskBuilder::MASK_VIEW);
}
}
@PoisonousJohn
Copy link

protected function revokeMask(Acl $acl, Entry $ace, $mask) {
    $acl->updateObjectAce($i, $ace->getMask() & ~$mask);

    return $this;
}

$i is undefined here

@r4cker
Copy link

r4cker commented Jul 9, 2012

You can revoke using deleteObjectAce instead of updating :

$user = $em->getRepository('VendorBundle:User')->find($id); 

$aclProvider = $this->get('security.acl.provider');
    $objectIdentity = ObjectIdentity::fromDomainObject($entity);

$acl = $aclProvider->findAcl($objectIdentity);
$aces = $acl->getObjectAces();

$securityIdentity = UserSecurityIdentity::fromAccount($user);

foreach($aces as $index=>$ace)
{
    if($ace->getSecurityIdentity() == $securityIdentity)
    {
    $acl->deleteObjectAce($index);
    }
}

$aclProvider->updateAcl($acl);

@pakaufmann
Copy link

I had to change the function "addMask" to the following function, or else i would get an error when I tried to change the permissions of an existing ACE.

protected function addMask($securityIdentity, $mask, $acl)
{
    $updated = false;
    foreach($acl->getObjectAces() as $index=>$ace)
    {
        if($ace->getSecurityIdentity() == $securityIdentity)
        {
            $acl->updateObjectAce($index, $mask, null);
            $updated = true;
        }
    }
    if(!$updated)
    {
        $acl->insertObjectAce($securityIdentity, $mask);
    }
    $this->provider->updateAcl($acl);
    return $this;
}

@dKab
Copy link

dKab commented Aug 3, 2014

What if I also want to delete the object identity object when deleting my domain object? How to do that?

@jeetonweb
Copy link

Do you have an example how to add ACE for class scopes as defined here : http://symfony.com/doc/current/cookbook/security/acl_advanced.html

I could able to store the ACE but its not reflecting on permission.

@quberok
Copy link

quberok commented May 7, 2015

Function to delete ACL entity from database

/**
     * Delete ACL entity
     *
     * @param $entity
     * @return $this
     * @throws \Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException
     */
    public function clean($entity)
    {
        $objectIdentity = ObjectIdentity::fromDomainObject($entity);
        $this->provider->deleteAcl($objectIdentity);

        return $this;
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment