add handling of AclCacheInterface

This commit is contained in:
Toni Uebernickel 2012-02-06 14:58:49 +01:00
parent 04e7970312
commit 680da9e977
6 changed files with 203 additions and 10 deletions

View file

@ -33,8 +33,6 @@ use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
/**
* An implementation of the AclProviderInterface using Propel ORM.
*
* @todo Add handling of AclCacheInterface.
*
* @author Toni Uebernickel <tuebernickel@gmail.com>
*/
class AclProvider implements AclProviderInterface
@ -98,6 +96,14 @@ class AclProvider implements AclProviderInterface
*/
public function findAcl(ObjectIdentityInterface $objectIdentity, array $securityIdentities = array())
{
$modelObj = ObjectIdentityQuery::create()->findOneByAclObjectIdentity($objectIdentity, $this->connection);
if (null !== $this->cache and null !== $modelObj) {
$cachedAcl = $this->cache->getFromCacheById($modelObj->getId());
if ($cachedAcl instanceof AclInterface) {
return $cachedAcl;
}
}
$collection = EntryQuery::create()->findByAclIdentity($objectIdentity, $securityIdentities);
if (0 === count($collection)) {
@ -120,7 +126,6 @@ class AclProvider implements AclProviderInterface
$parentAcl = null;
$entriesInherited = true;
$modelObj = ObjectIdentityQuery::create()->findOneByAclObjectIdentity($objectIdentity, $this->connection);
if (null !== $modelObj) {
$entriesInherited = $modelObj->getEntriesInheriting();
if (null !== $modelObj->getParentObjectIdentityId()) {

View file

@ -39,8 +39,6 @@ use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
/**
* An implementation of the MutableAclProviderInterface using Propel ORM.
*
* @todo Add handling of AclCacheInterface.
*
* @author Toni Uebernickel <tuebernickel@gmail.com>
*/
class MutableAclProvider extends AclProvider implements MutableAclProviderInterface
@ -125,11 +123,26 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
}
}
/*
* If caching is enabled, retrieve the (grand-)children of this ACL.
* Those will be removed from the cache as well, as their parents do not exist anymore.
*/
if (null !== $this->cache) {
$children = ObjectIdentityQuery::create()->findGrandChildren($objIdentity, $this->connection);
}
// This deletes all object and object-field ACEs, too.
$objIdentity->delete($this->connection);
$this->connection->commit();
if (null !== $this->cache) {
$this->cache->evictFromCacheById($objIdentity->getId());
foreach ($children as $eachChild) {
$this->cache->evictFromCacheById($eachChild->getId());
}
}
return true;
// @codeCoverageIgnoreStart
} catch (Exception $e) {
@ -151,13 +164,12 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
*/
public function updateAcl(MutableAclInterface $acl)
{
if (!$acl instanceof Acl) {
throw new \InvalidArgumentException('The given ACL is not tracked by this provider. Please provide \Propel\PropelBundle\Security\Acl\Domain\Acl only.');
if (!$acl instanceof MutableAcl) {
throw new \InvalidArgumentException('The given ACL is not tracked by this provider. Please provide \Propel\PropelBundle\Security\Acl\Domain\MutableAcl only.');
}
try {
$modelEntries = EntryQuery::create()->findByAclIdentity($acl->getObjectIdentity(), array(), $this->connection);
$objectIdentity = ObjectIdentityQuery::create()->findOneByAclObjectIdentity($acl->getObjectIdentity(), $this->connection);
$this->connection->beginTransaction();
@ -194,6 +206,12 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
$this->connection->commit();
// After successfully committing the transaction, we are good to update the cache.
if (null !== $this->cache) {
$this->cache->evictFromCacheById($objectIdentity->getId());
$this->cache->putInCache($acl);
}
return true;
// @codeCoverageIgnoreStart
} catch (Exception $e) {

View file

@ -28,6 +28,7 @@ use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
class AclTestCase extends TestCase
{
protected $con = null;
protected $cache = null;
public function setUp()
{
@ -87,7 +88,7 @@ class AclTestCase extends TestCase
protected function getAclProvider()
{
return new MutableAclProvider(new PermissionGrantingStrategy(), $this->con);
return new MutableAclProvider(new PermissionGrantingStrategy(), $this->con, $this->cache);
}
protected function getAclObjectIdentity($identifier = 1)

View file

@ -0,0 +1,60 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\Tests\Fixtures\Acl;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\AclCacheInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
class ArrayCache implements AclCacheInterface
{
public $content = array();
public function evictFromCacheById($primaryKey)
{
if (isset($this->content[$primaryKey])) {
unset($this->content[$primaryKey]);
}
}
public function evictFromCacheByIdentity(ObjectIdentityInterface $oid)
{
// Propel ACL does not make use of those.
}
public function getFromCacheById($primaryKey)
{
if (isset($this->content[$primaryKey])) {
return $this->content[$primaryKey];
}
return null;
}
public function getFromCacheByIdentity(ObjectIdentityInterface $oid)
{
// Propel ACL does not make use of those.
}
public function putInCache(AclInterface $acl)
{
if (null === $acl->getId()) {
throw new \InvalidArgumentException('The given ACL does not have an ID.');
}
$this->content[$acl->getId()] = $acl;
}
public function clearCache()
{
$this->content = array();
}
}

View file

@ -11,6 +11,8 @@
namespace Propel\PropelBundle\Tests\Security\Acl;
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\PropelBundle\Model\Acl\EntryQuery;
use Propel\PropelBundle\Model\Acl\EntryPeer;
use Propel\PropelBundle\Security\Acl\AclProvider;
@ -18,6 +20,7 @@ use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
use Propel\PropelBundle\Tests\AclTestCase;
use Propel\PropelBundle\Tests\Fixtures\Acl\ArrayCache as AclCache;
/**
* @author Toni Uebernickel <tuebernickel@gmail.com>
@ -218,8 +221,39 @@ class AclProviderTest extends AclTestCase
return array($parentObj, $obj, $childObj);
}
/**
* @depends testFindAclWithEntries
*/
public function testFindAclReadsFromCache()
{
$this->cache = new AclCache();
$obj = $this->createModelObjectIdentity(1);
$entry = $this->createEntry();
$entry
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity('ROLE_USER')))
->setAclClass($obj->getAclClass())
->setMask(64)
;
$obj->addEntry($entry)->save($this->con);
// Read and put into cache
$acl = $this->getAclProvider()->findAcl($this->getAclObjectIdentity(1), array($this->getRoleSecurityIdentity('ROLE_USER')));
$this->cache->content[1] = $acl;
// Change database
EntryQuery::create()->update(array(EntryPeer::translateFieldName(EntryPeer::MASK, \BasePeer::TYPE_COLNAME, \BasePeer::TYPE_PHPNAME) => 128), $this->con);
$this->assertEquals(0, EntryQuery::create()->filterByMask(64)->count($this->con));
// Verify cache has been read
$cachedAcl = $this->getAclProvider()->findAcl($this->getAclObjectIdentity(1), array($this->getRoleSecurityIdentity('ROLE_USER')));
$cachedObjectAces = $cachedAcl->getObjectAces();
$this->assertSame($acl, $cachedAcl);
$this->assertEquals(64, $cachedObjectAces[0]->getMask());
}
protected function getAclProvider()
{
return new AclProvider(new PermissionGrantingStrategy(), $this->con);
return new AclProvider(new PermissionGrantingStrategy(), $this->con, $this->cache);
}
}

View file

@ -14,6 +14,7 @@ use Propel\PropelBundle\Model\Acl\EntryQuery;
use Propel\PropelBundle\Model\Acl\ObjectIdentityQuery;
use Propel\PropelBundle\Tests\AclTestCase;
use Propel\PropelBundle\Tests\Fixtures\Acl\ArrayCache as AclCache;
/**
* @author Toni Uebernickel <tuebernickel@gmail.com>
@ -241,4 +242,78 @@ class MutableAclProviderTest extends AclTestCase
$this->getAclProvider()->deleteAcl($this->getAclObjectIdentity(1));
$this->assertEquals(0, EntryQuery::create()->count($this->con));
}
/**
* @depends testUpdateAclCreatesInsertedAces
*/
public function testUpdateAclWritesCacheOfNewAcl()
{
$this->cache = new AclCache();
$this->assertEmpty($this->cache->content);
$acl = $this->getAcl();
$this->assertNotEmpty($this->cache->content);
$this->assertSame($acl, $this->cache->content[$acl->getId()]);
}
/**
* @depends testUpdateAclWritesCacheOfNewAcl
*/
public function testUpdateAclUpdatesCacheOfAcl()
{
$this->cache = new AclCache();
$acl = $this->getAcl(1);
$acl->updateObjectAce(0, 128);
$this->getAclProvider()->updateAcl($acl);
$objectAces = $this->cache->content[$acl->getId()]->getObjectAces();
$this->assertEquals(128, $objectAces[0]->getMask());
}
/**
* @depends testUpdateAclWritesCacheOfNewAcl
*/
public function testDeleteAclEvictsFromCache()
{
$this->cache = new AclCache();
$this->getAcl();
$this->getAclProvider()->deleteAcl($this->getAclObjectIdentity(1));
$this->assertEmpty($this->cache->content);
}
/**
* @depends testCreateAclWithParent
* @depends testDeleteAclEvictsFromCache
*/
public function testDeleteAclEvictsChildrenFromCache()
{
$this->cache = new AclCache();
$parentAcl = $this->getAcl(1);
$childAcl = $this->getAcl(2);
$grandChildAcl = $this->getAcl(3);
$grandChildAcl->setParentAcl($childAcl);
$childAcl->setParentAcl($parentAcl);
$this->getAclProvider()->updateAcl($grandChildAcl);
$this->getAclProvider()->updateAcl($childAcl);
$this->assertCount(3, $this->cache->content);
$this->getAclProvider()->deleteAcl($this->getAclObjectIdentity(1));
$this->assertEmpty($this->cache->content);
}
protected function getAcl($identifier = 1)
{
$acl = $this->getAclProvider()->createAcl($this->getAclObjectIdentity($identifier));
$acl->insertObjectAce($this->getRoleSecurityIdentity(), 64);
$this->getAclProvider()->updateAcl($acl);
return $acl;
}
}