From 680da9e977583808f273d953bd5479276112669d Mon Sep 17 00:00:00 2001 From: Toni Uebernickel Date: Mon, 6 Feb 2012 14:58:49 +0100 Subject: [PATCH] add handling of AclCacheInterface --- Security/Acl/AclProvider.php | 11 ++- Security/Acl/MutableAclProvider.php | 28 +++++-- Tests/AclTestCase.php | 3 +- Tests/Fixtures/Acl/ArrayCache.php | 60 +++++++++++++++ Tests/Security/Acl/AclProviderTest.php | 36 ++++++++- Tests/Security/Acl/MutableAclProviderTest.php | 75 +++++++++++++++++++ 6 files changed, 203 insertions(+), 10 deletions(-) create mode 100644 Tests/Fixtures/Acl/ArrayCache.php diff --git a/Security/Acl/AclProvider.php b/Security/Acl/AclProvider.php index 236f23f..b686b5b 100644 --- a/Security/Acl/AclProvider.php +++ b/Security/Acl/AclProvider.php @@ -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 */ 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()) { diff --git a/Security/Acl/MutableAclProvider.php b/Security/Acl/MutableAclProvider.php index 1689ecf..99e0194 100644 --- a/Security/Acl/MutableAclProvider.php +++ b/Security/Acl/MutableAclProvider.php @@ -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 */ 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) { diff --git a/Tests/AclTestCase.php b/Tests/AclTestCase.php index 8691242..4c5aa1a 100644 --- a/Tests/AclTestCase.php +++ b/Tests/AclTestCase.php @@ -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) diff --git a/Tests/Fixtures/Acl/ArrayCache.php b/Tests/Fixtures/Acl/ArrayCache.php new file mode 100644 index 0000000..aae101b --- /dev/null +++ b/Tests/Fixtures/Acl/ArrayCache.php @@ -0,0 +1,60 @@ +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(); + } +} \ No newline at end of file diff --git a/Tests/Security/Acl/AclProviderTest.php b/Tests/Security/Acl/AclProviderTest.php index f31eee2..97b3164 100644 --- a/Tests/Security/Acl/AclProviderTest.php +++ b/Tests/Security/Acl/AclProviderTest.php @@ -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 @@ -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); } } \ No newline at end of file diff --git a/Tests/Security/Acl/MutableAclProviderTest.php b/Tests/Security/Acl/MutableAclProviderTest.php index 727a788..a563cfb 100644 --- a/Tests/Security/Acl/MutableAclProviderTest.php +++ b/Tests/Security/Acl/MutableAclProviderTest.php @@ -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 @@ -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; + } } \ No newline at end of file