summaryrefslogtreecommitdiffstats
path: root/Acl/Tests/Dbal
diff options
context:
space:
mode:
Diffstat (limited to 'Acl/Tests/Dbal')
-rw-r--r--Acl/Tests/Dbal/AclProviderBenchmarkTest.php267
-rw-r--r--Acl/Tests/Dbal/AclProviderTest.php280
-rw-r--r--Acl/Tests/Dbal/MutableAclProviderTest.php572
3 files changed, 1119 insertions, 0 deletions
diff --git a/Acl/Tests/Dbal/AclProviderBenchmarkTest.php b/Acl/Tests/Dbal/AclProviderBenchmarkTest.php
new file mode 100644
index 0000000..c95b474
--- /dev/null
+++ b/Acl/Tests/Dbal/AclProviderBenchmarkTest.php
@@ -0,0 +1,267 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Acl\Tests\Dbal;
+
+use Symfony\Component\Security\Acl\Dbal\AclProvider;
+use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
+use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+use Symfony\Component\Security\Acl\Dbal\Schema;
+use Doctrine\DBAL\DriverManager;
+
+/**
+ * @group benchmark
+ */
+class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
+{
+ /** @var \Doctrine\DBAL\Connection */
+ protected $con;
+ protected $insertClassStmt;
+ protected $insertSidStmt;
+ protected $insertOidAncestorStmt;
+ protected $insertOidStmt;
+ protected $insertEntryStmt;
+
+ protected function setUp()
+ {
+ try {
+ $this->con = DriverManager::getConnection(array(
+ 'driver' => 'pdo_mysql',
+ 'host' => 'localhost',
+ 'user' => 'root',
+ 'dbname' => 'testdb',
+ ));
+ $this->con->connect();
+ } catch (\Exception $e) {
+ $this->markTestSkipped('Unable to connect to the database: '.$e->getMessage());
+ }
+ }
+
+ protected function tearDown()
+ {
+ $this->con = null;
+ }
+
+ public function testFindAcls()
+ {
+ // $this->generateTestData();
+
+ // get some random test object identities from the database
+ $oids = array();
+ $stmt = $this->con->executeQuery('SELECT object_identifier, class_type FROM acl_object_identities o INNER JOIN acl_classes c ON c.id = o.class_id ORDER BY RAND() LIMIT 25');
+ foreach ($stmt->fetchAll() as $oid) {
+ $oids[] = new ObjectIdentity($oid['object_identifier'], $oid['class_type']);
+ }
+
+ $provider = $this->getProvider();
+
+ $start = microtime(true);
+ $provider->findAcls($oids);
+ $time = microtime(true) - $start;
+ echo 'Total Time: '.$time."s\n";
+ }
+
+ /**
+ * This generates a huge amount of test data to be used mainly for benchmarking
+ * purposes, not so much for testing. That's why it's not called by default.
+ */
+ protected function generateTestData()
+ {
+ $sm = $this->con->getSchemaManager();
+ $sm->dropAndCreateDatabase('testdb');
+ $this->con->exec('USE testdb');
+
+ // import the schema
+ $schema = new Schema($options = $this->getOptions());
+ foreach ($schema->toSql($this->con->getDatabasePlatform()) as $sql) {
+ $this->con->exec($sql);
+ }
+
+ // setup prepared statements
+ $this->insertClassStmt = $this->con->prepare('INSERT INTO acl_classes (id, class_type) VALUES (?, ?)');
+ $this->insertSidStmt = $this->con->prepare('INSERT INTO acl_security_identities (id, identifier, username) VALUES (?, ?, ?)');
+ $this->insertOidStmt = $this->con->prepare('INSERT INTO acl_object_identities (id, class_id, object_identifier, parent_object_identity_id, entries_inheriting) VALUES (?, ?, ?, ?, ?)');
+ $this->insertEntryStmt = $this->con->prepare('INSERT INTO acl_entries (id, class_id, object_identity_id, field_name, ace_order, security_identity_id, mask, granting, granting_strategy, audit_success, audit_failure) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
+ $this->insertOidAncestorStmt = $this->con->prepare('INSERT INTO acl_object_identity_ancestors (object_identity_id, ancestor_id) VALUES (?, ?)');
+
+ for ($i = 0; $i < 40000; ++$i) {
+ $this->generateAclHierarchy();
+ }
+ }
+
+ protected function generateAclHierarchy()
+ {
+ $rootId = $this->generateAcl($this->chooseClassId(), null, array());
+
+ $this->generateAclLevel(rand(1, 15), $rootId, array($rootId));
+ }
+
+ protected function generateAclLevel($depth, $parentId, $ancestors)
+ {
+ $level = count($ancestors);
+ for ($i = 0, $t = rand(1, 10); $i < $t; ++$i) {
+ $id = $this->generateAcl($this->chooseClassId(), $parentId, $ancestors);
+
+ if ($level < $depth) {
+ $this->generateAclLevel($depth, $id, array_merge($ancestors, array($id)));
+ }
+ }
+ }
+
+ protected function chooseClassId()
+ {
+ static $id = 1000;
+
+ if ($id === 1000 || ($id < 1500 && rand(0, 1))) {
+ $this->insertClassStmt->execute(array($id, $this->getRandomString(rand(20, 100), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\_')));
+ ++$id;
+
+ return $id - 1;
+ } else {
+ return rand(1000, $id - 1);
+ }
+ }
+
+ protected function generateAcl($classId, $parentId, $ancestors)
+ {
+ static $id = 1000;
+
+ $this->insertOidStmt->execute(array(
+ $id,
+ $classId,
+ $this->getRandomString(rand(20, 50)),
+ $parentId,
+ rand(0, 1),
+ ));
+
+ $this->insertOidAncestorStmt->execute(array($id, $id));
+ foreach ($ancestors as $ancestor) {
+ $this->insertOidAncestorStmt->execute(array($id, $ancestor));
+ }
+
+ $this->generateAces($classId, $id);
+ ++$id;
+
+ return $id - 1;
+ }
+
+ protected function chooseSid()
+ {
+ static $id = 1000;
+
+ if ($id === 1000 || ($id < 11000 && rand(0, 1))) {
+ $this->insertSidStmt->execute(array(
+ $id,
+ $this->getRandomString(rand(5, 30)),
+ rand(0, 1),
+ ));
+ ++$id;
+
+ return $id - 1;
+ } else {
+ return rand(1000, $id - 1);
+ }
+ }
+
+ protected function generateAces($classId, $objectId)
+ {
+ static $id = 1000;
+
+ $sids = array();
+ $fieldOrder = array();
+
+ for ($i = 0; $i <= 30; ++$i) {
+ $fieldName = rand(0, 1) ? null : $this->getRandomString(rand(10, 20));
+
+ do {
+ $sid = $this->chooseSid();
+ } while (array_key_exists($sid, $sids) && in_array($fieldName, $sids[$sid], true));
+
+ $fieldOrder[$fieldName] = array_key_exists($fieldName, $fieldOrder) ? $fieldOrder[$fieldName] + 1 : 0;
+ if (!isset($sids[$sid])) {
+ $sids[$sid] = array();
+ }
+ $sids[$sid][] = $fieldName;
+
+ $strategy = rand(0, 2);
+ if ($strategy === 0) {
+ $strategy = PermissionGrantingStrategy::ALL;
+ } elseif ($strategy === 1) {
+ $strategy = PermissionGrantingStrategy::ANY;
+ } else {
+ $strategy = PermissionGrantingStrategy::EQUAL;
+ }
+
+ // id, cid, oid, field, order, sid, mask, granting, strategy, a success, a failure
+ $this->insertEntryStmt->execute(array(
+ $id,
+ $classId,
+ rand(0, 5) ? $objectId : null,
+ $fieldName,
+ $fieldOrder[$fieldName],
+ $sid,
+ $this->generateMask(),
+ rand(0, 1),
+ $strategy,
+ rand(0, 1),
+ rand(0, 1),
+ ));
+
+ ++$id;
+ }
+ }
+
+ protected function generateMask()
+ {
+ $i = rand(1, 30);
+ $mask = 0;
+
+ while ($i <= 30) {
+ $mask |= 1 << rand(0, 30);
+ ++$i;
+ }
+
+ return $mask;
+ }
+
+ protected function getRandomString($length, $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
+ {
+ $s = '';
+ $cLength = strlen($chars);
+
+ while (strlen($s) < $length) {
+ $s .= $chars[mt_rand(0, $cLength - 1)];
+ }
+
+ return $s;
+ }
+
+ protected function getOptions()
+ {
+ return array(
+ 'oid_table_name' => 'acl_object_identities',
+ 'oid_ancestors_table_name' => 'acl_object_identity_ancestors',
+ 'class_table_name' => 'acl_classes',
+ 'sid_table_name' => 'acl_security_identities',
+ 'entry_table_name' => 'acl_entries',
+ );
+ }
+
+ protected function getStrategy()
+ {
+ return new PermissionGrantingStrategy();
+ }
+
+ protected function getProvider()
+ {
+ return new AclProvider($this->con, $this->getStrategy(), $this->getOptions());
+ }
+}
diff --git a/Acl/Tests/Dbal/AclProviderTest.php b/Acl/Tests/Dbal/AclProviderTest.php
new file mode 100644
index 0000000..680c6c3
--- /dev/null
+++ b/Acl/Tests/Dbal/AclProviderTest.php
@@ -0,0 +1,280 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Acl\Tests\Dbal;
+
+use Symfony\Component\Security\Acl\Dbal\AclProvider;
+use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
+use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+use Symfony\Component\Security\Acl\Dbal\Schema;
+use Doctrine\DBAL\DriverManager;
+
+/**
+ * @requires extension pdo_sqlite
+ */
+class AclProviderTest extends \PHPUnit_Framework_TestCase
+{
+ protected $con;
+ protected $insertClassStmt;
+ protected $insertEntryStmt;
+ protected $insertOidStmt;
+ protected $insertOidAncestorStmt;
+ protected $insertSidStmt;
+
+ /**
+ * @expectedException \Symfony\Component\Security\Acl\Exception\AclNotFoundException
+ * @expectedMessage There is no ACL for the given object identity.
+ */
+ public function testFindAclThrowsExceptionWhenNoAclExists()
+ {
+ $this->getProvider()->findAcl(new ObjectIdentity('foo', 'foo'));
+ }
+
+ public function testFindAclsThrowsExceptionUnlessAnACLIsFoundForEveryOID()
+ {
+ $oids = array();
+ $oids[] = new ObjectIdentity('1', 'foo');
+ $oids[] = new ObjectIdentity('foo', 'foo');
+
+ try {
+ $this->getProvider()->findAcls($oids);
+
+ $this->fail('Provider did not throw an expected exception.');
+ } catch (\Exception $e) {
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Exception\AclNotFoundException', $e);
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Exception\NotAllAclsFoundException', $e);
+
+ $partialResult = $e->getPartialResult();
+ $this->assertTrue($partialResult->contains($oids[0]));
+ $this->assertFalse($partialResult->contains($oids[1]));
+ }
+ }
+
+ public function testFindAcls()
+ {
+ $oids = array();
+ $oids[] = new ObjectIdentity('1', 'foo');
+ $oids[] = new ObjectIdentity('2', 'foo');
+
+ $provider = $this->getProvider();
+
+ $acls = $provider->findAcls($oids);
+ $this->assertInstanceOf('SplObjectStorage', $acls);
+ $this->assertCount(2, $acls);
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl0 = $acls->offsetGet($oids[0]));
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl1 = $acls->offsetGet($oids[1]));
+ $this->assertTrue($oids[0]->equals($acl0->getObjectIdentity()));
+ $this->assertTrue($oids[1]->equals($acl1->getObjectIdentity()));
+ }
+
+ public function testFindAclsWithDifferentTypes()
+ {
+ $oids = array();
+ $oids[] = new ObjectIdentity('123', 'Bundle\SomeVendor\MyBundle\Entity\SomeEntity');
+ $oids[] = new ObjectIdentity('123', 'Bundle\MyBundle\Entity\AnotherEntity');
+
+ $provider = $this->getProvider();
+
+ $acls = $provider->findAcls($oids);
+ $this->assertInstanceOf('SplObjectStorage', $acls);
+ $this->assertCount(2, $acls);
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl0 = $acls->offsetGet($oids[0]));
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl1 = $acls->offsetGet($oids[1]));
+ $this->assertTrue($oids[0]->equals($acl0->getObjectIdentity()));
+ $this->assertTrue($oids[1]->equals($acl1->getObjectIdentity()));
+ }
+
+ public function testFindAclCachesAclInMemory()
+ {
+ $oid = new ObjectIdentity('1', 'foo');
+ $provider = $this->getProvider();
+
+ $acl = $provider->findAcl($oid);
+ $this->assertSame($acl, $cAcl = $provider->findAcl($oid));
+
+ $cAces = $cAcl->getObjectAces();
+ foreach ($acl->getObjectAces() as $index => $ace) {
+ $this->assertSame($ace, $cAces[$index]);
+ }
+ }
+
+ public function testFindAcl()
+ {
+ $oid = new ObjectIdentity('1', 'foo');
+ $provider = $this->getProvider();
+
+ $acl = $provider->findAcl($oid);
+
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl);
+ $this->assertTrue($oid->equals($acl->getObjectIdentity()));
+ $this->assertEquals(4, $acl->getId());
+ $this->assertCount(0, $acl->getClassAces());
+ $this->assertCount(0, $this->getField($acl, 'classFieldAces'));
+ $this->assertCount(3, $acl->getObjectAces());
+ $this->assertCount(0, $this->getField($acl, 'objectFieldAces'));
+
+ $aces = $acl->getObjectAces();
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Entry', $aces[0]);
+ $this->assertTrue($aces[0]->isGranting());
+ $this->assertTrue($aces[0]->isAuditSuccess());
+ $this->assertTrue($aces[0]->isAuditFailure());
+ $this->assertEquals('all', $aces[0]->getStrategy());
+ $this->assertSame(2, $aces[0]->getMask());
+
+ // check ACE are in correct order
+ $i = 0;
+ foreach ($aces as $index => $ace) {
+ $this->assertEquals($i, $index);
+ ++$i;
+ }
+
+ $sid = $aces[0]->getSecurityIdentity();
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\UserSecurityIdentity', $sid);
+ $this->assertEquals('john.doe', $sid->getUsername());
+ $this->assertEquals('SomeClass', $sid->getClass());
+ }
+
+ protected function setUp()
+ {
+ $this->con = DriverManager::getConnection(array(
+ 'driver' => 'pdo_sqlite',
+ 'memory' => true,
+ ));
+
+ // import the schema
+ $schema = new Schema($options = $this->getOptions());
+ foreach ($schema->toSql($this->con->getDatabasePlatform()) as $sql) {
+ $this->con->exec($sql);
+ }
+
+ // populate the schema with some test data
+ $this->insertClassStmt = $this->con->prepare('INSERT INTO acl_classes (id, class_type) VALUES (?, ?)');
+ foreach ($this->getClassData() as $data) {
+ $this->insertClassStmt->execute($data);
+ }
+
+ $this->insertSidStmt = $this->con->prepare('INSERT INTO acl_security_identities (id, identifier, username) VALUES (?, ?, ?)');
+ foreach ($this->getSidData() as $data) {
+ $this->insertSidStmt->execute($data);
+ }
+
+ $this->insertOidStmt = $this->con->prepare('INSERT INTO acl_object_identities (id, class_id, object_identifier, parent_object_identity_id, entries_inheriting) VALUES (?, ?, ?, ?, ?)');
+ foreach ($this->getOidData() as $data) {
+ $this->insertOidStmt->execute($data);
+ }
+
+ $this->insertEntryStmt = $this->con->prepare('INSERT INTO acl_entries (id, class_id, object_identity_id, field_name, ace_order, security_identity_id, mask, granting, granting_strategy, audit_success, audit_failure) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
+ foreach ($this->getEntryData() as $data) {
+ $this->insertEntryStmt->execute($data);
+ }
+
+ $this->insertOidAncestorStmt = $this->con->prepare('INSERT INTO acl_object_identity_ancestors (object_identity_id, ancestor_id) VALUES (?, ?)');
+ foreach ($this->getOidAncestorData() as $data) {
+ $this->insertOidAncestorStmt->execute($data);
+ }
+ }
+
+ protected function tearDown()
+ {
+ $this->con = null;
+ }
+
+ protected function getField($object, $field)
+ {
+ $reflection = new \ReflectionProperty($object, $field);
+ $reflection->setAccessible(true);
+
+ return $reflection->getValue($object);
+ }
+
+ protected function getEntryData()
+ {
+ // id, cid, oid, field, order, sid, mask, granting, strategy, a success, a failure
+ return array(
+ array(1, 1, 1, null, 0, 1, 1, 1, 'all', 1, 1),
+ array(2, 1, 1, null, 1, 2, 1 << 2 | 1 << 1, 0, 'any', 0, 0),
+ array(3, 3, 4, null, 0, 1, 2, 1, 'all', 1, 1),
+ array(4, 3, 4, null, 2, 2, 1, 1, 'all', 1, 1),
+ array(5, 3, 4, null, 1, 3, 1, 1, 'all', 1, 1),
+ );
+ }
+
+ protected function getOidData()
+ {
+ // id, cid, oid, parent_oid, entries_inheriting
+ return array(
+ array(1, 1, '123', null, 1),
+ array(2, 2, '123', 1, 1),
+ array(3, 2, 'i:3:123', 1, 1),
+ array(4, 3, '1', 2, 1),
+ array(5, 3, '2', 2, 1),
+ );
+ }
+
+ protected function getOidAncestorData()
+ {
+ return array(
+ array(1, 1),
+ array(2, 1),
+ array(2, 2),
+ array(3, 1),
+ array(3, 3),
+ array(4, 2),
+ array(4, 1),
+ array(4, 4),
+ array(5, 2),
+ array(5, 1),
+ array(5, 5),
+ );
+ }
+
+ protected function getSidData()
+ {
+ return array(
+ array(1, 'SomeClass-john.doe', 1),
+ array(2, 'MyClass-john.doe@foo.com', 1),
+ array(3, 'FooClass-123', 1),
+ array(4, 'MooClass-ROLE_USER', 1),
+ array(5, 'ROLE_USER', 0),
+ array(6, 'IS_AUTHENTICATED_FULLY', 0),
+ );
+ }
+
+ protected function getClassData()
+ {
+ return array(
+ array(1, 'Bundle\SomeVendor\MyBundle\Entity\SomeEntity'),
+ array(2, 'Bundle\MyBundle\Entity\AnotherEntity'),
+ array(3, 'foo'),
+ );
+ }
+
+ protected function getOptions()
+ {
+ return array(
+ 'oid_table_name' => 'acl_object_identities',
+ 'oid_ancestors_table_name' => 'acl_object_identity_ancestors',
+ 'class_table_name' => 'acl_classes',
+ 'sid_table_name' => 'acl_security_identities',
+ 'entry_table_name' => 'acl_entries',
+ );
+ }
+
+ protected function getStrategy()
+ {
+ return new PermissionGrantingStrategy();
+ }
+
+ protected function getProvider()
+ {
+ return new AclProvider($this->con, $this->getStrategy(), $this->getOptions());
+ }
+}
diff --git a/Acl/Tests/Dbal/MutableAclProviderTest.php b/Acl/Tests/Dbal/MutableAclProviderTest.php
new file mode 100644
index 0000000..c2169e4
--- /dev/null
+++ b/Acl/Tests/Dbal/MutableAclProviderTest.php
@@ -0,0 +1,572 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Acl\Tests\Dbal;
+
+use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
+use Symfony\Component\Security\Acl\Model\FieldEntryInterface;
+use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
+use Symfony\Component\Security\Acl\Model\EntryInterface;
+use Symfony\Component\Security\Acl\Domain\Entry;
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\Acl;
+use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
+use Symfony\Component\Security\Acl\Exception\ConcurrentModificationException;
+use Symfony\Component\Security\Acl\Dbal\AclProvider;
+use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
+use Symfony\Component\Security\Acl\Dbal\MutableAclProvider;
+use Symfony\Component\Security\Acl\Dbal\Schema;
+use Doctrine\DBAL\DriverManager;
+use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+
+/**
+ * @requires extension pdo_sqlite
+ */
+class MutableAclProviderTest extends \PHPUnit_Framework_TestCase
+{
+ protected $con;
+
+ public static function assertAceEquals(EntryInterface $a, EntryInterface $b)
+ {
+ self::assertInstanceOf(get_class($a), $b);
+
+ foreach (array('getId', 'getMask', 'getStrategy', 'isGranting') as $getter) {
+ self::assertSame($a->$getter(), $b->$getter());
+ }
+
+ self::assertTrue($a->getSecurityIdentity()->equals($b->getSecurityIdentity()));
+ self::assertSame($a->getAcl()->getId(), $b->getAcl()->getId());
+
+ if ($a instanceof AuditableEntryInterface) {
+ self::assertSame($a->isAuditSuccess(), $b->isAuditSuccess());
+ self::assertSame($a->isAuditFailure(), $b->isAuditFailure());
+ }
+
+ if ($a instanceof FieldEntryInterface) {
+ self::assertSame($a->getField(), $b->getField());
+ }
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException
+ */
+ public function testCreateAclThrowsExceptionWhenAclAlreadyExists()
+ {
+ $provider = $this->getProvider();
+ $oid = new ObjectIdentity('123456', 'FOO');
+ $provider->createAcl($oid);
+ $provider->createAcl($oid);
+ }
+
+ public function testCreateAcl()
+ {
+ $provider = $this->getProvider();
+ $oid = new ObjectIdentity('123456', 'FOO');
+ $acl = $provider->createAcl($oid);
+ $cachedAcl = $provider->findAcl($oid);
+
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl);
+ $this->assertSame($acl, $cachedAcl);
+ $this->assertTrue($acl->getObjectIdentity()->equals($oid));
+ }
+
+ public function testDeleteAcl()
+ {
+ $provider = $this->getProvider();
+ $oid = new ObjectIdentity(1, 'Foo');
+ $acl = $provider->createAcl($oid);
+
+ $provider->deleteAcl($oid);
+ $loadedAcls = $this->getField($provider, 'loadedAcls');
+ $this->assertCount(0, $loadedAcls['Foo']);
+
+ try {
+ $provider->findAcl($oid);
+ $this->fail('ACL has not been properly deleted.');
+ } catch (AclNotFoundException $e) {
+ }
+ }
+
+ public function testDeleteAclDeletesChildren()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $parentAcl = $provider->createAcl(new ObjectIdentity(2, 'Foo'));
+ $acl->setParentAcl($parentAcl);
+ $provider->updateAcl($acl);
+ $provider->deleteAcl($parentAcl->getObjectIdentity());
+
+ try {
+ $provider->findAcl(new ObjectIdentity(1, 'Foo'));
+ $this->fail('Child-ACLs have not been deleted.');
+ } catch (AclNotFoundException $e) {
+ }
+ }
+
+ public function testFindAclsAddsPropertyListener()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+
+ $propertyChanges = $this->getField($provider, 'propertyChanges');
+ $this->assertCount(1, $propertyChanges);
+ $this->assertTrue($propertyChanges->contains($acl));
+ $this->assertEquals(array(), $propertyChanges->offsetGet($acl));
+
+ $listeners = $this->getField($acl, 'listeners');
+ $this->assertSame($provider, $listeners[0]);
+ }
+
+ public function testFindAclsAddsPropertyListenerOnlyOnce()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $acl = $provider->findAcl(new ObjectIdentity(1, 'Foo'));
+
+ $propertyChanges = $this->getField($provider, 'propertyChanges');
+ $this->assertCount(1, $propertyChanges);
+ $this->assertTrue($propertyChanges->contains($acl));
+ $this->assertEquals(array(), $propertyChanges->offsetGet($acl));
+
+ $listeners = $this->getField($acl, 'listeners');
+ $this->assertCount(1, $listeners);
+ $this->assertSame($provider, $listeners[0]);
+ }
+
+ public function testFindAclsAddsPropertyListenerToParentAcls()
+ {
+ $provider = $this->getProvider();
+ $this->importAcls($provider, array(
+ 'main' => array(
+ 'object_identifier' => '1',
+ 'class_type' => 'foo',
+ 'parent_acl' => 'parent',
+ ),
+ 'parent' => array(
+ 'object_identifier' => '1',
+ 'class_type' => 'anotherFoo',
+ ),
+ ));
+
+ $propertyChanges = $this->getField($provider, 'propertyChanges');
+ $this->assertCount(0, $propertyChanges);
+
+ $acl = $provider->findAcl(new ObjectIdentity('1', 'foo'));
+ $this->assertCount(2, $propertyChanges);
+ $this->assertTrue($propertyChanges->contains($acl));
+ $this->assertTrue($propertyChanges->contains($acl->getParentAcl()));
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testPropertyChangedDoesNotTrackUnmanagedAcls()
+ {
+ $provider = $this->getProvider();
+ $acl = new Acl(1, new ObjectIdentity(1, 'foo'), new PermissionGrantingStrategy(), array(), false);
+
+ $provider->propertyChanged($acl, 'classAces', array(), array('foo'));
+ }
+
+ public function testPropertyChangedTracksChangesToAclProperties()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $propertyChanges = $this->getField($provider, 'propertyChanges');
+
+ $provider->propertyChanged($acl, 'entriesInheriting', false, true);
+ $changes = $propertyChanges->offsetGet($acl);
+ $this->assertTrue(isset($changes['entriesInheriting']));
+ $this->assertFalse($changes['entriesInheriting'][0]);
+ $this->assertTrue($changes['entriesInheriting'][1]);
+
+ $provider->propertyChanged($acl, 'entriesInheriting', true, false);
+ $provider->propertyChanged($acl, 'entriesInheriting', false, true);
+ $provider->propertyChanged($acl, 'entriesInheriting', true, false);
+ $changes = $propertyChanges->offsetGet($acl);
+ $this->assertFalse(isset($changes['entriesInheriting']));
+ }
+
+ public function testPropertyChangedTracksChangesToAceProperties()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $ace = new Entry(1, $acl, new UserSecurityIdentity('foo', 'FooClass'), 'all', 1, true, true, true);
+ $ace2 = new Entry(2, $acl, new UserSecurityIdentity('foo', 'FooClass'), 'all', 1, true, true, true);
+ $propertyChanges = $this->getField($provider, 'propertyChanges');
+
+ $provider->propertyChanged($ace, 'mask', 1, 3);
+ $changes = $propertyChanges->offsetGet($acl);
+ $this->assertTrue(isset($changes['aces']));
+ $this->assertInstanceOf('\SplObjectStorage', $changes['aces']);
+ $this->assertTrue($changes['aces']->contains($ace));
+ $aceChanges = $changes['aces']->offsetGet($ace);
+ $this->assertTrue(isset($aceChanges['mask']));
+ $this->assertEquals(1, $aceChanges['mask'][0]);
+ $this->assertEquals(3, $aceChanges['mask'][1]);
+
+ $provider->propertyChanged($ace, 'strategy', 'all', 'any');
+ $changes = $propertyChanges->offsetGet($acl);
+ $this->assertTrue(isset($changes['aces']));
+ $this->assertInstanceOf('\SplObjectStorage', $changes['aces']);
+ $this->assertTrue($changes['aces']->contains($ace));
+ $aceChanges = $changes['aces']->offsetGet($ace);
+ $this->assertTrue(isset($aceChanges['mask']));
+ $this->assertTrue(isset($aceChanges['strategy']));
+ $this->assertEquals('all', $aceChanges['strategy'][0]);
+ $this->assertEquals('any', $aceChanges['strategy'][1]);
+
+ $provider->propertyChanged($ace, 'mask', 3, 1);
+ $changes = $propertyChanges->offsetGet($acl);
+ $aceChanges = $changes['aces']->offsetGet($ace);
+ $this->assertFalse(isset($aceChanges['mask']));
+ $this->assertTrue(isset($aceChanges['strategy']));
+
+ $provider->propertyChanged($ace2, 'mask', 1, 3);
+ $provider->propertyChanged($ace, 'strategy', 'any', 'all');
+ $changes = $propertyChanges->offsetGet($acl);
+ $this->assertTrue(isset($changes['aces']));
+ $this->assertFalse($changes['aces']->contains($ace));
+ $this->assertTrue($changes['aces']->contains($ace2));
+
+ $provider->propertyChanged($ace2, 'mask', 3, 4);
+ $provider->propertyChanged($ace2, 'mask', 4, 1);
+ $changes = $propertyChanges->offsetGet($acl);
+ $this->assertFalse(isset($changes['aces']));
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testUpdateAclDoesNotAcceptUntrackedAcls()
+ {
+ $provider = $this->getProvider();
+ $acl = new Acl(1, new ObjectIdentity(1, 'Foo'), new PermissionGrantingStrategy(), array(), true);
+ $provider->updateAcl($acl);
+ }
+
+ public function testUpdateDoesNothingWhenThereAreNoChanges()
+ {
+ $con = $this->getMock('Doctrine\DBAL\Connection', array(), array(), '', false);
+ $con
+ ->expects($this->never())
+ ->method('beginTransaction')
+ ;
+ $con
+ ->expects($this->never())
+ ->method('executeQuery')
+ ;
+
+ $provider = new MutableAclProvider($con, new PermissionGrantingStrategy(), array());
+ $acl = new Acl(1, new ObjectIdentity(1, 'Foo'), new PermissionGrantingStrategy(), array(), true);
+ $propertyChanges = $this->getField($provider, 'propertyChanges');
+ $propertyChanges->offsetSet($acl, array());
+ $provider->updateAcl($acl);
+ }
+
+ public function testUpdateAclThrowsExceptionOnConcurrentModificationOfSharedProperties()
+ {
+ $provider = $this->getProvider();
+ $acl1 = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $acl2 = $provider->createAcl(new ObjectIdentity(2, 'Foo'));
+ $acl3 = $provider->createAcl(new ObjectIdentity(1, 'AnotherFoo'));
+ $sid = new RoleSecurityIdentity('ROLE_FOO');
+
+ $acl1->insertClassAce($sid, 1);
+ $acl3->insertClassAce($sid, 1);
+ $provider->updateAcl($acl1);
+ $provider->updateAcl($acl3);
+
+ $acl2->insertClassAce($sid, 16);
+ $provider->updateAcl($acl2);
+
+ $acl1->insertClassAce($sid, 3);
+ $acl2->insertClassAce($sid, 5);
+ try {
+ $provider->updateAcl($acl1);
+ $this->fail('Provider failed to detect a concurrent modification.');
+ } catch (ConcurrentModificationException $e) {
+ }
+ }
+
+ public function testUpdateAcl()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $sid = new UserSecurityIdentity('johannes', 'FooClass');
+ $acl->setEntriesInheriting(!$acl->isEntriesInheriting());
+
+ $acl->insertObjectAce($sid, 1);
+ $acl->insertClassAce($sid, 5, 0, false);
+ $acl->insertObjectAce($sid, 2, 1, true);
+ $acl->insertClassFieldAce('field', $sid, 2, 0, true);
+ $provider->updateAcl($acl);
+
+ $acl->updateObjectAce(0, 3);
+ $acl->deleteObjectAce(1);
+ $acl->updateObjectAuditing(0, true, false);
+ $acl->updateClassFieldAce(0, 'field', 15);
+ $provider->updateAcl($acl);
+
+ $reloadProvider = $this->getProvider();
+ $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
+ $this->assertNotSame($acl, $reloadedAcl);
+ $this->assertSame($acl->isEntriesInheriting(), $reloadedAcl->isEntriesInheriting());
+
+ $aces = $acl->getObjectAces();
+ $reloadedAces = $reloadedAcl->getObjectAces();
+ $this->assertEquals(count($aces), count($reloadedAces));
+ foreach ($aces as $index => $ace) {
+ $this->assertAceEquals($ace, $reloadedAces[$index]);
+ }
+ }
+
+ public function testUpdateAclWorksForChangingTheParentAcl()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $parentAcl = $provider->createAcl(new ObjectIdentity(1, 'AnotherFoo'));
+ $acl->setParentAcl($parentAcl);
+ $provider->updateAcl($acl);
+
+ $reloadProvider = $this->getProvider();
+ $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
+ $this->assertNotSame($acl, $reloadedAcl);
+ $this->assertSame($parentAcl->getId(), $reloadedAcl->getParentAcl()->getId());
+ }
+
+ public function testUpdateAclUpdatesChildAclsCorrectly()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+
+ $parentAcl = $provider->createAcl(new ObjectIdentity(1, 'Bar'));
+ $acl->setParentAcl($parentAcl);
+ $provider->updateAcl($acl);
+
+ $parentParentAcl = $provider->createAcl(new ObjectIdentity(1, 'Baz'));
+ $parentAcl->setParentAcl($parentParentAcl);
+ $provider->updateAcl($parentAcl);
+
+ $newParentParentAcl = $provider->createAcl(new ObjectIdentity(2, 'Baz'));
+ $parentAcl->setParentAcl($newParentParentAcl);
+ $provider->updateAcl($parentAcl);
+
+ $reloadProvider = $this->getProvider();
+ $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
+ $this->assertEquals($newParentParentAcl->getId(), $reloadedAcl->getParentAcl()->getParentAcl()->getId());
+ }
+
+ public function testUpdateAclInsertingMultipleObjectFieldAcesThrowsDBConstraintViolations()
+ {
+ $provider = $this->getProvider();
+ $oid = new ObjectIdentity(1, 'Foo');
+ $sid1 = new UserSecurityIdentity('johannes', 'FooClass');
+ $sid2 = new UserSecurityIdentity('guilro', 'FooClass');
+ $sid3 = new UserSecurityIdentity('bmaz', 'FooClass');
+ $fieldName = 'fieldName';
+
+ $acl = $provider->createAcl($oid);
+ $acl->insertObjectFieldAce($fieldName, $sid1, 4);
+ $provider->updateAcl($acl);
+
+ $acl = $provider->findAcl($oid);
+ $acl->insertObjectFieldAce($fieldName, $sid2, 4);
+ $provider->updateAcl($acl);
+
+ $acl = $provider->findAcl($oid);
+ $acl->insertObjectFieldAce($fieldName, $sid3, 4);
+ $provider->updateAcl($acl);
+ }
+
+ public function testUpdateAclDeletingObjectFieldAcesThrowsDBConstraintViolations()
+ {
+ $provider = $this->getProvider();
+ $oid = new ObjectIdentity(1, 'Foo');
+ $sid1 = new UserSecurityIdentity('johannes', 'FooClass');
+ $sid2 = new UserSecurityIdentity('guilro', 'FooClass');
+ $sid3 = new UserSecurityIdentity('bmaz', 'FooClass');
+ $fieldName = 'fieldName';
+
+ $acl = $provider->createAcl($oid);
+ $acl->insertObjectFieldAce($fieldName, $sid1, 4);
+ $provider->updateAcl($acl);
+
+ $acl = $provider->findAcl($oid);
+ $acl->insertObjectFieldAce($fieldName, $sid2, 4);
+ $provider->updateAcl($acl);
+
+ $index = 0;
+ $acl->deleteObjectFieldAce($index, $fieldName);
+ $provider->updateAcl($acl);
+
+ $acl = $provider->findAcl($oid);
+ $acl->insertObjectFieldAce($fieldName, $sid3, 4);
+ $provider->updateAcl($acl);
+ }
+
+ public function testUpdateUserSecurityIdentity()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $sid = new UserSecurityIdentity('johannes', 'FooClass');
+ $acl->setEntriesInheriting(!$acl->isEntriesInheriting());
+
+ $acl->insertObjectAce($sid, 1);
+ $acl->insertClassAce($sid, 5, 0, false);
+ $acl->insertObjectAce($sid, 2, 1, true);
+ $acl->insertClassFieldAce('field', $sid, 2, 0, true);
+ $provider->updateAcl($acl);
+
+ $newSid = new UserSecurityIdentity('mathieu', 'FooClass');
+ $provider->updateUserSecurityIdentity($newSid, 'johannes');
+
+ $reloadProvider = $this->getProvider();
+ $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
+
+ $this->assertNotSame($acl, $reloadedAcl);
+ $this->assertSame($acl->isEntriesInheriting(), $reloadedAcl->isEntriesInheriting());
+
+ $aces = $acl->getObjectAces();
+ $reloadedAces = $reloadedAcl->getObjectAces();
+ $this->assertEquals(count($aces), count($reloadedAces));
+ foreach ($reloadedAces as $ace) {
+ $this->assertTrue($ace->getSecurityIdentity()->equals($newSid));
+ }
+ }
+
+ /**
+ * Imports acls.
+ *
+ * Data must have the following format:
+ * array(
+ * *name* => array(
+ * 'object_identifier' => *required*
+ * 'class_type' => *required*,
+ * 'parent_acl' => *name (optional)*
+ * ),
+ * )
+ *
+ * @param AclProvider $provider
+ * @param array $data
+ *
+ * @throws \InvalidArgumentException
+ * @throws \Exception
+ */
+ protected function importAcls(AclProvider $provider, array $data)
+ {
+ $aclIds = $parentAcls = array();
+ $con = $this->getField($provider, 'connection');
+ $con->beginTransaction();
+ try {
+ foreach ($data as $name => $aclData) {
+ if (!isset($aclData['object_identifier'], $aclData['class_type'])) {
+ throw new \InvalidArgumentException('"object_identifier", and "class_type" must be present.');
+ }
+
+ $this->callMethod($provider, 'createObjectIdentity', array(new ObjectIdentity($aclData['object_identifier'], $aclData['class_type'])));
+ $aclId = $con->lastInsertId();
+ $aclIds[$name] = $aclId;
+
+ $sql = $this->callMethod($provider, 'getInsertObjectIdentityRelationSql', array($aclId, $aclId));
+ $con->executeQuery($sql);
+
+ if (isset($aclData['parent_acl'])) {
+ if (isset($aclIds[$aclData['parent_acl']])) {
+ $con->executeQuery('UPDATE acl_object_identities SET parent_object_identity_id = '.$aclIds[$aclData['parent_acl']].' WHERE id = '.$aclId);
+ $con->executeQuery($this->callMethod($provider, 'getInsertObjectIdentityRelationSql', array($aclId, $aclIds[$aclData['parent_acl']])));
+ } else {
+ $parentAcls[$aclId] = $aclData['parent_acl'];
+ }
+ }
+ }
+
+ foreach ($parentAcls as $aclId => $name) {
+ if (!isset($aclIds[$name])) {
+ throw new \InvalidArgumentException(sprintf('"%s" does not exist.', $name));
+ }
+
+ $con->executeQuery(sprintf('UPDATE acl_object_identities SET parent_object_identity_id = %d WHERE id = %d', $aclIds[$name], $aclId));
+ $con->executeQuery($this->callMethod($provider, 'getInsertObjectIdentityRelationSql', array($aclId, $aclIds[$name])));
+ }
+
+ $con->commit();
+ } catch (\Exception $e) {
+ $con->rollBack();
+
+ throw $e;
+ }
+ }
+
+ protected function callMethod($object, $method, array $args)
+ {
+ $method = new \ReflectionMethod($object, $method);
+ $method->setAccessible(true);
+
+ return $method->invokeArgs($object, $args);
+ }
+
+ protected function setUp()
+ {
+ $this->con = DriverManager::getConnection(array(
+ 'driver' => 'pdo_sqlite',
+ 'memory' => true,
+ ));
+
+ // import the schema
+ $schema = new Schema($this->getOptions());
+ foreach ($schema->toSql($this->con->getDatabasePlatform()) as $sql) {
+ $this->con->exec($sql);
+ }
+ }
+
+ protected function tearDown()
+ {
+ $this->con = null;
+ }
+
+ protected function getField($object, $field)
+ {
+ $reflection = new \ReflectionProperty($object, $field);
+ $reflection->setAccessible(true);
+
+ return $reflection->getValue($object);
+ }
+
+ public function setField($object, $field, $value)
+ {
+ $reflection = new \ReflectionProperty($object, $field);
+ $reflection->setAccessible(true);
+ $reflection->setValue($object, $value);
+ $reflection->setAccessible(false);
+ }
+
+ protected function getOptions()
+ {
+ return array(
+ 'oid_table_name' => 'acl_object_identities',
+ 'oid_ancestors_table_name' => 'acl_object_identity_ancestors',
+ 'class_table_name' => 'acl_classes',
+ 'sid_table_name' => 'acl_security_identities',
+ 'entry_table_name' => 'acl_entries',
+ );
+ }
+
+ protected function getStrategy()
+ {
+ return new PermissionGrantingStrategy();
+ }
+
+ protected function getProvider($cache = null)
+ {
+ return new MutableAclProvider($this->con, $this->getStrategy(), $this->getOptions(), $cache);
+ }
+}