summaryrefslogtreecommitdiffstats
path: root/Acl
diff options
context:
space:
mode:
Diffstat (limited to 'Acl')
-rw-r--r--Acl/.gitignore3
-rw-r--r--Acl/Dbal/AclProvider.php6
-rw-r--r--Acl/Domain/Acl.php16
-rw-r--r--Acl/LICENSE19
-rw-r--r--Acl/README.md23
-rw-r--r--Acl/Tests/Dbal/AclProviderBenchmarkTest.php267
-rw-r--r--Acl/Tests/Dbal/AclProviderTest.php281
-rw-r--r--Acl/Tests/Dbal/MutableAclProviderTest.php537
-rw-r--r--Acl/Tests/Domain/AclTest.php514
-rw-r--r--Acl/Tests/Domain/AuditLoggerTest.php83
-rw-r--r--Acl/Tests/Domain/DoctrineAclCacheTest.php101
-rw-r--r--Acl/Tests/Domain/EntryTest.php119
-rw-r--r--Acl/Tests/Domain/FieldEntryTest.php74
-rw-r--r--Acl/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php41
-rw-r--r--Acl/Tests/Domain/ObjectIdentityTest.php109
-rw-r--r--Acl/Tests/Domain/PermissionGrantingStrategyTest.php185
-rw-r--r--Acl/Tests/Domain/RoleSecurityIdentityTest.php55
-rw-r--r--Acl/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php196
-rw-r--r--Acl/Tests/Domain/UserSecurityIdentityTest.php73
-rw-r--r--Acl/Tests/Permission/BasicPermissionMapTest.php23
-rw-r--r--Acl/Tests/Permission/MaskBuilderTest.php103
-rw-r--r--Acl/Tests/Voter/AclVoterTest.php405
-rw-r--r--Acl/composer.json42
-rw-r--r--Acl/phpunit.xml.dist29
24 files changed, 3289 insertions, 15 deletions
diff --git a/Acl/.gitignore b/Acl/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/Acl/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/Acl/Dbal/AclProvider.php b/Acl/Dbal/AclProvider.php
index 1d1cb16..5d45655 100644
--- a/Acl/Dbal/AclProvider.php
+++ b/Acl/Dbal/AclProvider.php
@@ -40,8 +40,8 @@ class AclProvider implements AclProviderInterface
protected $cache;
protected $connection;
- protected $loadedAces;
- protected $loadedAcls;
+ protected $loadedAces = array();
+ protected $loadedAcls = array();
protected $options;
private $permissionGrantingStrategy;
@@ -57,8 +57,6 @@ class AclProvider implements AclProviderInterface
{
$this->cache = $cache;
$this->connection = $connection;
- $this->loadedAces = array();
- $this->loadedAcls = array();
$this->options = $options;
$this->permissionGrantingStrategy = $permissionGrantingStrategy;
}
diff --git a/Acl/Domain/Acl.php b/Acl/Domain/Acl.php
index 4665c0e..dd3e8d4 100644
--- a/Acl/Domain/Acl.php
+++ b/Acl/Domain/Acl.php
@@ -37,14 +37,14 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
private $parentAcl;
private $permissionGrantingStrategy;
private $objectIdentity;
- private $classAces;
- private $classFieldAces;
- private $objectAces;
- private $objectFieldAces;
+ private $classAces = array();
+ private $classFieldAces = array();
+ private $objectAces = array();
+ private $objectFieldAces = array();
private $id;
private $loadedSids;
private $entriesInheriting;
- private $listeners;
+ private $listeners = array();
/**
* Constructor
@@ -62,12 +62,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
$this->permissionGrantingStrategy = $permissionGrantingStrategy;
$this->loadedSids = $loadedSids;
$this->entriesInheriting = $entriesInheriting;
- $this->parentAcl = null;
- $this->classAces = array();
- $this->classFieldAces = array();
- $this->objectAces = array();
- $this->objectFieldAces = array();
- $this->listeners = array();
}
/**
diff --git a/Acl/LICENSE b/Acl/LICENSE
new file mode 100644
index 0000000..0b3292c
--- /dev/null
+++ b/Acl/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2014 Fabien Potencier
+
+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.
diff --git a/Acl/README.md b/Acl/README.md
new file mode 100644
index 0000000..87e5092
--- /dev/null
+++ b/Acl/README.md
@@ -0,0 +1,23 @@
+Security Component - ACL (Access Control List)
+==============================================
+
+Security provides an infrastructure for sophisticated authorization systems,
+which makes it possible to easily separate the actual authorization logic from
+so called user providers that hold the users credentials. It is inspired by
+the Java Spring framework.
+
+Resources
+---------
+
+Documentation:
+
+http://symfony.com/doc/2.4/book/security.html
+
+Tests
+-----
+
+You can run the unit tests with the following command:
+
+ $ cd path/to/Symfony/Component/Security/Acl/
+ $ composer.phar install --dev
+ $ phpunit
diff --git a/Acl/Tests/Dbal/AclProviderBenchmarkTest.php b/Acl/Tests/Dbal/AclProviderBenchmarkTest.php
new file mode 100644
index 0000000..8f68f1f
--- /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 += 1;
+
+ 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 += 1;
+
+ 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 += 1;
+
+ 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 += 1;
+ }
+ }
+
+ 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..717a258
--- /dev/null
+++ b/Acl/Tests/Dbal/AclProviderTest.php
@@ -0,0 +1,281 @@
+<?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;
+
+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 $ex) {
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Exception\AclNotFoundException', $ex);
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Exception\NotAllAclsFoundException', $ex);
+
+ $partialResult = $ex->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()
+ {
+ if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers())) {
+ self::markTestSkipped('This test requires SQLite support in your environment');
+ }
+
+ $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..440f69c
--- /dev/null
+++ b/Acl/Tests/Dbal/MutableAclProviderTest.php
@@ -0,0 +1,537 @@
+<?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;
+
+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 $notFound) { }
+ }
+
+ 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 $notFound) { }
+ }
+
+ 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 $ex) { }
+ }
+
+ 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);
+ }
+
+ /**
+ * 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()
+ {
+ if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers())) {
+ self::markTestSkipped('This test requires SQLite support in your environment');
+ }
+
+ $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);
+ }
+}
diff --git a/Acl/Tests/Domain/AclTest.php b/Acl/Tests/Domain/AclTest.php
new file mode 100644
index 0000000..2034c21
--- /dev/null
+++ b/Acl/Tests/Domain/AclTest.php
@@ -0,0 +1,514 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+
+use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
+use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+use Symfony\Component\Security\Acl\Domain\Acl;
+
+class AclTest extends \PHPUnit_Framework_TestCase
+{
+ public function testConstructor()
+ {
+ $acl = new Acl(1, $oid = new ObjectIdentity('foo', 'foo'), $permissionStrategy = new PermissionGrantingStrategy(), array(), true);
+
+ $this->assertSame(1, $acl->getId());
+ $this->assertSame($oid, $acl->getObjectIdentity());
+ $this->assertNull($acl->getParentAcl());
+ $this->assertTrue($acl->isEntriesInheriting());
+ }
+
+ /**
+ * @expectedException \OutOfBoundsException
+ * @dataProvider getDeleteAceTests
+ */
+ public function testDeleteAceThrowsExceptionOnInvalidIndex($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'delete'.$type.'Ace'}(0);
+ }
+
+ /**
+ * @dataProvider getDeleteAceTests
+ */
+ public function testDeleteAce($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 1);
+ $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 2, 1);
+ $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 3, 2);
+
+ $listener = $this->getListener(array(
+ $type.'Aces', 'aceOrder', 'aceOrder', $type.'Aces',
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $this->assertCount(3, $acl->{'get'.$type.'Aces'}());
+
+ $acl->{'delete'.$type.'Ace'}(0);
+ $this->assertCount(2, $aces = $acl->{'get'.$type.'Aces'}());
+ $this->assertEquals(2, $aces[0]->getMask());
+ $this->assertEquals(3, $aces[1]->getMask());
+
+ $acl->{'delete'.$type.'Ace'}(1);
+ $this->assertCount(1, $aces = $acl->{'get'.$type.'Aces'}());
+ $this->assertEquals(2, $aces[0]->getMask());
+ }
+
+ public function getDeleteAceTests()
+ {
+ return array(
+ array('class'),
+ array('object'),
+ );
+ }
+
+ /**
+ * @expectedException \OutOfBoundsException
+ * @dataProvider getDeleteFieldAceTests
+ */
+ public function testDeleteFieldAceThrowsExceptionOnInvalidIndex($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'delete'.$type.'Ace'}('foo', 0);
+ }
+
+ /**
+ * @dataProvider getDeleteFieldAceTests
+ */
+ public function testDeleteFieldAce($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 1, 0);
+ $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 2, 1);
+ $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 3, 2);
+
+ $listener = $this->getListener(array(
+ $type.'Aces', 'aceOrder', 'aceOrder', $type.'Aces',
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $this->assertCount(3, $acl->{'get'.$type.'Aces'}('foo'));
+
+ $acl->{'delete'.$type.'Ace'}(0, 'foo');
+ $this->assertCount(2, $aces = $acl->{'get'.$type.'Aces'}('foo'));
+ $this->assertEquals(2, $aces[0]->getMask());
+ $this->assertEquals(3, $aces[1]->getMask());
+
+ $acl->{'delete'.$type.'Ace'}(1, 'foo');
+ $this->assertCount(1, $aces = $acl->{'get'.$type.'Aces'}('foo'));
+ $this->assertEquals(2, $aces[0]->getMask());
+ }
+
+ public function getDeleteFieldAceTests()
+ {
+ return array(
+ array('classField'),
+ array('objectField'),
+ );
+ }
+
+ /**
+ * @dataProvider getInsertAceTests
+ */
+ public function testInsertAce($property, $method)
+ {
+ $acl = $this->getAcl();
+
+ $listener = $this->getListener(array(
+ $property, 'aceOrder', $property, 'aceOrder', $property
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $sid = new RoleSecurityIdentity('foo');
+ $acl->$method($sid, 1);
+ $acl->$method($sid, 2);
+ $acl->$method($sid, 3, 1, false);
+
+ $this->assertCount(3, $aces = $acl->{'get'.$property}());
+ $this->assertEquals(2, $aces[0]->getMask());
+ $this->assertEquals(3, $aces[1]->getMask());
+ $this->assertEquals(1, $aces[2]->getMask());
+ }
+
+ /**
+ * @expectedException \OutOfBoundsException
+ * @dataProvider getInsertAceTests
+ */
+ public function testInsertClassAceThrowsExceptionOnInvalidIndex($property, $method)
+ {
+ $acl = $this->getAcl();
+ $acl->$method(new RoleSecurityIdentity('foo'), 1, 1);
+ }
+
+ public function getInsertAceTests()
+ {
+ return array(
+ array('classAces', 'insertClassAce'),
+ array('objectAces', 'insertObjectAce'),
+ );
+ }
+
+ /**
+ * @dataProvider getInsertFieldAceTests
+ */
+ public function testInsertClassFieldAce($property, $method)
+ {
+ $acl = $this->getAcl();
+
+ $listener = $this->getListener(array(
+ $property, $property, 'aceOrder', $property,
+ 'aceOrder', 'aceOrder', $property,
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $sid = new RoleSecurityIdentity('foo');
+ $acl->$method('foo', $sid, 1);
+ $acl->$method('foo2', $sid, 1);
+ $acl->$method('foo', $sid, 3);
+ $acl->$method('foo', $sid, 2);
+
+ $this->assertCount(3, $aces = $acl->{'get'.$property}('foo'));
+ $this->assertCount(1, $acl->{'get'.$property}('foo2'));
+ $this->assertEquals(2, $aces[0]->getMask());
+ $this->assertEquals(3, $aces[1]->getMask());
+ $this->assertEquals(1, $aces[2]->getMask());
+ }
+
+ /**
+ * @expectedException \OutOfBoundsException
+ * @dataProvider getInsertFieldAceTests
+ */
+ public function testInsertClassFieldAceThrowsExceptionOnInvalidIndex($property, $method)
+ {
+ $acl = $this->getAcl();
+ $acl->$method('foo', new RoleSecurityIdentity('foo'), 1, 1);
+ }
+
+ public function getInsertFieldAceTests()
+ {
+ return array(
+ array('classFieldAces', 'insertClassFieldAce'),
+ array('objectFieldAces', 'insertObjectFieldAce'),
+ );
+ }
+
+ public function testIsFieldGranted()
+ {
+ $sids = array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity('ROLE_IDDQD'));
+ $masks = array(1, 2, 4);
+ $strategy = $this->getMock('Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface');
+ $acl = new Acl(1, new ObjectIdentity(1, 'foo'), $strategy, array(), true);
+
+ $strategy
+ ->expects($this->once())
+ ->method('isFieldGranted')
+ ->with($this->equalTo($acl), $this->equalTo('foo'), $this->equalTo($masks), $this->equalTo($sids), $this->isTrue())
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($acl->isFieldGranted('foo', $masks, $sids, true));
+ }
+
+ public function testIsGranted()
+ {
+ $sids = array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity('ROLE_IDDQD'));
+ $masks = array(1, 2, 4);
+ $strategy = $this->getMock('Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface');
+ $acl = new Acl(1, new ObjectIdentity(1, 'foo'), $strategy, array(), true);
+
+ $strategy
+ ->expects($this->once())
+ ->method('isGranted')
+ ->with($this->equalTo($acl), $this->equalTo($masks), $this->equalTo($sids), $this->isTrue())
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($acl->isGranted($masks, $sids, true));
+ }
+
+ public function testSetGetParentAcl()
+ {
+ $acl = $this->getAcl();
+ $parentAcl = $this->getAcl();
+
+ $listener = $this->getListener(array('parentAcl'));
+ $acl->addPropertyChangedListener($listener);
+
+ $this->assertNull($acl->getParentAcl());
+ $acl->setParentAcl($parentAcl);
+ $this->assertSame($parentAcl, $acl->getParentAcl());
+
+ $acl->setParentAcl(null);
+ $this->assertNull($acl->getParentAcl());
+ }
+
+ public function testSetIsEntriesInheriting()
+ {
+ $acl = $this->getAcl();
+
+ $listener = $this->getListener(array('entriesInheriting'));
+ $acl->addPropertyChangedListener($listener);
+
+ $this->assertTrue($acl->isEntriesInheriting());
+ $acl->setEntriesInheriting(false);
+ $this->assertFalse($acl->isEntriesInheriting());
+ }
+
+ public function testIsSidLoadedWhenAllSidsAreLoaded()
+ {
+ $acl = $this->getAcl();
+
+ $this->assertTrue($acl->isSidLoaded(new UserSecurityIdentity('foo', 'Foo')));
+ $this->assertTrue($acl->isSidLoaded(new RoleSecurityIdentity('ROLE_FOO', 'Foo')));
+ }
+
+ public function testIsSidLoaded()
+ {
+ $acl = new Acl(1, new ObjectIdentity('1', 'foo'), new PermissionGrantingStrategy(), array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('johannes', 'Bar')), true);
+
+ $this->assertTrue($acl->isSidLoaded(new UserSecurityIdentity('foo', 'Foo')));
+ $this->assertTrue($acl->isSidLoaded(new UserSecurityIdentity('johannes', 'Bar')));
+ $this->assertTrue($acl->isSidLoaded(array(
+ new UserSecurityIdentity('foo', 'Foo'),
+ new UserSecurityIdentity('johannes', 'Bar'),
+ )));
+ $this->assertFalse($acl->isSidLoaded(new RoleSecurityIdentity('ROLE_FOO')));
+ $this->assertFalse($acl->isSidLoaded(new UserSecurityIdentity('schmittjoh@gmail.com', 'Moo')));
+ $this->assertFalse($acl->isSidLoaded(array(
+ new UserSecurityIdentity('foo', 'Foo'),
+ new UserSecurityIdentity('johannes', 'Bar'),
+ new RoleSecurityIdentity('ROLE_FOO'),
+ )));
+ }
+
+ /**
+ * @dataProvider getUpdateAceTests
+ * @expectedException \OutOfBoundsException
+ */
+ public function testUpdateAceThrowsOutOfBoundsExceptionOnInvalidIndex($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'update'.$type}(0, 1);
+ }
+
+ /**
+ * @dataProvider getUpdateAceTests
+ */
+ public function testUpdateAce($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'insert'.$type}(new RoleSecurityIdentity('foo'), 1);
+
+ $listener = $this->getListener(array(
+ 'mask', 'mask', 'strategy',
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $aces = $acl->{'get'.$type.'s'}();
+ $ace = reset($aces);
+ $this->assertEquals(1, $ace->getMask());
+ $this->assertEquals('all', $ace->getStrategy());
+
+ $acl->{'update'.$type}(0, 3);
+ $this->assertEquals(3, $ace->getMask());
+ $this->assertEquals('all', $ace->getStrategy());
+
+ $acl->{'update'.$type}(0, 1, 'foo');
+ $this->assertEquals(1, $ace->getMask());
+ $this->assertEquals('foo', $ace->getStrategy());
+ }
+
+ public function getUpdateAceTests()
+ {
+ return array(
+ array('classAce'),
+ array('objectAce'),
+ );
+ }
+
+ /**
+ * @dataProvider getUpdateFieldAceTests
+ * @expectedException \OutOfBoundsException
+ */
+ public function testUpdateFieldAceThrowsExceptionOnInvalidIndex($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'update'.$type}(0, 'foo', 1);
+ }
+
+ /**
+ * @dataProvider getUpdateFieldAceTests
+ */
+ public function testUpdateFieldAce($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'insert'.$type}('foo', new UserSecurityIdentity('foo', 'Foo'), 1);
+
+ $listener = $this->getListener(array(
+ 'mask', 'mask', 'strategy'
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $aces = $acl->{'get'.$type.'s'}('foo');
+ $ace = reset($aces);
+ $this->assertEquals(1, $ace->getMask());
+ $this->assertEquals('all', $ace->getStrategy());
+
+ $acl->{'update'.$type}(0, 'foo', 3);
+ $this->assertEquals(3, $ace->getMask());
+ $this->assertEquals('all', $ace->getStrategy());
+
+ $acl->{'update'.$type}(0, 'foo', 1, 'foo');
+ $this->assertEquals(1, $ace->getMask());
+ $this->assertEquals('foo', $ace->getStrategy());
+ }
+
+ public function getUpdateFieldAceTests()
+ {
+ return array(
+ array('classFieldAce'),
+ array('objectFieldAce'),
+ );
+ }
+
+ /**
+ * @dataProvider getUpdateAuditingTests
+ * @expectedException \OutOfBoundsException
+ */
+ public function testUpdateAuditingThrowsExceptionOnInvalidIndex($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'update'.$type.'Auditing'}(0, true, false);
+ }
+
+ /**
+ * @dataProvider getUpdateAuditingTests
+ */
+ public function testUpdateAuditing($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 1);
+
+ $listener = $this->getListener(array(
+ 'auditFailure', 'auditSuccess', 'auditFailure',
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $aces = $acl->{'get'.$type.'Aces'}();
+ $ace = reset($aces);
+ $this->assertFalse($ace->isAuditSuccess());
+ $this->assertFalse($ace->isAuditFailure());
+
+ $acl->{'update'.$type.'Auditing'}(0, false, true);
+ $this->assertFalse($ace->isAuditSuccess());
+ $this->assertTrue($ace->isAuditFailure());
+
+ $acl->{'update'.$type.'Auditing'}(0, true, false);
+ $this->assertTrue($ace->isAuditSuccess());
+ $this->assertFalse($ace->isAuditFailure());
+ }
+
+ public function getUpdateAuditingTests()
+ {
+ return array(
+ array('class'),
+ array('object'),
+ );
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @dataProvider getUpdateFieldAuditingTests
+ */
+ public function testUpdateFieldAuditingThrowsExceptionOnInvalidField($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'update'.$type.'Auditing'}(0, 'foo', true, true);
+ }
+
+ /**
+ * @expectedException \OutOfBoundsException
+ * @dataProvider getUpdateFieldAuditingTests
+ */
+ public function testUpdateFieldAuditingThrowsExceptionOnInvalidIndex($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 1);
+ $acl->{'update'.$type.'Auditing'}(1, 'foo', true, false);
+ }
+
+ /**
+ * @dataProvider getUpdateFieldAuditingTests
+ */
+ public function testUpdateFieldAuditing($type)
+ {
+ $acl = $this->getAcl();
+ $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 1);
+
+ $listener = $this->getListener(array(
+ 'auditSuccess', 'auditSuccess', 'auditFailure',
+ ));
+ $acl->addPropertyChangedListener($listener);
+
+ $aces = $acl->{'get'.$type.'Aces'}('foo');
+ $ace = reset($aces);
+ $this->assertFalse($ace->isAuditSuccess());
+ $this->assertFalse($ace->isAuditFailure());
+
+ $acl->{'update'.$type.'Auditing'}(0, 'foo', true, false);
+ $this->assertTrue($ace->isAuditSuccess());
+ $this->assertFalse($ace->isAuditFailure());
+
+ $acl->{'update'.$type.'Auditing'}(0, 'foo', false, true);
+ $this->assertFalse($ace->isAuditSuccess());
+ $this->assertTrue($ace->isAuditFailure());
+ }
+
+ public function getUpdateFieldAuditingTests()
+ {
+ return array(
+ array('classField'),
+ array('objectField'),
+ );
+ }
+
+ protected function getListener($expectedChanges)
+ {
+ $aceProperties = array('aceOrder', 'mask', 'strategy', 'auditSuccess', 'auditFailure');
+
+ $listener = $this->getMock('Doctrine\Common\PropertyChangedListener');
+ foreach ($expectedChanges as $index => $property) {
+ if (in_array($property, $aceProperties)) {
+ $class = 'Symfony\Component\Security\Acl\Domain\Entry';
+ } else {
+ $class = 'Symfony\Component\Security\Acl\Domain\Acl';
+ }
+
+ $listener
+ ->expects($this->at($index))
+ ->method('propertyChanged')
+ ->with($this->isInstanceOf($class), $this->equalTo($property))
+ ;
+ }
+
+ return $listener;
+ }
+
+ protected function getAcl()
+ {
+ return new Acl(1, new ObjectIdentity(1, 'foo'), new PermissionGrantingStrategy(), array(), true);
+ }
+}
diff --git a/Acl/Tests/Domain/AuditLoggerTest.php b/Acl/Tests/Domain/AuditLoggerTest.php
new file mode 100644
index 0000000..fe56b8c
--- /dev/null
+++ b/Acl/Tests/Domain/AuditLoggerTest.php
@@ -0,0 +1,83 @@
+<?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\Domain;
+
+class AuditLoggerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider getTestLogData
+ */
+ public function testLogIfNeeded($granting, $audit)
+ {
+ $logger = $this->getLogger();
+ $ace = $this->getEntry();
+
+ if (true === $granting) {
+ $ace
+ ->expects($this->once())
+ ->method('isAuditSuccess')
+ ->will($this->returnValue($audit))
+ ;
+
+ $ace
+ ->expects($this->never())
+ ->method('isAuditFailure')
+ ;
+ } else {
+ $ace
+ ->expects($this->never())
+ ->method('isAuditSuccess')
+ ;
+
+ $ace
+ ->expects($this->once())
+ ->method('isAuditFailure')
+ ->will($this->returnValue($audit))
+ ;
+ }
+
+ if (true === $audit) {
+ $logger
+ ->expects($this->once())
+ ->method('doLog')
+ ->with($this->equalTo($granting), $this->equalTo($ace))
+ ;
+ } else {
+ $logger
+ ->expects($this->never())
+ ->method('doLog')
+ ;
+ }
+
+ $logger->logIfNeeded($granting, $ace);
+ }
+
+ public function getTestLogData()
+ {
+ return array(
+ array(true, false),
+ array(true, true),
+ array(false, false),
+ array(false, true),
+ );
+ }
+
+ protected function getEntry()
+ {
+ return $this->getMock('Symfony\Component\Security\Acl\Model\AuditableEntryInterface');
+ }
+
+ protected function getLogger()
+ {
+ return $this->getMockForAbstractClass('Symfony\Component\Security\Acl\Domain\AuditLogger');
+ }
+}
diff --git a/Acl/Tests/Domain/DoctrineAclCacheTest.php b/Acl/Tests/Domain/DoctrineAclCacheTest.php
new file mode 100644
index 0000000..128f2c8
--- /dev/null
+++ b/Acl/Tests/Domain/DoctrineAclCacheTest.php
@@ -0,0 +1,101 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
+use Symfony\Component\Security\Acl\Domain\Acl;
+use Symfony\Component\Security\Acl\Domain\DoctrineAclCache;
+use Doctrine\Common\Cache\ArrayCache;
+
+class DoctrineAclCacheTest extends \PHPUnit_Framework_TestCase
+{
+ protected $permissionGrantingStrategy;
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @dataProvider getEmptyValue
+ */
+ public function testConstructorDoesNotAcceptEmptyPrefix($empty)
+ {
+ new DoctrineAclCache(new ArrayCache(), $this->getPermissionGrantingStrategy(), $empty);
+ }
+
+ public function getEmptyValue()
+ {
+ return array(
+ array(null),
+ array(false),
+ array(''),
+ );
+ }
+
+ public function test()
+ {
+ $cache = $this->getCache();
+
+ $aclWithParent = $this->getAcl(1);
+ $acl = $this->getAcl();
+
+ $cache->putInCache($aclWithParent);
+ $cache->putInCache($acl);
+
+ $cachedAcl = $cache->getFromCacheByIdentity($acl->getObjectIdentity());
+ $this->assertEquals($acl->getId(), $cachedAcl->getId());
+ $this->assertNull($acl->getParentAcl());
+
+ $cachedAclWithParent = $cache->getFromCacheByIdentity($aclWithParent->getObjectIdentity());
+ $this->assertEquals($aclWithParent->getId(), $cachedAclWithParent->getId());
+ $this->assertNotNull($cachedParentAcl = $cachedAclWithParent->getParentAcl());
+ $this->assertEquals($aclWithParent->getParentAcl()->getId(), $cachedParentAcl->getId());
+ }
+
+ protected function getAcl($depth = 0)
+ {
+ static $id = 1;
+
+ $acl = new Acl($id, new ObjectIdentity($id, 'foo'), $this->getPermissionGrantingStrategy(), array(), $depth > 0);
+
+ // insert some ACEs
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+ $acl->insertClassAce($sid, 1);
+ $acl->insertClassFieldAce('foo', $sid, 1);
+ $acl->insertObjectAce($sid, 1);
+ $acl->insertObjectFieldAce('foo', $sid, 1);
+ $id++;
+
+ if ($depth > 0) {
+ $acl->setParentAcl($this->getAcl($depth - 1));
+ }
+
+ return $acl;
+ }
+
+ protected function getPermissionGrantingStrategy()
+ {
+ if (null === $this->permissionGrantingStrategy) {
+ $this->permissionGrantingStrategy = new PermissionGrantingStrategy();
+ }
+
+ return $this->permissionGrantingStrategy;
+ }
+
+ protected function getCache($cacheDriver = null, $prefix = DoctrineAclCache::PREFIX)
+ {
+ if (null === $cacheDriver) {
+ $cacheDriver = new ArrayCache();
+ }
+
+ return new DoctrineAclCache($cacheDriver, $this->getPermissionGrantingStrategy(), $prefix);
+ }
+}
diff --git a/Acl/Tests/Domain/EntryTest.php b/Acl/Tests/Domain/EntryTest.php
new file mode 100644
index 0000000..6a2aac0
--- /dev/null
+++ b/Acl/Tests/Domain/EntryTest.php
@@ -0,0 +1,119 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\Entry;
+
+class EntryTest extends \PHPUnit_Framework_TestCase
+{
+ public function testConstructor()
+ {
+ $ace = $this->getAce($acl = $this->getAcl(), $sid = $this->getSid());
+
+ $this->assertEquals(123, $ace->getId());
+ $this->assertSame($acl, $ace->getAcl());
+ $this->assertSame($sid, $ace->getSecurityIdentity());
+ $this->assertEquals('foostrat', $ace->getStrategy());
+ $this->assertEquals(123456, $ace->getMask());
+ $this->assertTrue($ace->isGranting());
+ $this->assertTrue($ace->isAuditSuccess());
+ $this->assertFalse($ace->isAuditFailure());
+ }
+
+ public function testSetAuditSuccess()
+ {
+ $ace = $this->getAce();
+
+ $this->assertTrue($ace->isAuditSuccess());
+ $ace->setAuditSuccess(false);
+ $this->assertFalse($ace->isAuditSuccess());
+ $ace->setAuditsuccess(true);
+ $this->assertTrue($ace->isAuditSuccess());
+ }
+
+ public function testSetAuditFailure()
+ {
+ $ace = $this->getAce();
+
+ $this->assertFalse($ace->isAuditFailure());
+ $ace->setAuditFailure(true);
+ $this->assertTrue($ace->isAuditFailure());
+ $ace->setAuditFailure(false);
+ $this->assertFalse($ace->isAuditFailure());
+ }
+
+ public function testSetMask()
+ {
+ $ace = $this->getAce();
+
+ $this->assertEquals(123456, $ace->getMask());
+ $ace->setMask(4321);
+ $this->assertEquals(4321, $ace->getMask());
+ }
+
+ public function testSetStrategy()
+ {
+ $ace = $this->getAce();
+
+ $this->assertEquals('foostrat', $ace->getStrategy());
+ $ace->setStrategy('foo');
+ $this->assertEquals('foo', $ace->getStrategy());
+ }
+
+ public function testSerializeUnserialize()
+ {
+ $ace = $this->getAce();
+
+ $serialized = serialize($ace);
+ $uAce = unserialize($serialized);
+
+ $this->assertNull($uAce->getAcl());
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface', $uAce->getSecurityIdentity());
+ $this->assertEquals($ace->getId(), $uAce->getId());
+ $this->assertEquals($ace->getMask(), $uAce->getMask());
+ $this->assertEquals($ace->getStrategy(), $uAce->getStrategy());
+ $this->assertEquals($ace->isGranting(), $uAce->isGranting());
+ $this->assertEquals($ace->isAuditSuccess(), $uAce->isAuditSuccess());
+ $this->assertEquals($ace->isAuditFailure(), $uAce->isAuditFailure());
+ }
+
+ protected function getAce($acl = null, $sid = null)
+ {
+ if (null === $acl) {
+ $acl = $this->getAcl();
+ }
+ if (null === $sid) {
+ $sid = $this->getSid();
+ }
+
+ return new Entry(
+ 123,
+ $acl,
+ $sid,
+ 'foostrat',
+ 123456,
+ true,
+ false,
+ true
+ );
+ }
+
+ protected function getAcl()
+ {
+ return $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface');
+ }
+
+ protected function getSid()
+ {
+ return $this->getMock('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface');
+ }
+}
diff --git a/Acl/Tests/Domain/FieldEntryTest.php b/Acl/Tests/Domain/FieldEntryTest.php
new file mode 100644
index 0000000..735e2e8
--- /dev/null
+++ b/Acl/Tests/Domain/FieldEntryTest.php
@@ -0,0 +1,74 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\FieldEntry;
+
+class FieldEntryTest extends \PHPUnit_Framework_TestCase
+{
+ public function testConstructor()
+ {
+ $ace = $this->getAce();
+
+ $this->assertEquals('foo', $ace->getField());
+ }
+
+ public function testSerializeUnserialize()
+ {
+ $ace = $this->getAce();
+
+ $serialized = serialize($ace);
+ $uAce = unserialize($serialized);
+
+ $this->assertNull($uAce->getAcl());
+ $this->assertInstanceOf('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface', $uAce->getSecurityIdentity());
+ $this->assertEquals($ace->getId(), $uAce->getId());
+ $this->assertEquals($ace->getField(), $uAce->getField());
+ $this->assertEquals($ace->getMask(), $uAce->getMask());
+ $this->assertEquals($ace->getStrategy(), $uAce->getStrategy());
+ $this->assertEquals($ace->isGranting(), $uAce->isGranting());
+ $this->assertEquals($ace->isAuditSuccess(), $uAce->isAuditSuccess());
+ $this->assertEquals($ace->isAuditFailure(), $uAce->isAuditFailure());
+ }
+
+ protected function getAce($acl = null, $sid = null)
+ {
+ if (null === $acl) {
+ $acl = $this->getAcl();
+ }
+ if (null === $sid) {
+ $sid = $this->getSid();
+ }
+
+ return new FieldEntry(
+ 123,
+ $acl,
+ 'foo',
+ $sid,
+ 'foostrat',
+ 123456,
+ true,
+ false,
+ true
+ );
+ }
+
+ protected function getAcl()
+ {
+ return $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface');
+ }
+
+ protected function getSid()
+ {
+ return $this->getMock('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface');
+ }
+}
diff --git a/Acl/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php b/Acl/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php
new file mode 100644
index 0000000..59fc3bd
--- /dev/null
+++ b/Acl/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php
@@ -0,0 +1,41 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\ObjectIdentityRetrievalStrategy;
+
+class ObjectIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetObjectIdentityReturnsNullForInvalidDomainObject()
+ {
+ $strategy = new ObjectIdentityRetrievalStrategy();
+ $this->assertNull($strategy->getObjectIdentity('foo'));
+ }
+
+ public function testGetObjectIdentity()
+ {
+ $strategy = new ObjectIdentityRetrievalStrategy();
+ $domainObject = new DomainObject();
+ $objectIdentity = $strategy->getObjectIdentity($domainObject);
+
+ $this->assertEquals($domainObject->getId(), $objectIdentity->getIdentifier());
+ $this->assertEquals(get_class($domainObject), $objectIdentity->getType());
+ }
+}
+
+class DomainObject
+{
+ public function getId()
+ {
+ return 'foo';
+ }
+}
diff --git a/Acl/Tests/Domain/ObjectIdentityTest.php b/Acl/Tests/Domain/ObjectIdentityTest.php
new file mode 100644
index 0000000..4eab7b2
--- /dev/null
+++ b/Acl/Tests/Domain/ObjectIdentityTest.php
@@ -0,0 +1,109 @@
+<?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\Domain
+{
+ use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+
+ class ObjectIdentityTest extends \PHPUnit_Framework_TestCase
+ {
+ public function testConstructor()
+ {
+ $id = new ObjectIdentity('fooid', 'footype');
+
+ $this->assertEquals('fooid', $id->getIdentifier());
+ $this->assertEquals('footype', $id->getType());
+ }
+
+ // Test that constructor never changes passed type, even with proxies
+ public function testConstructorWithProxy()
+ {
+ $id = new ObjectIdentity('fooid', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject');
+
+ $this->assertEquals('fooid', $id->getIdentifier());
+ $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType());
+ }
+
+ public function testFromDomainObjectPrefersInterfaceOverGetId()
+ {
+ $domainObject = $this->getMock('Symfony\Component\Security\Acl\Model\DomainObjectInterface');
+ $domainObject
+ ->expects($this->once())
+ ->method('getObjectIdentifier')
+ ->will($this->returnValue('getObjectIdentifier()'))
+ ;
+ $domainObject
+ ->expects($this->never())
+ ->method('getId')
+ ->will($this->returnValue('getId()'))
+ ;
+
+ $id = ObjectIdentity::fromDomainObject($domainObject);
+ $this->assertEquals('getObjectIdentifier()', $id->getIdentifier());
+ }
+
+ public function testFromDomainObjectWithoutInterface()
+ {
+ $id = ObjectIdentity::fromDomainObject(new TestDomainObject());
+ $this->assertEquals('getId()', $id->getIdentifier());
+ $this->assertEquals('Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType());
+ }
+
+ public function testFromDomainObjectWithProxy()
+ {
+ $id = ObjectIdentity::fromDomainObject(new \Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject());
+ $this->assertEquals('getId()', $id->getIdentifier());
+ $this->assertEquals('Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType());
+ }
+
+ /**
+ * @dataProvider getCompareData
+ */
+ public function testEquals($oid1, $oid2, $equal)
+ {
+ if ($equal) {
+ $this->assertTrue($oid1->equals($oid2));
+ } else {
+ $this->assertFalse($oid1->equals($oid2));
+ }
+ }
+
+ public function getCompareData()
+ {
+ return array(
+ array(new ObjectIdentity('123', 'foo'), new ObjectIdentity('123', 'foo'), true),
+ array(new ObjectIdentity('123', 'foo'), new ObjectIdentity(123, 'foo'), true),
+ array(new ObjectIdentity('1', 'foo'), new ObjectIdentity('2', 'foo'), false),
+ array(new ObjectIdentity('1', 'bla'), new ObjectIdentity('1', 'blub'), false),
+ );
+ }
+ }
+
+ class TestDomainObject
+ {
+ public function getObjectIdentifier()
+ {
+ return 'getObjectIdentifier()';
+ }
+
+ public function getId()
+ {
+ return 'getId()';
+ }
+ }
+}
+
+namespace Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain
+{
+ class TestDomainObject extends \Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject
+ {
+ }
+}
diff --git a/Acl/Tests/Domain/PermissionGrantingStrategyTest.php b/Acl/Tests/Domain/PermissionGrantingStrategyTest.php
new file mode 100644
index 0000000..490d256
--- /dev/null
+++ b/Acl/Tests/Domain/PermissionGrantingStrategyTest.php
@@ -0,0 +1,185 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\Acl;
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
+use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
+
+class PermissionGrantingStrategyTest extends \PHPUnit_Framework_TestCase
+{
+ public function testIsGrantedObjectAcesHavePriority()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $acl = $this->getAcl($strategy);
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+
+ $acl->insertClassAce($sid, 1);
+ $acl->insertObjectAce($sid, 1, 0, false);
+ $this->assertFalse($strategy->isGranted($acl, array(1), array($sid)));
+ }
+
+ public function testIsGrantedFallsBackToClassAcesIfNoApplicableObjectAceWasFound()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $acl = $this->getAcl($strategy);
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+
+ $acl->insertClassAce($sid, 1);
+ $this->assertTrue($strategy->isGranted($acl, array(1), array($sid)));
+ }
+
+ public function testIsGrantedFavorsLocalAcesOverParentAclAces()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+
+ $acl = $this->getAcl($strategy);
+ $acl->insertClassAce($sid, 1);
+
+ $parentAcl = $this->getAcl($strategy);
+ $acl->setParentAcl($parentAcl);
+ $parentAcl->insertClassAce($sid, 1, 0, false);
+
+ $this->assertTrue($strategy->isGranted($acl, array(1), array($sid)));
+ }
+
+ public function testIsGrantedFallsBackToParentAcesIfNoLocalAcesAreApplicable()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+ $anotherSid = new UserSecurityIdentity('ROLE_USER', 'Foo');
+
+ $acl = $this->getAcl($strategy);
+ $acl->insertClassAce($anotherSid, 1, 0, false);
+
+ $parentAcl = $this->getAcl($strategy);
+ $acl->setParentAcl($parentAcl);
+ $parentAcl->insertClassAce($sid, 1);
+
+ $this->assertTrue($strategy->isGranted($acl, array(1), array($sid)));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Acl\Exception\NoAceFoundException
+ */
+ public function testIsGrantedReturnsExceptionIfNoAceIsFound()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $acl = $this->getAcl($strategy);
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+
+ $strategy->isGranted($acl, array(1), array($sid));
+ }
+
+ public function testIsGrantedFirstApplicableEntryMakesUltimateDecisionForPermissionIdentityCombination()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $acl = $this->getAcl($strategy);
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+ $aSid = new RoleSecurityIdentity('ROLE_USER');
+
+ $acl->insertClassAce($aSid, 1);
+ $acl->insertClassAce($sid, 1, 1, false);
+ $acl->insertClassAce($sid, 1, 2);
+ $this->assertFalse($strategy->isGranted($acl, array(1), array($sid, $aSid)));
+
+ $acl->insertObjectAce($sid, 1, 0, false);
+ $acl->insertObjectAce($aSid, 1, 1);
+ $this->assertFalse($strategy->isGranted($acl, array(1), array($sid, $aSid)));
+ }
+
+ public function testIsGrantedCallsAuditLoggerOnGrant()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $acl = $this->getAcl($strategy);
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+
+ $logger = $this->getMock('Symfony\Component\Security\Acl\Model\AuditLoggerInterface');
+ $logger
+ ->expects($this->once())
+ ->method('logIfNeeded')
+ ;
+ $strategy->setAuditLogger($logger);
+
+ $acl->insertObjectAce($sid, 1);
+ $acl->updateObjectAuditing(0, true, false);
+
+ $this->assertTrue($strategy->isGranted($acl, array(1), array($sid)));
+ }
+
+ public function testIsGrantedCallsAuditLoggerOnDeny()
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $acl = $this->getAcl($strategy);
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+
+ $logger = $this->getMock('Symfony\Component\Security\Acl\Model\AuditLoggerInterface');
+ $logger
+ ->expects($this->once())
+ ->method('logIfNeeded')
+ ;
+ $strategy->setAuditLogger($logger);
+
+ $acl->insertObjectAce($sid, 1, 0, false);
+ $acl->updateObjectAuditing(0, false, true);
+
+ $this->assertFalse($strategy->isGranted($acl, array(1), array($sid)));
+ }
+
+ /**
+ * @dataProvider getAllStrategyTests
+ */
+ public function testIsGrantedStrategies($maskStrategy, $aceMask, $requiredMask, $result)
+ {
+ $strategy = new PermissionGrantingStrategy();
+ $acl = $this->getAcl($strategy);
+ $sid = new UserSecurityIdentity('johannes', 'Foo');
+
+ $acl->insertObjectAce($sid, $aceMask, 0, true, $maskStrategy);
+
+ if (false === $result) {
+ try {
+ $strategy->isGranted($acl, array($requiredMask), array($sid));
+ $this->fail('The ACE is not supposed to match.');
+ } catch (NoAceFoundException $noAce) { }
+ } else {
+ $this->assertTrue($strategy->isGranted($acl, array($requiredMask), array($sid)));
+ }
+ }
+
+ public function getAllStrategyTests()
+ {
+ return array(
+ array('all', 1 << 0 | 1 << 1, 1 << 0, true),
+ array('all', 1 << 0 | 1 << 1, 1 << 2, false),
+ array('all', 1 << 0 | 1 << 10, 1 << 0 | 1 << 10, true),
+ array('all', 1 << 0 | 1 << 1, 1 << 0 | 1 << 1 || 1 << 2, false),
+ array('any', 1 << 0 | 1 << 1, 1 << 0, true),
+ array('any', 1 << 0 | 1 << 1, 1 << 0 | 1 << 2, true),
+ array('any', 1 << 0 | 1 << 1, 1 << 2, false),
+ array('equal', 1 << 0 | 1 << 1, 1 << 0, false),
+ array('equal', 1 << 0 | 1 << 1, 1 << 1, false),
+ array('equal', 1 << 0 | 1 << 1, 1 << 0 | 1 << 1, true),
+ );
+ }
+
+ protected function getAcl($strategy)
+ {
+ static $id = 1;
+
+ return new Acl($id++, new ObjectIdentity(1, 'Foo'), $strategy, array(), true);
+ }
+}
diff --git a/Acl/Tests/Domain/RoleSecurityIdentityTest.php b/Acl/Tests/Domain/RoleSecurityIdentityTest.php
new file mode 100644
index 0000000..ad5f236
--- /dev/null
+++ b/Acl/Tests/Domain/RoleSecurityIdentityTest.php
@@ -0,0 +1,55 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+use Symfony\Component\Security\Core\Role\Role;
+use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
+
+class RoleSecurityIdentityTest extends \PHPUnit_Framework_TestCase
+{
+ public function testConstructor()
+ {
+ $id = new RoleSecurityIdentity('ROLE_FOO');
+
+ $this->assertEquals('ROLE_FOO', $id->getRole());
+ }
+
+ public function testConstructorWithRoleInstance()
+ {
+ $id = new RoleSecurityIdentity(new Role('ROLE_FOO'));
+
+ $this->assertEquals('ROLE_FOO', $id->getRole());
+ }
+
+ /**
+ * @dataProvider getCompareData
+ */
+ public function testEquals($id1, $id2, $equal)
+ {
+ if ($equal) {
+ $this->assertTrue($id1->equals($id2));
+ } else {
+ $this->assertFalse($id1->equals($id2));
+ }
+ }
+
+ public function getCompareData()
+ {
+ return array(
+ array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity('ROLE_FOO'), true),
+ array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity(new Role('ROLE_FOO')), true),
+ array(new RoleSecurityIdentity('ROLE_USER'), new RoleSecurityIdentity('ROLE_FOO'), false),
+ array(new RoleSecurityIdentity('ROLE_FOO'), new UserSecurityIdentity('ROLE_FOO', 'Foo'), false),
+ );
+ }
+}
diff --git a/Acl/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php b/Acl/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php
new file mode 100644
index 0000000..02fbe67
--- /dev/null
+++ b/Acl/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php
@@ -0,0 +1,196 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\SecurityIdentityRetrievalStrategy;
+
+class SecurityIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider getSecurityIdentityRetrievalTests
+ */
+ public function testGetSecurityIdentities($user, array $roles, $authenticationStatus, array $sids)
+ {
+ $strategy = $this->getStrategy($roles, $authenticationStatus);
+
+ if ('anonymous' === $authenticationStatus) {
+ $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken')
+ ->disableOriginalConstructor()
+ ->getMock();
+ } else {
+ $class = '';
+ if (is_string($user)) {
+ $class = 'MyCustomTokenImpl';
+ }
+
+ $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')
+ ->setMockClassName($class)
+ ->getMock();
+ }
+ $token
+ ->expects($this->once())
+ ->method('getRoles')
+ ->will($this->returnValue(array('foo')))
+ ;
+ if ('anonymous' === $authenticationStatus) {
+ $token
+ ->expects($this->never())
+ ->method('getUser')
+ ;
+ } else {
+ $token
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($user))
+ ;
+ }
+
+ $extractedSids = $strategy->getSecurityIdentities($token);
+
+ foreach ($extractedSids as $index => $extractedSid) {
+ if (!isset($sids[$index])) {
+ $this->fail(sprintf('Expected SID at index %d, but there was none.', true));
+ }
+
+ if (false === $sids[$index]->equals($extractedSid)) {
+ $this->fail(sprintf('Index: %d, expected SID "%s", but got "%s".', $index, $sids[$index], $extractedSid));
+ }
+ }
+ }
+
+ public function getSecurityIdentityRetrievalTests()
+ {
+ return array(
+ array($this->getAccount('johannes', 'FooUser'), array('ROLE_USER', 'ROLE_SUPERADMIN'), 'fullFledged', array(
+ new UserSecurityIdentity('johannes', 'FooUser'),
+ new RoleSecurityIdentity('ROLE_USER'),
+ new RoleSecurityIdentity('ROLE_SUPERADMIN'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
+ )),
+ array('johannes', array('ROLE_FOO'), 'fullFledged', array(
+ new UserSecurityIdentity('johannes', 'MyCustomTokenImpl'),
+ new RoleSecurityIdentity('ROLE_FOO'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
+ )),
+ array(new CustomUserImpl('johannes'), array('ROLE_FOO'), 'fullFledged', array(
+ new UserSecurityIdentity('johannes', 'Symfony\Component\Security\Acl\Tests\Domain\CustomUserImpl'),
+ new RoleSecurityIdentity('ROLE_FOO'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
+ )),
+ array($this->getAccount('foo', 'FooBarUser'), array('ROLE_FOO'), 'rememberMe', array(
+ new UserSecurityIdentity('foo', 'FooBarUser'),
+ new RoleSecurityIdentity('ROLE_FOO'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
+ )),
+ array('guest', array('ROLE_FOO'), 'anonymous', array(
+ new RoleSecurityIdentity('ROLE_FOO'),
+ new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
+ ))
+ );
+ }
+
+ protected function getAccount($username, $class)
+ {
+ $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface', array(), array(), $class);
+ $account
+ ->expects($this->any())
+ ->method('getUsername')
+ ->will($this->returnValue($username))
+ ;
+
+ return $account;
+ }
+
+ protected function getStrategy(array $roles = array(), $authenticationStatus = 'fullFledged')
+ {
+ $roleHierarchy = $this->getMock('Symfony\Component\Security\Core\Role\RoleHierarchyInterface');
+ $roleHierarchy
+ ->expects($this->once())
+ ->method('getReachableRoles')
+ ->with($this->equalTo(array('foo')))
+ ->will($this->returnValue($roles))
+ ;
+
+ $trustResolver = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver', array(), array('', ''));
+
+ $trustResolver
+ ->expects($this->at(0))
+ ->method('isAnonymous')
+ ->will($this->returnValue('anonymous' === $authenticationStatus))
+ ;
+
+ if ('fullFledged' === $authenticationStatus) {
+ $trustResolver
+ ->expects($this->once())
+ ->method('isFullFledged')
+ ->will($this->returnValue(true))
+ ;
+ $trustResolver
+ ->expects($this->never())
+ ->method('isRememberMe')
+ ;
+ } elseif ('rememberMe' === $authenticationStatus) {
+ $trustResolver
+ ->expects($this->once())
+ ->method('isFullFledged')
+ ->will($this->returnValue(false))
+ ;
+ $trustResolver
+ ->expects($this->once())
+ ->method('isRememberMe')
+ ->will($this->returnValue(true))
+ ;
+ } else {
+ $trustResolver
+ ->expects($this->at(1))
+ ->method('isAnonymous')
+ ->will($this->returnValue(true))
+ ;
+ $trustResolver
+ ->expects($this->once())
+ ->method('isFullFledged')
+ ->will($this->returnValue(false))
+ ;
+ $trustResolver
+ ->expects($this->once())
+ ->method('isRememberMe')
+ ->will($this->returnValue(false))
+ ;
+ }
+
+ return new SecurityIdentityRetrievalStrategy($roleHierarchy, $trustResolver);
+ }
+}
+
+class CustomUserImpl
+{
+ protected $name;
+
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function __toString()
+ {
+ return $this->name;
+ }
+}
diff --git a/Acl/Tests/Domain/UserSecurityIdentityTest.php b/Acl/Tests/Domain/UserSecurityIdentityTest.php
new file mode 100644
index 0000000..09d3f0d
--- /dev/null
+++ b/Acl/Tests/Domain/UserSecurityIdentityTest.php
@@ -0,0 +1,73 @@
+<?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\Domain;
+
+use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+
+class UserSecurityIdentityTest extends \PHPUnit_Framework_TestCase
+{
+ public function testConstructor()
+ {
+ $id = new UserSecurityIdentity('foo', 'Foo');
+
+ $this->assertEquals('foo', $id->getUsername());
+ $this->assertEquals('Foo', $id->getClass());
+ }
+
+ // Test that constructor never changes the type, even for proxies
+ public function testConstructorWithProxy()
+ {
+ $id = new UserSecurityIdentity('foo', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\Foo');
+
+ $this->assertEquals('foo', $id->getUsername());
+ $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\Foo', $id->getClass());
+ }
+
+ /**
+ * @dataProvider getCompareData
+ */
+ public function testEquals($id1, $id2, $equal)
+ {
+ $this->assertSame($equal, $id1->equals($id2));
+ }
+
+ public function getCompareData()
+ {
+ $account = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')
+ ->setMockClassName('USI_AccountImpl')
+ ->getMock();
+ $account
+ ->expects($this->any())
+ ->method('getUsername')
+ ->will($this->returnValue('foo'))
+ ;
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($account))
+ ;
+
+ return array(
+ array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('foo', 'Foo'), true),
+ array(new UserSecurityIdentity('foo', 'Bar'), new UserSecurityIdentity('foo', 'Foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('bar', 'Foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromAccount($account), false),
+ array(new UserSecurityIdentity('bla', 'Foo'), new UserSecurityIdentity('blub', 'Foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), new RoleSecurityIdentity('foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromToken($token), false),
+ array(new UserSecurityIdentity('foo', 'USI_AccountImpl'), UserSecurityIdentity::fromToken($token), true),
+ );
+ }
+}
diff --git a/Acl/Tests/Permission/BasicPermissionMapTest.php b/Acl/Tests/Permission/BasicPermissionMapTest.php
new file mode 100644
index 0000000..2afe588
--- /dev/null
+++ b/Acl/Tests/Permission/BasicPermissionMapTest.php
@@ -0,0 +1,23 @@
+<?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\Permission;
+
+use Symfony\Component\Security\Acl\Permission\BasicPermissionMap;
+
+class BasicPermissionMapTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetMasksReturnsNullWhenNotSupportedMask()
+ {
+ $map = new BasicPermissionMap();
+ $this->assertNull($map->getMasks('IS_AUTHENTICATED_REMEMBERED', null));
+ }
+}
diff --git a/Acl/Tests/Permission/MaskBuilderTest.php b/Acl/Tests/Permission/MaskBuilderTest.php
new file mode 100644
index 0000000..8245669
--- /dev/null
+++ b/Acl/Tests/Permission/MaskBuilderTest.php
@@ -0,0 +1,103 @@
+<?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\Permission;
+
+use Symfony\Component\Security\Acl\Permission\MaskBuilder;
+
+class MaskBuilderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \InvalidArgumentException
+ * @dataProvider getInvalidConstructorData
+ */
+ public function testConstructorWithNonInteger($invalidMask)
+ {
+ new MaskBuilder($invalidMask);
+ }
+
+ public function getInvalidConstructorData()
+ {
+ return array(
+ array(234.463),
+ array('asdgasdf'),
+ array(array()),
+ array(new \stdClass()),
+ );
+ }
+
+ public function testConstructorWithoutArguments()
+ {
+ $builder = new MaskBuilder();
+
+ $this->assertEquals(0, $builder->get());
+ }
+
+ public function testConstructor()
+ {
+ $builder = new MaskBuilder(123456);
+
+ $this->assertEquals(123456, $builder->get());
+ }
+
+ public function testAddAndRemove()
+ {
+ $builder = new MaskBuilder();
+
+ $builder
+ ->add('view')
+ ->add('eDiT')
+ ->add('ownEr')
+ ;
+ $mask = $builder->get();
+
+ $this->assertEquals(MaskBuilder::MASK_VIEW, $mask & MaskBuilder::MASK_VIEW);
+ $this->assertEquals(MaskBuilder::MASK_EDIT, $mask & MaskBuilder::MASK_EDIT);
+ $this->assertEquals(MaskBuilder::MASK_OWNER, $mask & MaskBuilder::MASK_OWNER);
+ $this->assertEquals(0, $mask & MaskBuilder::MASK_MASTER);
+ $this->assertEquals(0, $mask & MaskBuilder::MASK_CREATE);
+ $this->assertEquals(0, $mask & MaskBuilder::MASK_DELETE);
+ $this->assertEquals(0, $mask & MaskBuilder::MASK_UNDELETE);
+
+ $builder->remove('edit')->remove('OWner');
+ $mask = $builder->get();
+ $this->assertEquals(0, $mask & MaskBuilder::MASK_EDIT);
+ $this->assertEquals(0, $mask & MaskBuilder::MASK_OWNER);
+ $this->assertEquals(MaskBuilder::MASK_VIEW, $mask & MaskBuilder::MASK_VIEW);
+ }
+
+ public function testGetPattern()
+ {
+ $builder = new MaskBuilder();
+ $this->assertEquals(MaskBuilder::ALL_OFF, $builder->getPattern());
+
+ $builder->add('view');
+ $this->assertEquals(str_repeat('.', 31).'V', $builder->getPattern());
+
+ $builder->add('owner');
+ $this->assertEquals(str_repeat('.', 24).'N......V', $builder->getPattern());
+
+ $builder->add(1 << 10);
+ $this->assertEquals(str_repeat('.', 21).MaskBuilder::ON.'..N......V', $builder->getPattern());
+ }
+
+ public function testReset()
+ {
+ $builder = new MaskBuilder();
+ $this->assertEquals(0, $builder->get());
+
+ $builder->add('view');
+ $this->assertTrue($builder->get() > 0);
+
+ $builder->reset();
+ $this->assertEquals(0, $builder->get());
+ }
+}
diff --git a/Acl/Tests/Voter/AclVoterTest.php b/Acl/Tests/Voter/AclVoterTest.php
new file mode 100644
index 0000000..6bec231
--- /dev/null
+++ b/Acl/Tests/Voter/AclVoterTest.php
@@ -0,0 +1,405 @@
+<?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\Voter;
+
+use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
+use Symfony\Component\Security\Acl\Voter\FieldVote;
+use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
+use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
+use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
+use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
+use Symfony\Component\Security\Acl\Voter\AclVoter;
+
+class AclVoterTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider getSupportsAttributeTests
+ */
+ public function testSupportsAttribute($attribute, $supported)
+ {
+ list($voter,, $permissionMap,,) = $this->getVoter();
+
+ $permissionMap
+ ->expects($this->once())
+ ->method('contains')
+ ->with($this->identicalTo($attribute))
+ ->will($this->returnValue($supported))
+ ;
+
+ $this->assertSame($supported, $voter->supportsAttribute($attribute));
+ }
+
+ public function getSupportsAttributeTests()
+ {
+ return array(
+ array('foo', true),
+ array('foo', false),
+ );
+ }
+
+ /**
+ * @dataProvider getSupportsClassTests
+ */
+ public function testSupportsClass($class)
+ {
+ list($voter,,,,) = $this->getVoter();
+
+ $this->assertTrue($voter->supportsClass($class));
+ }
+
+ public function getSupportsClassTests()
+ {
+ return array(
+ array('foo'),
+ array('bar'),
+ array('moo'),
+ );
+ }
+
+ public function testVote()
+ {
+ list($voter,, $permissionMap,,) = $this->getVoter();
+ $permissionMap
+ ->expects($this->atLeastOnce())
+ ->method('getMasks')
+ ->will($this->returnValue(null))
+ ;
+
+ $this->assertSame(VoterInterface::ACCESS_ABSTAIN, $voter->vote($this->getToken(), null, array('VIEW', 'EDIT', 'DELETE')));
+ }
+
+ /**
+ * @dataProvider getTrueFalseTests
+ */
+ public function testVoteWhenNoObjectIsPassed($allowIfObjectIdentityUnavailable)
+ {
+ list($voter,, $permissionMap,,) = $this->getVoter($allowIfObjectIdentityUnavailable);
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->will($this->returnValue(array()))
+ ;
+
+ if ($allowIfObjectIdentityUnavailable) {
+ $vote = VoterInterface::ACCESS_GRANTED;
+ } else {
+ $vote = VoterInterface::ACCESS_ABSTAIN;
+ }
+
+ $this->assertSame($vote, $voter->vote($this->getToken(), null, array('VIEW')));
+ }
+
+ /**
+ * @dataProvider getTrueFalseTests
+ */
+ public function testVoteWhenOidStrategyReturnsNull($allowIfUnavailable)
+ {
+ list($voter,, $permissionMap, $oidStrategy,) = $this->getVoter($allowIfUnavailable);
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->will($this->returnValue(array()))
+ ;
+
+ $oidStrategy
+ ->expects($this->once())
+ ->method('getObjectIdentity')
+ ->will($this->returnValue(null))
+ ;
+
+ if ($allowIfUnavailable) {
+ $vote = VoterInterface::ACCESS_GRANTED;
+ } else {
+ $vote = VoterInterface::ACCESS_ABSTAIN;
+ }
+
+ $this->assertSame($vote, $voter->vote($this->getToken(), new \stdClass(), array('VIEW')));
+ }
+
+ public function getTrueFalseTests()
+ {
+ return array(array(true), array(false));
+ }
+
+ public function testVoteNoAclFound()
+ {
+ list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter();
+
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->will($this->returnValue(array()))
+ ;
+
+ $oidStrategy
+ ->expects($this->once())
+ ->method('getObjectIdentity')
+ ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo')))
+ ;
+
+ $sidStrategy
+ ->expects($this->once())
+ ->method('getSecurityIdentities')
+ ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO'))))
+ ;
+
+ $provider
+ ->expects($this->once())
+ ->method('findAcl')
+ ->with($this->equalTo($oid), $this->equalTo($sids))
+ ->will($this->throwException(new AclNotFoundException('Not found.')))
+ ;
+
+ $this->assertSame(VoterInterface::ACCESS_DENIED, $voter->vote($this->getToken(), new \stdClass(), array('VIEW')));
+ }
+
+ /**
+ * @dataProvider getTrueFalseTests
+ */
+ public function testVoteGrantsAccess($grant)
+ {
+ list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter();
+
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->with($this->equalTo('VIEW'))
+ ->will($this->returnValue($masks = array(1, 2, 3)))
+ ;
+
+ $oidStrategy
+ ->expects($this->once())
+ ->method('getObjectIdentity')
+ ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo')))
+ ;
+
+ $sidStrategy
+ ->expects($this->once())
+ ->method('getSecurityIdentities')
+ ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO'))))
+ ;
+
+ $provider
+ ->expects($this->once())
+ ->method('findAcl')
+ ->with($this->equalTo($oid), $this->equalTo($sids))
+ ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface')))
+ ;
+
+ $acl
+ ->expects($this->once())
+ ->method('isGranted')
+ ->with($this->identicalTo($masks), $this->equalTo($sids), $this->isFalse())
+ ->will($this->returnValue($grant))
+ ;
+
+ if ($grant) {
+ $vote = VoterInterface::ACCESS_GRANTED;
+ } else {
+ $vote = VoterInterface::ACCESS_DENIED;
+ }
+
+ $this->assertSame($vote, $voter->vote($this->getToken(), new \stdClass(), array('VIEW')));
+ }
+
+ public function testVoteNoAceFound()
+ {
+ list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter();
+
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->with($this->equalTo('VIEW'))
+ ->will($this->returnValue($masks = array(1, 2, 3)))
+ ;
+
+ $oidStrategy
+ ->expects($this->once())
+ ->method('getObjectIdentity')
+ ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo')))
+ ;
+
+ $sidStrategy
+ ->expects($this->once())
+ ->method('getSecurityIdentities')
+ ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO'))))
+ ;
+
+ $provider
+ ->expects($this->once())
+ ->method('findAcl')
+ ->with($this->equalTo($oid), $this->equalTo($sids))
+ ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface')))
+ ;
+
+ $acl
+ ->expects($this->once())
+ ->method('isGranted')
+ ->with($this->identicalTo($masks), $this->equalTo($sids), $this->isFalse())
+ ->will($this->throwException(new NoAceFoundException('No ACE')))
+ ;
+
+ $this->assertSame(VoterInterface::ACCESS_DENIED, $voter->vote($this->getToken(), new \stdClass(), array('VIEW')));
+ }
+
+ /**
+ * @dataProvider getTrueFalseTests
+ */
+ public function testVoteGrantsFieldAccess($grant)
+ {
+ list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter();
+
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->with($this->equalTo('VIEW'))
+ ->will($this->returnValue($masks = array(1, 2, 3)))
+ ;
+
+ $oidStrategy
+ ->expects($this->once())
+ ->method('getObjectIdentity')
+ ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo')))
+ ;
+
+ $sidStrategy
+ ->expects($this->once())
+ ->method('getSecurityIdentities')
+ ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO'))))
+ ;
+
+ $provider
+ ->expects($this->once())
+ ->method('findAcl')
+ ->with($this->equalTo($oid), $this->equalTo($sids))
+ ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface')))
+ ;
+
+ $acl
+ ->expects($this->once())
+ ->method('isFieldGranted')
+ ->with($this->identicalTo('foo'), $this->identicalTo($masks), $this->equalTo($sids), $this->isFalse())
+ ->will($this->returnValue($grant))
+ ;
+
+ if ($grant) {
+ $vote = VoterInterface::ACCESS_GRANTED;
+ } else {
+ $vote = VoterInterface::ACCESS_DENIED;
+ }
+
+ $this->assertSame($vote, $voter->vote($this->getToken(), new FieldVote(new \stdClass(), 'foo'), array('VIEW')));
+ }
+
+ public function testVoteNoFieldAceFound()
+ {
+ list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter();
+
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->with($this->equalTo('VIEW'))
+ ->will($this->returnValue($masks = array(1, 2, 3)))
+ ;
+
+ $oidStrategy
+ ->expects($this->once())
+ ->method('getObjectIdentity')
+ ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo')))
+ ;
+
+ $sidStrategy
+ ->expects($this->once())
+ ->method('getSecurityIdentities')
+ ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO'))))
+ ;
+
+ $provider
+ ->expects($this->once())
+ ->method('findAcl')
+ ->with($this->equalTo($oid), $this->equalTo($sids))
+ ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface')))
+ ;
+
+ $acl
+ ->expects($this->once())
+ ->method('isFieldGranted')
+ ->with($this->identicalTo('foo'), $this->identicalTo($masks), $this->equalTo($sids), $this->isFalse())
+ ->will($this->throwException(new NoAceFoundException('No ACE')))
+ ;
+
+ $this->assertSame(VoterInterface::ACCESS_DENIED, $voter->vote($this->getToken(), new FieldVote(new \stdClass(), 'foo'), array('VIEW')));
+ }
+
+ public function testWhenReceivingAnObjectIdentityInterfaceWeDontRetrieveANewObjectIdentity()
+ {
+ list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter();
+
+ $oid = new ObjectIdentity('someID','someType');
+
+ $permissionMap
+ ->expects($this->once())
+ ->method('getMasks')
+ ->with($this->equalTo('VIEW'))
+ ->will($this->returnValue($masks = array(1, 2, 3)))
+ ;
+
+ $oidStrategy
+ ->expects($this->never())
+ ->method('getObjectIdentity')
+ ;
+
+ $sidStrategy
+ ->expects($this->once())
+ ->method('getSecurityIdentities')
+ ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO'))))
+ ;
+
+ $provider
+ ->expects($this->once())
+ ->method('findAcl')
+ ->with($this->equalTo($oid), $this->equalTo($sids))
+ ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface')))
+ ;
+
+ $acl
+ ->expects($this->once())
+ ->method('isGranted')
+ ->with($this->identicalTo($masks), $this->equalTo($sids), $this->isFalse())
+ ->will($this->throwException(new NoAceFoundException('No ACE')))
+ ;
+
+ $voter->vote($this->getToken(), $oid, array('VIEW'));
+ }
+
+ protected function getToken()
+ {
+ return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ }
+
+ protected function getVoter($allowIfObjectIdentityUnavailable = true)
+ {
+ $provider = $this->getMock('Symfony\Component\Security\Acl\Model\AclProviderInterface');
+ $permissionMap = $this->getMock('Symfony\Component\Security\Acl\Permission\PermissionMapInterface');
+ $oidStrategy = $this->getMock('Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface');
+ $sidStrategy = $this->getMock('Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface');
+
+ return array(
+ new AclVoter($provider, $oidStrategy, $sidStrategy, $permissionMap, null, $allowIfObjectIdentityUnavailable),
+ $provider,
+ $permissionMap,
+ $oidStrategy,
+ $sidStrategy,
+ );
+ }
+}
diff --git a/Acl/composer.json b/Acl/composer.json
new file mode 100644
index 0000000..0e68d9e
--- /dev/null
+++ b/Acl/composer.json
@@ -0,0 +1,42 @@
+{
+ "name": "symfony/security-acl",
+ "type": "library",
+ "description": "Symfony Security Component - ACL (Access Control List)",
+ "keywords": [],
+ "homepage": "http://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3",
+ "symfony/security-core": "~2.4"
+ },
+ "require-dev": {
+ "doctrine/common": "~2.2",
+ "doctrine/dbal": "~2.2",
+ "psr/log": "~1.0"
+ },
+ "suggest": {
+ "symfony/class-loader": "For using the ACL generateSql script",
+ "symfony/finder": "For using the ACL generateSql script",
+ "doctrine/dbal": "For using the built-in ACL implementation"
+ },
+ "autoload": {
+ "psr-0": { "Symfony\\Component\\Security\\Acl\\": "" }
+ },
+ "target-dir": "Symfony/Component/Security/Acl",
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.4-dev"
+ }
+ }
+}
diff --git a/Acl/phpunit.xml.dist b/Acl/phpunit.xml.dist
new file mode 100644
index 0000000..6520948
--- /dev/null
+++ b/Acl/phpunit.xml.dist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ processIsolation="false"
+ stopOnFailure="false"
+ syntaxCheck="false"
+ bootstrap="vendor/autoload.php"
+>
+ <testsuites>
+ <testsuite name="Symfony Security Component ACL Test Suite">
+ <directory>./Tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./</directory>
+ <exclude>
+ <directory>./vendor</directory>
+ <directory>./Tests</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>