diff options
author | Fabien Potencier <fabien.potencier@gmail.com> | 2012-03-28 15:43:52 +0200 |
---|---|---|
committer | Fabien Potencier <fabien.potencier@gmail.com> | 2012-03-29 08:37:22 +0200 |
commit | 21456eff7d574878255b8944594096bb56055e21 (patch) | |
tree | c57d4e85c78288e45dc748dd71e1a939eadf42f4 | |
parent | 57d74022db3d8c8a19330dad1c34d5d52d325184 (diff) | |
download | symfony-security-21456eff7d574878255b8944594096bb56055e21.zip symfony-security-21456eff7d574878255b8944594096bb56055e21.tar.gz symfony-security-21456eff7d574878255b8944594096bb56055e21.tar.bz2 |
moved component and bridge unit tests to the src/ directory
This is the first step to make each Symfony Component and Bridge self-contained.
69 files changed, 8774 insertions, 4 deletions
@@ -9,10 +9,25 @@ the Java Spring framework. Resources --------- -Unit tests: - -https://github.com/symfony/symfony/tree/master/tests/Symfony/Tests/Component/Security - Documentation: http://symfony.com/doc/2.0/book/security.html + +Resources +--------- + +You can run the unit tests with the following command: + + phpunit -c src/Symfony/Component/Security/ + +If you also want to run the unit tests that depend on other Symfony +Components, declare the following environment variables before running +PHPUnit: + + export SYMFONY_HTTP_FOUNDATION=../path/to/HttpFoundation + export SYMFONY_HTTP_KERNEL=../path/to/HttpKernel + export SYMFONY_EVENT_DISPATCHER=../path/to/EventDispatcher + export SYMFONY_FORM=../path/to/Form + export SYMFONY_ROUTING=../path/to/Routing + export DOCTRINE_DBAL=../path/to/doctrine-dbal + export DOCTRINE_COMMON=../path/to/doctrine-common diff --git a/Tests/Acl/Dbal/AclProviderBenchmarkTest.php b/Tests/Acl/Dbal/AclProviderBenchmarkTest.php new file mode 100644 index 0000000..66c74e6 --- /dev/null +++ b/Tests/Acl/Dbal/AclProviderBenchmarkTest.php @@ -0,0 +1,272 @@ +<?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\Tests\Acl\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() + { + if (!class_exists('Doctrine\DBAL\DriverManager')) { + $this->markTestSkipped('The "Doctrine DBAL" library is not available'); + } + + 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/Tests/Acl/Dbal/AclProviderTest.php b/Tests/Acl/Dbal/AclProviderTest.php new file mode 100644 index 0000000..e03edc0 --- /dev/null +++ b/Tests/Acl/Dbal/AclProviderTest.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\Tests\Acl\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 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('Doctrine\DBAL\DriverManager')) { + $this->markTestSkipped('The Doctrine2 DBAL is required for this test'); + } + 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/Tests/Acl/Dbal/MutableAclProviderTest.php b/Tests/Acl/Dbal/MutableAclProviderTest.php new file mode 100644 index 0000000..df6a1cf --- /dev/null +++ b/Tests/Acl/Dbal/MutableAclProviderTest.php @@ -0,0 +1,492 @@ +<?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\Tests\Acl\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 testUpdateAclThrowsExceptionOnConcurrentModifcationOfSharedProperties() + { + $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()); + } + + /** + * 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('Doctrine\DBAL\DriverManager')) { + $this->markTestSkipped('The Doctrine2 DBAL is required for this test'); + } + 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/Tests/Acl/Domain/AclTest.php b/Tests/Acl/Domain/AclTest.php new file mode 100644 index 0000000..b6f3d79 --- /dev/null +++ b/Tests/Acl/Domain/AclTest.php @@ -0,0 +1,521 @@ +<?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\Tests\Acl\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); + } + + protected function setUp() + { + if (!class_exists('Doctrine\DBAL\DriverManager')) { + $this->markTestSkipped('The Doctrine2 DBAL is required for this test'); + } + } +} diff --git a/Tests/Acl/Domain/AuditLoggerTest.php b/Tests/Acl/Domain/AuditLoggerTest.php new file mode 100644 index 0000000..a0f38eb --- /dev/null +++ b/Tests/Acl/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\Tests\Acl\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/Tests/Acl/Domain/DoctrineAclCacheTest.php b/Tests/Acl/Domain/DoctrineAclCacheTest.php new file mode 100644 index 0000000..99eb27e --- /dev/null +++ b/Tests/Acl/Domain/DoctrineAclCacheTest.php @@ -0,0 +1,108 @@ +<?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\Tests\Acl\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); + } + + protected function setUp() + { + if (!class_exists('Doctrine\DBAL\DriverManager')) { + $this->markTestSkipped('The Doctrine2 DBAL is required for this test'); + } + } +} diff --git a/Tests/Acl/Domain/EntryTest.php b/Tests/Acl/Domain/EntryTest.php new file mode 100644 index 0000000..88dd89e --- /dev/null +++ b/Tests/Acl/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\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/Tests/Acl/Domain/FieldEntryTest.php b/Tests/Acl/Domain/FieldEntryTest.php new file mode 100644 index 0000000..7f0cbc0 --- /dev/null +++ b/Tests/Acl/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\Tests\Acl\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/Tests/Acl/Domain/ObjectIdentityRetrievalStrategyTest.php b/Tests/Acl/Domain/ObjectIdentityRetrievalStrategyTest.php new file mode 100644 index 0000000..e89e1ef --- /dev/null +++ b/Tests/Acl/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\Tests\Acl\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/Tests/Acl/Domain/ObjectIdentityTest.php b/Tests/Acl/Domain/ObjectIdentityTest.php new file mode 100644 index 0000000..20dbedf --- /dev/null +++ b/Tests/Acl/Domain/ObjectIdentityTest.php @@ -0,0 +1,91 @@ +<?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\Tests\Acl\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()); + } + + 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()); + } + + /** + * @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), + ); + } + + protected function setUp() + { + if (!class_exists('Doctrine\DBAL\DriverManager')) { + $this->markTestSkipped('The Doctrine2 DBAL is required for this test'); + } + } +} + +class TestDomainObject +{ + public function getObjectIdentifier() + { + return 'getObjectIdentifier()'; + } + + public function getId() + { + return 'getId()'; + } +} diff --git a/Tests/Acl/Domain/PermissionGrantingStrategyTest.php b/Tests/Acl/Domain/PermissionGrantingStrategyTest.php new file mode 100644 index 0000000..67c9f3f --- /dev/null +++ b/Tests/Acl/Domain/PermissionGrantingStrategyTest.php @@ -0,0 +1,192 @@ +<?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\Tests\Acl\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); + } + + protected function setUp() + { + if (!class_exists('Doctrine\DBAL\DriverManager')) { + $this->markTestSkipped('The Doctrine2 DBAL is required for this test'); + } + } +} diff --git a/Tests/Acl/Domain/RoleSecurityIdentityTest.php b/Tests/Acl/Domain/RoleSecurityIdentityTest.php new file mode 100644 index 0000000..341f33c --- /dev/null +++ b/Tests/Acl/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\Tests\Acl\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/Tests/Acl/Domain/SecurityIdentityRetrievalStrategyTest.php b/Tests/Acl/Domain/SecurityIdentityRetrievalStrategyTest.php new file mode 100644 index 0000000..2083728 --- /dev/null +++ b/Tests/Acl/Domain/SecurityIdentityRetrievalStrategyTest.php @@ -0,0 +1,198 @@ +<?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\Tests\Acl\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\Tests\Acl\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/Tests/Acl/Domain/UserSecurityIdentityTest.php b/Tests/Acl/Domain/UserSecurityIdentityTest.php new file mode 100644 index 0000000..bb98fd0 --- /dev/null +++ b/Tests/Acl/Domain/UserSecurityIdentityTest.php @@ -0,0 +1,64 @@ +<?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\Tests\Acl\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()); + } + + /** + * @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/Tests/Acl/Permission/BasicPermissionMapTest.php b/Tests/Acl/Permission/BasicPermissionMapTest.php new file mode 100644 index 0000000..66ab97f --- /dev/null +++ b/Tests/Acl/Permission/BasicPermissionMapTest.php @@ -0,0 +1,23 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Acl\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/Tests/Acl/Permission/MaskBuilderTest.php b/Tests/Acl/Permission/MaskBuilderTest.php new file mode 100644 index 0000000..848a6f2 --- /dev/null +++ b/Tests/Acl/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\Tests\Acl\Util; + +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/Tests/Acl/Voter/AclVoterTest.php b/Tests/Acl/Voter/AclVoterTest.php new file mode 100644 index 0000000..2474515 --- /dev/null +++ b/Tests/Acl/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\Tests\Acl\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/Tests/Core/Authentication/AuthenticationProviderManagerTest.php b/Tests/Core/Authentication/AuthenticationProviderManagerTest.php new file mode 100644 index 0000000..c57967b --- /dev/null +++ b/Tests/Core/Authentication/AuthenticationProviderManagerTest.php @@ -0,0 +1,138 @@ +<?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\Tests\Core\Authentication; + +use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; +use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\AccountStatusException; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; + +class AuthenticationProviderManagerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException InvalidArgumentException + */ + public function testAuthenticateWithoutProviders() + { + new AuthenticationProviderManager(array()); + } + + public function testAuthenticateWhenNoProviderSupportsToken() + { + $manager = new AuthenticationProviderManager(array( + $this->getAuthenticationProvider(false), + )); + + try { + $manager->authenticate($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->fail(); + } catch (ProviderNotFoundException $e) { + $this->assertSame($token, $e->getExtraInformation()); + } + } + + public function testAuthenticateWhenProviderReturnsAccountStatusException() + { + $manager = new AuthenticationProviderManager(array( + $this->getAuthenticationProvider(true, null, 'Symfony\Component\Security\Core\Exception\AccountStatusException'), + )); + + try { + $manager->authenticate($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->fail(); + } catch (AccountStatusException $e) { + $this->assertSame($token, $e->getExtraInformation()); + } + } + + public function testAuthenticateWhenProviderReturnsAuthenticationException() + { + $manager = new AuthenticationProviderManager(array( + $this->getAuthenticationProvider(true, null, 'Symfony\Component\Security\Core\Exception\AuthenticationException'), + )); + + try { + $manager->authenticate($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->fail(); + } catch (AuthenticationException $e) { + $this->assertSame($token, $e->getExtraInformation()); + } + } + + public function testAuthenticateWhenOneReturnsAuthenticationExceptionButNotAll() + { + $manager = new AuthenticationProviderManager(array( + $this->getAuthenticationProvider(true, null, 'Symfony\Component\Security\Core\Exception\AuthenticationException'), + $this->getAuthenticationProvider(true, $expected = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')), + )); + + $token = $manager->authenticate($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->assertSame($expected, $token); + } + + public function testAuthenticateReturnsTokenOfTheFirstMatchingProvider() + { + $second = $this->getMock('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface'); + $second + ->expects($this->never()) + ->method('supports') + ; + $manager = new AuthenticationProviderManager(array( + $this->getAuthenticationProvider(true, $expected = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')), + $second, + )); + + $token = $manager->authenticate($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->assertSame($expected, $token); + } + + public function testEraseCredentialFlag() + { + $manager = new AuthenticationProviderManager(array( + $this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar', 'key')), + )); + + $token = $manager->authenticate($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->assertEquals('', $token->getCredentials()); + + $manager = new AuthenticationProviderManager(array( + $this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar', 'key')), + ), false); + + $token = $manager->authenticate($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->assertEquals('bar', $token->getCredentials()); + } + + protected function getAuthenticationProvider($supports, $token = null, $exception = null) + { + $provider = $this->getMock('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface'); + $provider->expects($this->once()) + ->method('supports') + ->will($this->returnValue($supports)) + ; + + if (null !== $token) { + $provider->expects($this->once()) + ->method('authenticate') + ->will($this->returnValue($token)) + ; + } elseif (null !== $exception) { + $provider->expects($this->once()) + ->method('authenticate') + ->will($this->throwException($this->getMock($exception, null, array(), '', false))) + ; + } + + return $provider; + } +} diff --git a/Tests/Core/Authentication/AuthenticationTrustResolverTest.php b/Tests/Core/Authentication/AuthenticationTrustResolverTest.php new file mode 100644 index 0000000..c3b1585 --- /dev/null +++ b/Tests/Core/Authentication/AuthenticationTrustResolverTest.php @@ -0,0 +1,72 @@ +<?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\Tests\Core\Authentication; + +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; + +class AuthenticationTrustResolverTest extends \PHPUnit_Framework_TestCase +{ + public function testIsAnonymous() + { + $resolver = $this->getResolver(); + + $this->assertFalse($resolver->isAnonymous(null)); + $this->assertFalse($resolver->isAnonymous($this->getToken())); + $this->assertFalse($resolver->isAnonymous($this->getRememberMeToken())); + $this->assertTrue($resolver->isAnonymous($this->getAnonymousToken())); + } + + public function testIsRememberMe() + { + $resolver = $this->getResolver(); + + $this->assertFalse($resolver->isRememberMe(null)); + $this->assertFalse($resolver->isRememberMe($this->getToken())); + $this->assertFalse($resolver->isRememberMe($this->getAnonymousToken())); + $this->assertTrue($resolver->isRememberMe($this->getRememberMeToken())); + } + + public function testisFullFledged() + { + $resolver = $this->getResolver(); + + $this->assertFalse($resolver->isFullFledged(null)); + $this->assertFalse($resolver->isFullFledged($this->getAnonymousToken())); + $this->assertFalse($resolver->isFullFledged($this->getRememberMeToken())); + $this->assertTrue($resolver->isFullFledged($this->getToken())); + } + + protected function getToken() + { + return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } + + protected function getAnonymousToken() + { + return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', null, array('', '')); + } + + protected function getRememberMeToken() + { + return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array('setPersistent'), array(), '', false); + } + + protected function getResolver() + { + return new AuthenticationTrustResolver( + 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\AnonymousToken', + 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\RememberMeToken' + ); + } +} diff --git a/Tests/Core/Authentication/Provider/AnonymousAuthenticationProviderTest.php b/Tests/Core/Authentication/Provider/AnonymousAuthenticationProviderTest.php new file mode 100644 index 0000000..0a76724 --- /dev/null +++ b/Tests/Core/Authentication/Provider/AnonymousAuthenticationProviderTest.php @@ -0,0 +1,66 @@ +<?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\Tests\Core\Authentication\Provider; + +use Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider; + +class AnonymousAuthenticationProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testSupports() + { + $provider = $this->getProvider('foo'); + + $this->assertTrue($provider->supports($this->getSupportedToken('foo'))); + $this->assertFalse($provider->supports($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))); + } + + public function testAuthenticateWhenTokenIsNotSupported() + { + $provider = $this->getProvider('foo'); + + $this->assertNull($provider->authenticate($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testAuthenticateWhenKeyIsNotValid() + { + $provider = $this->getProvider('foo'); + + $this->assertNull($provider->authenticate($this->getSupportedToken('bar'))); + } + + public function testAuthenticate() + { + $provider = $this->getProvider('foo'); + $token = $this->getSupportedToken('foo'); + + $this->assertSame($token, $provider->authenticate($token)); + } + + protected function getSupportedToken($key) + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', array('getKey'), array(), '', false); + $token->expects($this->any()) + ->method('getKey') + ->will($this->returnValue($key)) + ; + + return $token; + } + + protected function getProvider($key) + { + return new AnonymousAuthenticationProvider($key); + } +} diff --git a/Tests/Core/Authentication/Provider/DaoAuthenticationProviderTest.php b/Tests/Core/Authentication/Provider/DaoAuthenticationProviderTest.php new file mode 100644 index 0000000..e211da4 --- /dev/null +++ b/Tests/Core/Authentication/Provider/DaoAuthenticationProviderTest.php @@ -0,0 +1,263 @@ +<?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\Tests\Core\Authentication\Provider; + + +use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; + +use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; + +class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationServiceException + */ + public function testRetrieveUserWhenProviderDoesNotReturnAnUserInterface() + { + $provider = $this->getProvider('fabien'); + $method = new \ReflectionMethod($provider, 'retrieveUser'); + $method->setAccessible(true); + + $method->invoke($provider, 'fabien', $this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException + */ + public function testRetrieveUserWhenUsernameIsNotFound() + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider->expects($this->once()) + ->method('loadUserByUsername') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\UsernameNotFoundException', null, array(), '', false))) + ; + + $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface')); + $method = new \ReflectionMethod($provider, 'retrieveUser'); + $method->setAccessible(true); + + $method->invoke($provider, 'fabien', $this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationServiceException + */ + public function testRetrieveUserWhenAnExceptionOccurs() + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider->expects($this->once()) + ->method('loadUserByUsername') + ->will($this->throwException($this->getMock('RuntimeException', null, array(), '', false))) + ; + + $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface')); + $method = new \ReflectionMethod($provider, 'retrieveUser'); + $method->setAccessible(true); + + $method->invoke($provider, 'fabien', $this->getSupportedToken()); + } + + public function testRetrieveUserReturnsUserFromTokenOnReauthentication() + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider->expects($this->never()) + ->method('loadUserByUsername') + ; + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = $this->getSupportedToken(); + $token->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)) + ; + + $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface')); + $reflection = new \ReflectionMethod($provider, 'retrieveUser'); + $reflection->setAccessible(true); + $result = $reflection->invoke($provider, null, $token); + + $this->assertSame($user, $result); + } + + public function testRetrieveUser() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider->expects($this->once()) + ->method('loadUserByUsername') + ->will($this->returnValue($user)) + ; + + $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface')); + $method = new \ReflectionMethod($provider, 'retrieveUser'); + $method->setAccessible(true); + + $this->assertSame($user, $method->invoke($provider, 'fabien', $this->getSupportedToken())); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testCheckAuthenticationWhenCredentialsAreEmpty() + { + $provider = $this->getProvider(); + $method = new \ReflectionMethod($provider, 'checkAuthentication'); + $method->setAccessible(true); + + $token = $this->getSupportedToken(); + $token->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue('')) + ; + + $method->invoke($provider, $this->getMock('Symfony\Component\Security\Core\User\UserInterface'), $token); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testCheckAuthenticationWhenCredentialsAreNotValid() + { + $encoder = $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); + $encoder->expects($this->once()) + ->method('isPasswordValid') + ->will($this->returnValue(false)) + ; + + $provider = $this->getProvider(false, false, $encoder); + $method = new \ReflectionMethod($provider, 'checkAuthentication'); + $method->setAccessible(true); + + $token = $this->getSupportedToken(); + $token->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue('foo')) + ; + + $method->invoke($provider, $this->getMock('Symfony\Component\Security\Core\User\UserInterface'), $token); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testCheckAuthenticationDoesNotReauthenticateWhenPasswordHasChanged() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('foo')) + ; + + $token = $this->getSupportedToken(); + $token->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + $dbUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $dbUser->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('newFoo')) + ; + + $provider = $this->getProvider(false, false, null); + $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); + $reflection->setAccessible(true); + $reflection->invoke($provider, $dbUser, $token); + } + + public function testCheckAuthenticationWhenTokenNeedsReauthenticationWorksWithoutOriginalCredentials() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('foo')) + ; + + $token = $this->getSupportedToken(); + $token->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + $dbUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $dbUser->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('foo')) + ; + + $provider = $this->getProvider(false, false, null); + $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); + $reflection->setAccessible(true); + $reflection->invoke($provider, $dbUser, $token); + } + + public function testCheckAuthentication() + { + $encoder = $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); + $encoder->expects($this->once()) + ->method('isPasswordValid') + ->will($this->returnValue(true)) + ; + + $provider = $this->getProvider(false, false, $encoder); + $method = new \ReflectionMethod($provider, 'checkAuthentication'); + $method->setAccessible(true); + + $token = $this->getSupportedToken(); + $token->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue('foo')) + ; + + $method->invoke($provider, $this->getMock('Symfony\Component\Security\Core\User\UserInterface'), $token); + } + + protected function getSupportedToken() + { + $mock = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', array('getCredentials', 'getUser', 'getProviderKey'), array(), '', false); + $mock + ->expects($this->any()) + ->method('getProviderKey') + ->will($this->returnValue('key')) + ; + + return $mock; + } + + protected function getProvider($user = false, $userChecker = false, $passwordEncoder = null) + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + if (false !== $user) { + $userProvider->expects($this->once()) + ->method('loadUserByUsername') + ->will($this->returnValue($user)) + ; + } + + if (false === $userChecker) { + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + } + + if (null === $passwordEncoder) { + $passwordEncoder = new PlaintextPasswordEncoder(); + } + + $encoderFactory = $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface'); + $encoderFactory + ->expects($this->any()) + ->method('getEncoder') + ->will($this->returnValue($passwordEncoder)) + ; + + return new DaoAuthenticationProvider($userProvider, $userChecker, 'key', $encoderFactory); + } +} diff --git a/Tests/Core/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php b/Tests/Core/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php new file mode 100644 index 0000000..9476c0d --- /dev/null +++ b/Tests/Core/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php @@ -0,0 +1,133 @@ +<?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\Tests\Core\Authentication\Provider; + +use Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider; + +class PreAuthenticatedAuthenticationProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testSupports() + { + $provider = $this->getProvider(); + + $this->assertTrue($provider->supports($this->getSupportedToken())); + $this->assertFalse($provider->supports($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))); + + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken') + ->disableOriginalConstructor() + ->getMock() + ; + $token + ->expects($this->once()) + ->method('getProviderKey') + ->will($this->returnValue('foo')) + ; + $this->assertFalse($provider->supports($token)); + } + + public function testAuthenticateWhenTokenIsNotSupported() + { + $provider = $this->getProvider(); + + $this->assertNull($provider->authenticate($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testAuthenticateWhenNoUserIsSet() + { + $provider = $this->getProvider(); + $provider->authenticate($this->getSupportedToken('')); + } + + public function testAuthenticate() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue(array())) + ; + $provider = $this->getProvider($user); + + $token = $provider->authenticate($this->getSupportedToken('fabien', 'pass')); + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken', $token); + $this->assertEquals('pass', $token->getCredentials()); + $this->assertEquals('key', $token->getProviderKey()); + $this->assertEquals(array(), $token->getRoles()); + $this->assertEquals(array('foo' => 'bar'), $token->getAttributes(), '->authenticate() copies token attributes'); + $this->assertSame($user, $token->getUser()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\LockedException + */ + public function testAuthenticateWhenUserCheckerThrowsException() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + $userChecker->expects($this->once()) + ->method('checkPostAuth') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\LockedException', null, array(), '', false))) + ; + + $provider = $this->getProvider($user, $userChecker); + + $provider->authenticate($this->getSupportedToken('fabien')); + } + + protected function getSupportedToken($user = false, $credentials = false) + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken', array('getUser', 'getCredentials', 'getProviderKey'), array(), '', false); + if (false !== $user) { + $token->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)) + ; + } + if (false !== $credentials) { + $token->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue($credentials)) + ; + } + + $token + ->expects($this->any()) + ->method('getProviderKey') + ->will($this->returnValue('key')) + ; + + $token->setAttributes(array('foo' => 'bar')); + + return $token; + } + + protected function getProvider($user = false, $userChecker = false) + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + if (false !== $user) { + $userProvider->expects($this->once()) + ->method('loadUserByUsername') + ->will($this->returnValue($user)) + ; + } + + if (false === $userChecker) { + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + } + + return new PreAuthenticatedAuthenticationProvider($userProvider, $userChecker, 'key'); + } +} diff --git a/Tests/Core/Authentication/Provider/RememberMeAuthenticationProviderTest.php b/Tests/Core/Authentication/Provider/RememberMeAuthenticationProviderTest.php new file mode 100644 index 0000000..9598b03 --- /dev/null +++ b/Tests/Core/Authentication/Provider/RememberMeAuthenticationProviderTest.php @@ -0,0 +1,111 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Core\Authentication\Provider; + +use Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider; +use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; +use Symfony\Component\Security\Core\Role\Role; + +class RememberMeAuthenticationProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testSupports() + { + $provider = $this->getProvider(); + + $this->assertTrue($provider->supports($this->getSupportedToken())); + $this->assertFalse($provider->supports($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))); + } + + public function testAuthenticateWhenTokenIsNotSupported() + { + $provider = $this->getProvider(); + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $this->assertNull($provider->authenticate($token)); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testAuthenticateWhenKeysDoNotMatch() + { + $provider = $this->getProvider(null, 'key1'); + $token = $this->getSupportedToken(null, 'key2'); + + $provider->authenticate($token); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AccountExpiredException + */ + public function testAuthenticateWhenPostChecksFails() + { + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + $userChecker->expects($this->once()) + ->method('checkPostAuth') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\AccountExpiredException', null, array(), '', false))) + ; + + $provider = $this->getProvider($userChecker); + + $provider->authenticate($this->getSupportedToken()); + } + + public function testAuthenticate() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->expects($this->exactly(2)) + ->method('getRoles') + ->will($this->returnValue(array('ROLE_FOO'))) + ; + + $provider = $this->getProvider(); + + $token = $this->getSupportedToken($user); + $authToken = $provider->authenticate($token); + + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $authToken); + $this->assertSame($user, $authToken->getUser()); + $this->assertEquals(array(new Role('ROLE_FOO')), $authToken->getRoles()); + $this->assertEquals('', $authToken->getCredentials()); + } + + protected function getSupportedToken($user = null, $key = 'test') + { + if (null === $user) { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->any()) + ->method('getRoles') + ->will($this->returnValue(array())) + ; + } + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array('getProviderKey'), array($user, 'foo', $key)); + $token + ->expects($this->once()) + ->method('getProviderKey') + ->will($this->returnValue('foo')) + ; + + return $token; + } + + protected function getProvider($userChecker = null, $key = 'test') + { + if (null === $userChecker) { + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + } + + return new RememberMeAuthenticationProvider($userChecker, $key, 'foo'); + } +} diff --git a/Tests/Core/Authentication/Provider/UserAuthenticationProviderTest.php b/Tests/Core/Authentication/Provider/UserAuthenticationProviderTest.php new file mode 100644 index 0000000..1b68531 --- /dev/null +++ b/Tests/Core/Authentication/Provider/UserAuthenticationProviderTest.php @@ -0,0 +1,206 @@ +<?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\Tests\Core\Authentication\Provider; + +use Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider; +use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; + +class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testSupports() + { + $provider = $this->getProvider(); + + $this->assertTrue($provider->supports($this->getSupportedToken())); + $this->assertFalse($provider->supports($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))); + } + + public function testAuthenticateWhenTokenIsNotSupported() + { + $provider = $this->getProvider(); + + $this->assertNull($provider->authenticate($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException + */ + public function testAuthenticateWhenUsernameIsNotFound() + { + $provider = $this->getProvider(false, false); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\UsernameNotFoundException', null, array(), '', false))) + ; + + $provider->authenticate($this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testAuthenticateWhenUsernameIsNotFoundAndHideIsTrue() + { + $provider = $this->getProvider(false, true); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\UsernameNotFoundException', null, array(), '', false))) + ; + + $provider->authenticate($this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationServiceException + */ + public function testAuthenticateWhenProviderDoesNotReturnAnUserInterface() + { + $provider = $this->getProvider(false, true); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->returnValue(null)) + ; + + $provider->authenticate($this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\CredentialsExpiredException + */ + public function testAuthenticateWhenPreChecksFails() + { + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + $userChecker->expects($this->once()) + ->method('checkPreAuth') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\CredentialsExpiredException', null, array(), '', false))) + ; + + $provider = $this->getProvider($userChecker); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\User\UserInterface'))) + ; + + $provider->authenticate($this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AccountExpiredException + */ + public function testAuthenticateWhenPostChecksFails() + { + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + $userChecker->expects($this->once()) + ->method('checkPostAuth') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\AccountExpiredException', null, array(), '', false))) + ; + + $provider = $this->getProvider($userChecker); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\User\UserInterface'))) + ; + + $provider->authenticate($this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + * @expectedExceptionMessage Bad credentials + */ + public function testAuthenticateWhenPostCheckAuthenticationFails() + { + $provider = $this->getProvider(); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\User\UserInterface'))) + ; + $provider->expects($this->once()) + ->method('checkAuthentication') + ->will($this->throwException($this->getMock('Symfony\Component\Security\Core\Exception\BadCredentialsException', null, array(), '', false))) + ; + + $provider->authenticate($this->getSupportedToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\BadCredentialsException + * @expectedExceptionMessage Foo + */ + public function testAuthenticateWhenPostCheckAuthenticationFailsWithHideFalse() + { + $provider = $this->getProvider(false, false); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\User\UserInterface'))) + ; + $provider->expects($this->once()) + ->method('checkAuthentication') + ->will($this->throwException(new BadCredentialsException('Foo'))) + ; + + $provider->authenticate($this->getSupportedToken()); + } + + public function testAuthenticate() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue(array('ROLE_FOO'))) + ; + + $provider = $this->getProvider(); + $provider->expects($this->once()) + ->method('retrieveUser') + ->will($this->returnValue($user)) + ; + + $token = $this->getSupportedToken(); + $token->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue('foo')) + ; + + $authToken = $provider->authenticate($token); + + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $authToken); + $this->assertSame($user, $authToken->getUser()); + $this->assertEquals(array(new Role('ROLE_FOO')), $authToken->getRoles()); + $this->assertEquals('foo', $authToken->getCredentials()); + $this->assertEquals(array('foo' => 'bar'), $authToken->getAttributes(), '->authenticate() copies token attributes'); + } + + protected function getSupportedToken() + { + $mock = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', array('getCredentials', 'getProviderKey'), array(), '', false); + $mock + ->expects($this->any()) + ->method('getProviderKey') + ->will($this->returnValue('key')) + ; + + $mock->setAttributes(array('foo' => 'bar')); + + return $mock; + } + + protected function getProvider($userChecker = false, $hide = true) + { + if (false === $userChecker) { + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + } + + return $this->getMockForAbstractClass('Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider', array($userChecker, 'key', $hide)); + } +} diff --git a/Tests/Core/Authentication/RememberMe/InMemoryTokenProviderTest.php b/Tests/Core/Authentication/RememberMe/InMemoryTokenProviderTest.php new file mode 100644 index 0000000..f6ec2a6 --- /dev/null +++ b/Tests/Core/Authentication/RememberMe/InMemoryTokenProviderTest.php @@ -0,0 +1,63 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Core\Authentication\RememberMe; + +use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; +use Symfony\Component\Security\Core\Authentication\RememberMe\InMemoryTokenProvider; + +class InMemoryTokenProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testCreateNewToken() + { + $provider = new InMemoryTokenProvider(); + + $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime()); + $provider->createNewToken($token); + + $this->assertSame($provider->loadTokenBySeries('foo'), $token); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\TokenNotFoundException + */ + public function testLoadTokenBySeriesThrowsNotFoundException() + { + $provider = new InMemoryTokenProvider(); + $provider->loadTokenBySeries('foo'); + } + + public function testUpdateToken() + { + $provider = new InMemoryTokenProvider(); + + $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime()); + $provider->createNewToken($token); + $provider->updateToken('foo', 'newFoo', $lastUsed = new \DateTime()); + $token = $provider->loadTokenBySeries('foo'); + + $this->assertEquals('newFoo', $token->getTokenValue()); + $this->assertSame($token->getLastUsed(), $lastUsed); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\TokenNotFoundException + */ + public function testDeleteToken() + { + $provider = new InMemoryTokenProvider(); + + $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime()); + $provider->createNewToken($token); + $provider->deleteTokenBySeries('foo'); + $provider->loadTokenBySeries('foo'); + } +} diff --git a/Tests/Core/Authentication/RememberMe/PersistentTokenTest.php b/Tests/Core/Authentication/RememberMe/PersistentTokenTest.php new file mode 100644 index 0000000..7336666 --- /dev/null +++ b/Tests/Core/Authentication/RememberMe/PersistentTokenTest.php @@ -0,0 +1,29 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Core\Authentication\RememberMe; + +use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; + +class PersistentTokenTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $lastUsed = new \DateTime(); + $token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed); + + $this->assertEquals('fooclass', $token->getClass()); + $this->assertEquals('fooname', $token->getUsername()); + $this->assertEquals('fooseries', $token->getSeries()); + $this->assertEquals('footokenvalue', $token->getTokenValue()); + $this->assertSame($lastUsed, $token->getLastUsed()); + } +} diff --git a/Tests/Core/Authentication/Token/AbstractTokenTest.php b/Tests/Core/Authentication/Token/AbstractTokenTest.php new file mode 100644 index 0000000..4df6068 --- /dev/null +++ b/Tests/Core/Authentication/Token/AbstractTokenTest.php @@ -0,0 +1,244 @@ +<?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\Tests\Core\Authentication\Token; + +use Symfony\Component\Security\Core\Role\Role; + +class TestUser +{ + protected $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function __toString() + { + return $this->name; + } +} + +class AbstractTokenTest extends \PHPUnit_Framework_TestCase +{ + public function testGetUsername() + { + $token = $this->getToken(array('ROLE_FOO')); + $token->setUser('fabien'); + $this->assertEquals('fabien', $token->getUsername()); + + $token->setUser(new TestUser('fabien')); + $this->assertEquals('fabien', $token->getUsername()); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->expects($this->once())->method('getUsername')->will($this->returnValue('fabien')); + $token->setUser($user); + $this->assertEquals('fabien', $token->getUsername()); + } + + public function testEraseCredentials() + { + $token = $this->getToken(array('ROLE_FOO')); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->expects($this->once())->method('eraseCredentials'); + $token->setUser($user); + + $token->eraseCredentials(); + } + + /** + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::serialize + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::unserialize + */ + public function testSerialize() + { + $token = $this->getToken(array('ROLE_FOO')); + $token->setAttributes(array('foo' => 'bar')); + + $uToken = unserialize(serialize($token)); + + $this->assertEquals($token->getRoles(), $uToken->getRoles()); + $this->assertEquals($token->getAttributes(), $uToken->getAttributes()); + } + + /** + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::__construct + */ + public function testConstructor() + { + $token = $this->getToken(array('ROLE_FOO')); + $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); + + $token = $this->getToken(array(new Role('ROLE_FOO'))); + $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); + + $token = $this->getToken(array(new Role('ROLE_FOO'), 'ROLE_BAR')); + $this->assertEquals(array(new Role('ROLE_FOO'), new Role('ROLE_BAR')), $token->getRoles()); + } + + /** + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::isAuthenticated + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::setAuthenticated + */ + public function testAuthenticatedFlag() + { + $token = $this->getToken(); + $this->assertFalse($token->isAuthenticated()); + + $token->setAuthenticated(true); + $this->assertTrue($token->isAuthenticated()); + + $token->setAuthenticated(false); + $this->assertFalse($token->isAuthenticated()); + } + + /** + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::getAttributes + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::setAttributes + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::hasAttribute + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::getAttribute + * @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::setAttribute + */ + public function testAttributes() + { + $attributes = array('foo' => 'bar'); + $token = $this->getToken(); + $token->setAttributes($attributes); + + $this->assertEquals($attributes, $token->getAttributes(), '->getAttributes() returns the token attributes'); + $this->assertEquals('bar', $token->getAttribute('foo'), '->getAttribute() returns the value of a attribute'); + $token->setAttribute('foo', 'foo'); + $this->assertEquals('foo', $token->getAttribute('foo'), '->setAttribute() changes the value of a attribute'); + $this->assertTrue($token->hasAttribute('foo'), '->hasAttribute() returns true if the attribute is defined'); + $this->assertFalse($token->hasAttribute('oof'), '->hasAttribute() returns false if the attribute is not defined'); + + try { + $token->getAttribute('foobar'); + $this->fail('->getAttribute() throws an \InvalidArgumentException exception when the attribute does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getAttribute() throws an \InvalidArgumentException exception when the attribute does not exist'); + $this->assertEquals('This token has no "foobar" attribute.', $e->getMessage(), '->getAttribute() throws an \InvalidArgumentException exception when the attribute does not exist'); + } + } + + /** + * @dataProvider getUsers + */ + public function testSetUser($user) + { + $token = $this->getToken(); + $token->setUser($user); + $this->assertSame($user, $token->getUser()); + } + + public function getUsers() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $advancedUser = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + + return array( + array($advancedUser), + array($user), + array(new TestUser('foo')), + array('foo'), + ); + } + + /** + * @dataProvider getUserChanges + */ + public function testSetUserSetsAuthenticatedToFalseWhenUserChanges($firstUser, $secondUser) + { + $token = $this->getToken(); + $token->setAuthenticated(true); + $this->assertTrue($token->isAuthenticated()); + + $token->setUser($firstUser); + $this->assertTrue($token->isAuthenticated()); + + $token->setUser($secondUser); + $this->assertFalse($token->isAuthenticated()); + } + + public function getUserChanges() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $advancedUser = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + + return array( + array( + 'foo', 'bar', + ), + array( + 'foo', new TestUser('bar'), + ), + array( + 'foo', $user, + ), + array( + 'foo', $advancedUser + ), + array( + $user, 'foo' + ), + array( + $advancedUser, 'foo' + ), + array( + $user, new TestUser('foo'), + ), + array( + $advancedUser, new TestUser('foo'), + ), + array( + new TestUser('foo'), new TestUser('bar'), + ), + array( + new TestUser('foo'), 'bar', + ), + array( + new TestUser('foo'), $user, + ), + array( + new TestUser('foo'), $advancedUser, + ), + array( + $user, $advancedUser + ), + array( + $advancedUser, $user + ), + ); + } + + /** + * @dataProvider getUsers + */ + public function testSetUserDoesNotSetAuthenticatedToFalseWhenUserDoesNotChange($user) + { + $token = $this->getToken(); + $token->setAuthenticated(true); + $this->assertTrue($token->isAuthenticated()); + + $token->setUser($user); + $this->assertTrue($token->isAuthenticated()); + + $token->setUser($user); + $this->assertTrue($token->isAuthenticated()); + } + + protected function getToken(array $roles = array()) + { + return $this->getMockForAbstractClass('Symfony\Component\Security\Core\Authentication\Token\AbstractToken', array($roles)); + } +} diff --git a/Tests/Core/Authentication/Token/AnonymousTokenTest.php b/Tests/Core/Authentication/Token/AnonymousTokenTest.php new file mode 100644 index 0000000..135397b --- /dev/null +++ b/Tests/Core/Authentication/Token/AnonymousTokenTest.php @@ -0,0 +1,45 @@ +<?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\Tests\Core\Authentication\Token; + +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Role\Role; + +class AnonymousTokenTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $token = new AnonymousToken('foo', 'bar'); + $this->assertTrue($token->isAuthenticated()); + + $token = new AnonymousToken('foo', 'bar', array('ROLE_FOO')); + $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); + } + + public function testGetKey() + { + $token = new AnonymousToken('foo', 'bar'); + $this->assertEquals('foo', $token->getKey()); + } + + public function testGetCredentials() + { + $token = new AnonymousToken('foo', 'bar'); + $this->assertEquals('', $token->getCredentials()); + } + + public function testGetUser() + { + $token = new AnonymousToken('foo', 'bar'); + $this->assertEquals('bar', $token->getUser()); + } +} diff --git a/Tests/Core/Authentication/Token/PreAuthenticatedTokenTest.php b/Tests/Core/Authentication/Token/PreAuthenticatedTokenTest.php new file mode 100644 index 0000000..59a533a --- /dev/null +++ b/Tests/Core/Authentication/Token/PreAuthenticatedTokenTest.php @@ -0,0 +1,48 @@ +<?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\Tests\Core\Authentication\Token; + +use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; +use Symfony\Component\Security\Core\Role\Role; + +class PreAuthenticatedTokenTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $token = new PreAuthenticatedToken('foo', 'bar', 'key'); + $this->assertFalse($token->isAuthenticated()); + + $token = new PreAuthenticatedToken('foo', 'bar', 'key', array('ROLE_FOO')); + $this->assertTrue($token->isAuthenticated()); + $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); + $this->assertEquals('key', $token->getProviderKey()); + } + + public function testGetCredentials() + { + $token = new PreAuthenticatedToken('foo', 'bar', 'key'); + $this->assertEquals('bar', $token->getCredentials()); + } + + public function testGetUser() + { + $token = new PreAuthenticatedToken('foo', 'bar', 'key'); + $this->assertEquals('foo', $token->getUser()); + } + + public function testEraseCredentials() + { + $token = new PreAuthenticatedToken('foo', 'bar', 'key'); + $token->eraseCredentials(); + $this->assertEquals('', $token->getCredentials()); + } +} diff --git a/Tests/Core/Authentication/Token/RememerMeTokenTest.php b/Tests/Core/Authentication/Token/RememerMeTokenTest.php new file mode 100644 index 0000000..9daf9bf --- /dev/null +++ b/Tests/Core/Authentication/Token/RememerMeTokenTest.php @@ -0,0 +1,83 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Core\Authentication\Token; + +use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; +use Symfony\Component\Security\Core\Role\Role; + +class RememberMeTokenTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $user = $this->getUser(); + $token = new RememberMeToken($user, 'fookey', 'foo'); + + $this->assertEquals('fookey', $token->getProviderKey()); + $this->assertEquals('foo', $token->getKey()); + $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); + $this->assertSame($user, $token->getUser()); + $this->assertTrue($token->isAuthenticated()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorKeyCannotBeNull() + { + new RememberMeToken( + $this->getUser(), + null, + null + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorKeyCannotBeEmptyString() + { + new RememberMeToken( + $this->getUser(), + '', + '' + ); + } + + /** + * @expectedException PHPUnit_Framework_Error + * @dataProvider getUserArguments + */ + public function testConstructorUserCannotBeNull($user) + { + new RememberMeToken($user, 'foo', 'foo'); + } + + public function getUserArguments() + { + return array( + array(null), + array('foo'), + ); + } + + protected function getUser($roles = array('ROLE_FOO')) + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue($roles)) + ; + + return $user; + } +} diff --git a/Tests/Core/Authentication/Token/UsernamePasswordTokenTest.php b/Tests/Core/Authentication/Token/UsernamePasswordTokenTest.php new file mode 100644 index 0000000..3da20eb --- /dev/null +++ b/Tests/Core/Authentication/Token/UsernamePasswordTokenTest.php @@ -0,0 +1,58 @@ +<?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\Tests\Core\Authentication\Token; + +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Role\Role; + +class UsernamePasswordTokenTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $token = new UsernamePasswordToken('foo', 'bar', 'key'); + $this->assertFalse($token->isAuthenticated()); + + $token = new UsernamePasswordToken('foo', 'bar', 'key', array('ROLE_FOO')); + $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); + $this->assertTrue($token->isAuthenticated()); + $this->assertEquals('key', $token->getProviderKey()); + } + + /** + * @expectedException LogicException + */ + public function testSetAuthenticatedToTrue() + { + $token = new UsernamePasswordToken('foo', 'bar', 'key'); + $token->setAuthenticated(true); + } + + public function testSetAuthenticatedToFalse() + { + $token = new UsernamePasswordToken('foo', 'bar', 'key'); + $token->setAuthenticated(false); + $this->assertFalse($token->isAuthenticated()); + } + + public function testEraseCredentials() + { + $token = new UsernamePasswordToken('foo', 'bar', 'key'); + $token->eraseCredentials(); + $this->assertEquals('', $token->getCredentials()); + } + + public function testToString() + { + $token = new UsernamePasswordToken('foo', '', 'foo', array('A', 'B')); + $this->assertEquals('UsernamePasswordToken(user="foo", authenticated=true, roles="A, B")', (string) $token); + } +} diff --git a/Tests/Core/Authorization/AccessDecisionManagerTest.php b/Tests/Core/Authorization/AccessDecisionManagerTest.php new file mode 100644 index 0000000..1c706cc --- /dev/null +++ b/Tests/Core/Authorization/AccessDecisionManagerTest.php @@ -0,0 +1,151 @@ +<?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\Tests\Core\Authorization; + +use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; + +class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase +{ + public function testSupportsClass() + { + $manager = new AccessDecisionManager(array( + $this->getVoterSupportsClass(true), + $this->getVoterSupportsClass(false), + )); + $this->assertTrue($manager->supportsClass('FooClass')); + + $manager = new AccessDecisionManager(array( + $this->getVoterSupportsClass(false), + $this->getVoterSupportsClass(false), + )); + $this->assertFalse($manager->supportsClass('FooClass')); + } + + public function testSupportsAttribute() + { + $manager = new AccessDecisionManager(array( + $this->getVoterSupportsAttribute(true), + $this->getVoterSupportsAttribute(false), + )); + $this->assertTrue($manager->supportsAttribute('foo')); + + $manager = new AccessDecisionManager(array( + $this->getVoterSupportsAttribute(false), + $this->getVoterSupportsAttribute(false), + )); + $this->assertFalse($manager->supportsAttribute('foo')); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetVotersEmpty() + { + $manager = new AccessDecisionManager(array()); + } + + /** + * @dataProvider getStrategyTests + */ + public function testStrategies($strategy, $voters, $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions, $expected) + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $manager = new AccessDecisionManager($voters, $strategy, $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions); + + $this->assertSame($expected, $manager->decide($token, array('ROLE_FOO'))); + } + + public function getStrategyTests() + { + return array( + // affirmative + array('affirmative', $this->getVoters(1, 0, 0), false, true, true), + array('affirmative', $this->getVoters(1, 2, 0), false, true, true), + array('affirmative', $this->getVoters(0, 1, 0), false, true, false), + array('affirmative', $this->getVoters(0, 0, 1), false, true, false), + array('affirmative', $this->getVoters(0, 0, 1), true, true, true), + + // consensus + array('consensus', $this->getVoters(1, 0, 0), false, true, true), + array('consensus', $this->getVoters(1, 2, 0), false, true, false), + array('consensus', $this->getVoters(2, 1, 0), false, true, true), + + array('consensus', $this->getVoters(0, 0, 1), false, true, false), + + array('consensus', $this->getVoters(0, 0, 1), true, true, true), + + array('consensus', $this->getVoters(2, 2, 0), false, true, true), + array('consensus', $this->getVoters(2, 2, 1), false, true, true), + + array('consensus', $this->getVoters(2, 2, 0), false, false, false), + array('consensus', $this->getVoters(2, 2, 1), false, false, false), + + // unanimous + array('unanimous', $this->getVoters(1, 0, 0), false, true, true), + array('unanimous', $this->getVoters(1, 0, 1), false, true, true), + array('unanimous', $this->getVoters(1, 1, 0), false, true, false), + + array('unanimous', $this->getVoters(0, 0, 2), false, true, false), + array('unanimous', $this->getVoters(0, 0, 2), true, true, true), + ); + } + + protected function getVoters($grants, $denies, $abstains) + { + $voters = array(); + for ($i = 0; $i < $grants; $i++) { + $voters[] = $this->getVoter(VoterInterface::ACCESS_GRANTED); + } + for ($i = 0; $i < $denies; $i++) { + $voters[] = $this->getVoter(VoterInterface::ACCESS_DENIED); + } + for ($i = 0; $i < $abstains; $i++) { + $voters[] = $this->getVoter(VoterInterface::ACCESS_ABSTAIN); + } + + return $voters; + } + + protected function getVoter($vote) + { + $voter = $this->getMock('Symfony\Component\Security\Core\Authorization\Voter\VoterInterface'); + $voter->expects($this->any()) + ->method('vote') + ->will($this->returnValue($vote)); + ; + + return $voter; + } + + protected function getVoterSupportsClass($ret) + { + $voter = $this->getMock('Symfony\Component\Security\Core\Authorization\Voter\VoterInterface'); + $voter->expects($this->any()) + ->method('supportsClass') + ->will($this->returnValue($ret)); + ; + + return $voter; + } + + protected function getVoterSupportsAttribute($ret) + { + $voter = $this->getMock('Symfony\Component\Security\Core\Authorization\Voter\VoterInterface'); + $voter->expects($this->any()) + ->method('supportsAttribute') + ->will($this->returnValue($ret)); + ; + + return $voter; + } +} diff --git a/Tests/Core/Authorization/Voter/AuthenticatedVoterTest.php b/Tests/Core/Authorization/Voter/AuthenticatedVoterTest.php new file mode 100644 index 0000000..5704b3d --- /dev/null +++ b/Tests/Core/Authorization/Voter/AuthenticatedVoterTest.php @@ -0,0 +1,79 @@ +<?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\Tests\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; +use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; +use Symfony\Component\Security\Core\Role\Role; + +class AuthenticatedVoterTest extends \PHPUnit_Framework_TestCase +{ + public function testSupportsClass() + { + $voter = new AuthenticatedVoter($this->getResolver()); + $this->assertTrue($voter->supportsClass('stdClass')); + } + + /** + * @dataProvider getVoteTests + */ + public function testVote($authenticated, $attributes, $expected) + { + $voter = new AuthenticatedVoter($this->getResolver()); + + $this->assertSame($expected, $voter->vote($this->getToken($authenticated), null, $attributes)); + } + + public function getVoteTests() + { + return array( + array('fully', array(), VoterInterface::ACCESS_ABSTAIN), + array('fully', array('FOO'), VoterInterface::ACCESS_ABSTAIN), + array('remembered', array(), VoterInterface::ACCESS_ABSTAIN), + array('remembered', array('FOO'), VoterInterface::ACCESS_ABSTAIN), + array('anonymously', array(), VoterInterface::ACCESS_ABSTAIN), + array('anonymously', array('FOO'), VoterInterface::ACCESS_ABSTAIN), + + array('fully', array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED), + array('remembered', array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED), + array('anonymously', array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED), + + array('fully', array('IS_AUTHENTICATED_REMEMBERED'), VoterInterface::ACCESS_GRANTED), + array('remembered', array('IS_AUTHENTICATED_REMEMBERED'), VoterInterface::ACCESS_GRANTED), + array('anonymously', array('IS_AUTHENTICATED_REMEMBERED'), VoterInterface::ACCESS_DENIED), + + array('fully', array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_GRANTED), + array('remembered', array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_DENIED), + array('anonymously', array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_DENIED), + ); + } + + protected function getResolver() + { + return new AuthenticationTrustResolver( + 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\AnonymousToken', + 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\RememberMeToken' + ); + } + + protected function getToken($authenticated) + { + if ('fully' === $authenticated) { + return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } elseif ('remembered' === $authenticated) { + return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array('setPersistent'), array(), '', false); + } else { + return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', null, array('', '')); + } + } +} diff --git a/Tests/Core/Authorization/Voter/RoleHierarchyVoterTest.php b/Tests/Core/Authorization/Voter/RoleHierarchyVoterTest.php new file mode 100644 index 0000000..a50fa79 --- /dev/null +++ b/Tests/Core/Authorization/Voter/RoleHierarchyVoterTest.php @@ -0,0 +1,36 @@ +<?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\Tests\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; +use Symfony\Component\Security\Core\Role\RoleHierarchy; + +class RoleHierarchyVoterTest extends RoleVoterTest +{ + /** + * @dataProvider getVoteTests + */ + public function testVote($roles, $attributes, $expected) + { + $voter = new RoleHierarchyVoter(new RoleHierarchy(array('ROLE_FOO' => array('ROLE_FOOBAR')))); + + $this->assertSame($expected, $voter->vote($this->getToken($roles), null, $attributes)); + } + + public function getVoteTests() + { + return array_merge(parent::getVoteTests(), array( + array(array('ROLE_FOO'), array('ROLE_FOOBAR'), VoterInterface::ACCESS_GRANTED), + )); + } +} diff --git a/Tests/Core/Authorization/Voter/RoleVoterTest.php b/Tests/Core/Authorization/Voter/RoleVoterTest.php new file mode 100644 index 0000000..63608eb --- /dev/null +++ b/Tests/Core/Authorization/Voter/RoleVoterTest.php @@ -0,0 +1,62 @@ +<?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\Tests\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; +use Symfony\Component\Security\Core\Role\Role; + +class RoleVoterTest extends \PHPUnit_Framework_TestCase +{ + public function testSupportsClass() + { + $voter = new RoleVoter(); + + $this->assertTrue($voter->supportsClass('Foo')); + } + + /** + * @dataProvider getVoteTests + */ + public function testVote($roles, $attributes, $expected) + { + $voter = new RoleVoter(); + + $this->assertSame($expected, $voter->vote($this->getToken($roles), null, $attributes)); + } + + public function getVoteTests() + { + return array( + array(array(), array(), VoterInterface::ACCESS_ABSTAIN), + array(array(), array('FOO'), VoterInterface::ACCESS_ABSTAIN), + array(array(), array('ROLE_FOO'), VoterInterface::ACCESS_DENIED), + array(array('ROLE_FOO'), array('ROLE_FOO'), VoterInterface::ACCESS_GRANTED), + array(array('ROLE_FOO'), array('FOO', 'ROLE_FOO'), VoterInterface::ACCESS_GRANTED), + array(array('ROLE_BAR', 'ROLE_FOO'), array('ROLE_FOO'), VoterInterface::ACCESS_GRANTED), + ); + } + + protected function getToken(array $roles) + { + foreach ($roles as $i => $role) { + $roles[$i] = new Role($role); + } + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue($roles)); + ; + + return $token; + } +} diff --git a/Tests/Core/Encoder/BasePasswordEncoderTest.php b/Tests/Core/Encoder/BasePasswordEncoderTest.php new file mode 100644 index 0000000..2ef1dcc --- /dev/null +++ b/Tests/Core/Encoder/BasePasswordEncoderTest.php @@ -0,0 +1,85 @@ +<?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\Tests\Core\Encoder; + +use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder; + +class PasswordEncoder extends BasePasswordEncoder +{ + public function encodePassword($raw, $salt) + { + } + + public function isPasswordValid($encoded, $raw, $salt) + { + } +} + +class BasePasswordEncoderTest extends \PHPUnit_Framework_TestCase +{ + public function testComparePassword() + { + $this->assertTrue($this->invokeComparePasswords('password', 'password')); + $this->assertFalse($this->invokeComparePasswords('password', 'foo')); + } + + public function testDemergePasswordAndSalt() + { + $this->assertEquals(array('password', 'salt'), $this->invokeDemergePasswordAndSalt('password{salt}')); + $this->assertEquals(array('password', ''), $this->invokeDemergePasswordAndSalt('password')); + $this->assertEquals(array('', ''), $this->invokeDemergePasswordAndSalt('')); + } + + public function testMergePasswordAndSalt() + { + $this->assertEquals('password{salt}', $this->invokeMergePasswordAndSalt('password', 'salt')); + $this->assertEquals('password', $this->invokeMergePasswordAndSalt('password', '')); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testMergePasswordAndSaltWithException() + { + $this->invokeMergePasswordAndSalt('password', '{foo}'); + } + + protected function invokeDemergePasswordAndSalt($password) + { + $encoder = new PasswordEncoder(); + $r = new \ReflectionObject($encoder); + $m = $r->getMethod('demergePasswordAndSalt'); + $m->setAccessible(true); + + return $m->invoke($encoder, $password); + } + + protected function invokeMergePasswordAndSalt($password, $salt) + { + $encoder = new PasswordEncoder(); + $r = new \ReflectionObject($encoder); + $m = $r->getMethod('mergePasswordAndSalt'); + $m->setAccessible(true); + + return $m->invoke($encoder, $password, $salt); + } + + protected function invokeComparePasswords($p1, $p2) + { + $encoder = new PasswordEncoder(); + $r = new \ReflectionObject($encoder); + $m = $r->getMethod('comparePasswords'); + $m->setAccessible(true); + + return $m->invoke($encoder, $p1, $p2); + } +} diff --git a/Tests/Core/Encoder/EncoderFactoryTest.php b/Tests/Core/Encoder/EncoderFactoryTest.php new file mode 100644 index 0000000..a060809 --- /dev/null +++ b/Tests/Core/Encoder/EncoderFactoryTest.php @@ -0,0 +1,43 @@ +<?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\Tests\Core\Encoder; + +use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\EncoderFactory; + +class EncoderFactoryTest extends \PHPUnit_Framework_TestCase +{ + public function testGetEncoderWithMessageDigestEncoder() + { + $factory = new EncoderFactory(array('Symfony\Component\Security\Core\User\UserInterface' => array( + 'class' => 'Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder', + 'arguments' => array('sha512', true, 5), + ))); + + $encoder = $factory->getEncoder($this->getMock('Symfony\Component\Security\Core\User\UserInterface')); + $expectedEncoder = new MessageDigestPasswordEncoder('sha512', true, 5); + + $this->assertEquals($expectedEncoder->encodePassword('foo', 'moo'), $encoder->encodePassword('foo', 'moo')); + } + + public function testGetEncoderWithService() + { + $factory = new EncoderFactory(array( + 'Symfony\Component\Security\Core\User\UserInterface' => new MessageDigestPasswordEncoder('sha1'), + )); + + $encoder = $factory->getEncoder($this->getMock('Symfony\Component\Security\Core\User\UserInterface')); + $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); + + $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); + } +} diff --git a/Tests/Core/Encoder/MessageDigestPasswordEncoderTest.php b/Tests/Core/Encoder/MessageDigestPasswordEncoderTest.php new file mode 100644 index 0000000..64032c4 --- /dev/null +++ b/Tests/Core/Encoder/MessageDigestPasswordEncoderTest.php @@ -0,0 +1,45 @@ +<?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\Tests\Core\Encoder; + +use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; + +class MessageDigestPasswordEncoderTest extends \PHPUnit_Framework_TestCase +{ + public function testIsPasswordValid() + { + $encoder = new MessageDigestPasswordEncoder('sha256', false, 1); + + $this->assertTrue($encoder->isPasswordValid(hash('sha256', 'password'), 'password', '')); + } + + public function testEncodePassword() + { + $encoder = new MessageDigestPasswordEncoder('sha256', false, 1); + $this->assertSame(hash('sha256', 'password'), $encoder->encodePassword('password', '')); + + $encoder = new MessageDigestPasswordEncoder('sha256', true, 1); + $this->assertSame(base64_encode(hash('sha256', 'password', true)), $encoder->encodePassword('password', '')); + + $encoder = new MessageDigestPasswordEncoder('sha256', false, 2); + $this->assertSame(hash('sha256', hash('sha256', 'password', true).'password'), $encoder->encodePassword('password', '')); + } + + /** + * @expectedException LogicException + */ + public function testEncodePasswordAlgorithmDoesNotExist() + { + $encoder = new MessageDigestPasswordEncoder('foobar'); + $encoder->encodePassword('password', ''); + } +} diff --git a/Tests/Core/Encoder/PlaintextPasswordEncoderTest.php b/Tests/Core/Encoder/PlaintextPasswordEncoderTest.php new file mode 100644 index 0000000..af0008f --- /dev/null +++ b/Tests/Core/Encoder/PlaintextPasswordEncoderTest.php @@ -0,0 +1,39 @@ +<?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\Tests\Core\Encoder; + +use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; + +class PlaintextPasswordEncoderTest extends \PHPUnit_Framework_TestCase +{ + public function testIsPasswordValid() + { + $encoder = new PlaintextPasswordEncoder(); + + $this->assertTrue($encoder->isPasswordValid('foo', 'foo', '')); + $this->assertFalse($encoder->isPasswordValid('bar', 'foo', '')); + $this->assertFalse($encoder->isPasswordValid('FOO', 'foo', '')); + + $encoder = new PlaintextPasswordEncoder(true); + + $this->assertTrue($encoder->isPasswordValid('foo', 'foo', '')); + $this->assertFalse($encoder->isPasswordValid('bar', 'foo', '')); + $this->assertTrue($encoder->isPasswordValid('FOO', 'foo', '')); + } + + public function testEncodePassword() + { + $encoder = new PlaintextPasswordEncoder(); + + $this->assertSame('foo', $encoder->encodePassword('foo', '')); + } +} diff --git a/Tests/Core/Role/RoleHierarchyTest.php b/Tests/Core/Role/RoleHierarchyTest.php new file mode 100644 index 0000000..a98aed6 --- /dev/null +++ b/Tests/Core/Role/RoleHierarchyTest.php @@ -0,0 +1,32 @@ +<?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\Tests\Core\Role; + +use Symfony\Component\Security\Core\Role\RoleHierarchy; +use Symfony\Component\Security\Core\Role\Role; + +class RoleHierarchyTest extends \PHPUnit_Framework_TestCase +{ + public function testGetReachableRoles() + { + $role = new RoleHierarchy(array( + 'ROLE_ADMIN' => array('ROLE_USER'), + 'ROLE_SUPER_ADMIN' => array('ROLE_ADMIN', 'ROLE_FOO'), + )); + + $this->assertEquals(array(new Role('ROLE_USER')), $role->getReachableRoles(array(new Role('ROLE_USER')))); + $this->assertEquals(array(new Role('ROLE_FOO')), $role->getReachableRoles(array(new Role('ROLE_FOO')))); + $this->assertEquals(array(new Role('ROLE_ADMIN'), new Role('ROLE_USER')), $role->getReachableRoles(array(new Role('ROLE_ADMIN')))); + $this->assertEquals(array(new Role('ROLE_FOO'), new Role('ROLE_ADMIN'), new Role('ROLE_USER')), $role->getReachableRoles(array(new Role('ROLE_FOO'), new Role('ROLE_ADMIN')))); + $this->assertEquals(array(new Role('ROLE_SUPER_ADMIN'), new Role('ROLE_ADMIN'), new Role('ROLE_FOO'), new Role('ROLE_USER')), $role->getReachableRoles(array(new Role('ROLE_SUPER_ADMIN')))); + } +} diff --git a/Tests/Core/Role/RoleTest.php b/Tests/Core/Role/RoleTest.php new file mode 100644 index 0000000..e2e7ca8 --- /dev/null +++ b/Tests/Core/Role/RoleTest.php @@ -0,0 +1,24 @@ +<?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\Tests\Core\Role; + +use Symfony\Component\Security\Core\Role\Role; + +class RoleTest extends \PHPUnit_Framework_TestCase +{ + public function testGetRole() + { + $role = new Role('FOO'); + + $this->assertEquals('FOO', $role->getRole()); + } +} diff --git a/Tests/Core/Role/SwitchUserRoleTest.php b/Tests/Core/Role/SwitchUserRoleTest.php new file mode 100644 index 0000000..bf9b173 --- /dev/null +++ b/Tests/Core/Role/SwitchUserRoleTest.php @@ -0,0 +1,31 @@ +<?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\Tests\Core\Role; + +use Symfony\Component\Security\Core\Role\SwitchUserRole; + +class SwitchUserRoleTest extends \PHPUnit_Framework_TestCase +{ + public function testGetSource() + { + $role = new SwitchUserRole('FOO', $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + + $this->assertSame($token, $role->getSource()); + } + + public function testGetRole() + { + $role = new SwitchUserRole('FOO', $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + + $this->assertEquals('FOO', $role->getRole()); + } +} diff --git a/Tests/Core/SecurityContextTest.php b/Tests/Core/SecurityContextTest.php new file mode 100644 index 0000000..66a4b13 --- /dev/null +++ b/Tests/Core/SecurityContextTest.php @@ -0,0 +1,92 @@ +<?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\Tests\Core; + +use Symfony\Component\Security\Core\SecurityContext; + +class SecurityContextTest extends \PHPUnit_Framework_TestCase +{ + public function testVoteAuthenticatesTokenIfNecessary() + { + $authManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $decisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + + $context = new SecurityContext($authManager, $decisionManager); + $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + + $authManager + ->expects($this->once()) + ->method('authenticate') + ->with($this->equalTo($token)) + ->will($this->returnValue($newToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))) + ; + + $decisionManager + ->expects($this->once()) + ->method('decide') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($context->isGranted('foo')); + $this->assertSame($newToken, $context->getToken()); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException + */ + public function testVoteWithoutAuthenticationToken() + { + $context = new SecurityContext( + $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), + $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface') + ); + + $context->isGranted('ROLE_FOO'); + } + + public function testIsGranted() + { + $manager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + $manager->expects($this->once())->method('decide')->will($this->returnValue(false)); + $context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), $manager); + $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $token + ->expects($this->once()) + ->method('isAuthenticated') + ->will($this->returnValue(true)) + ; + $this->assertFalse($context->isGranted('ROLE_FOO')); + + $manager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + $manager->expects($this->once())->method('decide')->will($this->returnValue(true)); + $context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), $manager); + $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $token + ->expects($this->once()) + ->method('isAuthenticated') + ->will($this->returnValue(true)) + ; + $this->assertTrue($context->isGranted('ROLE_FOO')); + } + + public function testGetSetToken() + { + $context = new SecurityContext( + $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), + $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface') + ); + $this->assertNull($context->getToken()); + + $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->assertSame($token, $context->getToken()); + } +} diff --git a/Tests/Core/User/AccountCheckerTest.php b/Tests/Core/User/AccountCheckerTest.php new file mode 100644 index 0000000..315e0d4 --- /dev/null +++ b/Tests/Core/User/AccountCheckerTest.php @@ -0,0 +1,108 @@ +<?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\Tests\Core\User; + +use Symfony\Component\Security\Core\User\UserChecker; + +class UserCheckerTest extends \PHPUnit_Framework_TestCase +{ + public function testCheckPreAuthNotAdvancedUserInterface() + { + $checker = new UserChecker(); + + $this->assertNull($checker->checkPreAuth($this->getMock('Symfony\Component\Security\Core\User\UserInterface'))); + } + + public function testCheckPreAuthPass() + { + $checker = new UserChecker(); + + $account = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + $account->expects($this->once())->method('isCredentialsNonExpired')->will($this->returnValue(true)); + + $this->assertNull($checker->checkPreAuth($account)); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\CredentialsExpiredException + */ + public function testCheckPreAuthCredentialsExpired() + { + $checker = new UserChecker(); + + $account = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + $account->expects($this->once())->method('isCredentialsNonExpired')->will($this->returnValue(false)); + + $checker->checkPreAuth($account); + } + + public function testCheckPostAuthNotAdvancedUserInterface() + { + $checker = new UserChecker(); + + $this->assertNull($checker->checkPostAuth($this->getMock('Symfony\Component\Security\Core\User\UserInterface'))); + } + + public function testCheckPostAuthPass() + { + $checker = new UserChecker(); + + $account = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + $account->expects($this->once())->method('isAccountNonLocked')->will($this->returnValue(true)); + $account->expects($this->once())->method('isEnabled')->will($this->returnValue(true)); + $account->expects($this->once())->method('isAccountNonExpired')->will($this->returnValue(true)); + + $this->assertNull($checker->checkPostAuth($account)); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\LockedException + */ + public function testCheckPostAuthAccountLocked() + { + $checker = new UserChecker(); + + $account = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + $account->expects($this->once())->method('isAccountNonLocked')->will($this->returnValue(false)); + + $checker->checkPostAuth($account); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\DisabledException + */ + public function testCheckPostAuthDisabled() + { + $checker = new UserChecker(); + + $account = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + $account->expects($this->once())->method('isAccountNonLocked')->will($this->returnValue(true)); + $account->expects($this->once())->method('isEnabled')->will($this->returnValue(false)); + + $checker->checkPostAuth($account); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AccountExpiredException + */ + public function testCheckPostAuthAccountExpired() + { + $checker = new UserChecker(); + + $account = $this->getMock('Symfony\Component\Security\Core\User\AdvancedUserInterface'); + $account->expects($this->once())->method('isAccountNonLocked')->will($this->returnValue(true)); + $account->expects($this->once())->method('isEnabled')->will($this->returnValue(true)); + $account->expects($this->once())->method('isAccountNonExpired')->will($this->returnValue(false)); + + $checker->checkPostAuth($account); + } +} diff --git a/Tests/Core/User/ChainUserProviderTest.php b/Tests/Core/User/ChainUserProviderTest.php new file mode 100644 index 0000000..97e397f --- /dev/null +++ b/Tests/Core/User/ChainUserProviderTest.php @@ -0,0 +1,185 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Core\User; + +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; + +use Symfony\Component\Security\Core\User\ChainUserProvider; + +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; + +class ChainUserProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testLoadUserByUsername() + { + $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foo')) + ->will($this->throwException(new UsernameNotFoundException('not found'))) + ; + + $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foo')) + ->will($this->returnValue($account = $this->getAccount())) + ; + + $provider = new ChainUserProvider(array($provider1, $provider2)); + $this->assertSame($account, $provider->loadUserByUsername('foo')); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException + */ + public function testLoadUserByUsernameThrowsUsernameNotFoundException() + { + $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foo')) + ->will($this->throwException(new UsernameNotFoundException('not found'))) + ; + + $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foo')) + ->will($this->throwException(new UsernameNotFoundException('not found'))) + ; + + $provider = new ChainUserProvider(array($provider1, $provider2)); + $provider->loadUserByUsername('foo'); + } + + public function testRefreshUser() + { + $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('refreshUser') + ->will($this->throwException(new UnsupportedUserException('unsupported'))) + ; + + $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('refreshUser') + ->will($this->returnValue($account = $this->getAccount())) + ; + + $provider = new ChainUserProvider(array($provider1, $provider2)); + $this->assertSame($account, $provider->refreshUser($this->getAccount())); + } + + public function testRefreshUserAgain() + { + $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('refreshUser') + ->will($this->throwException(new UsernameNotFoundException('not found'))) + ; + + $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('refreshUser') + ->will($this->returnValue($account = $this->getAccount())) + ; + + $provider = new ChainUserProvider(array($provider1, $provider2)); + $this->assertSame($account, $provider->refreshUser($this->getAccount())); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\UnsupportedUserException + */ + public function testRefreshUserThrowsUnsupportedUserException() + { + $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('refreshUser') + ->will($this->throwException(new UnsupportedUserException('unsupported'))) + ; + + $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('refreshUser') + ->will($this->throwException(new UnsupportedUserException('unsupported'))) + ; + + $provider = new ChainUserProvider(array($provider1, $provider2)); + $provider->refreshUser($this->getAccount()); + } + + public function testSupportsClass() + { + $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('supportsClass') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)) + ; + + $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('supportsClass') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)) + ; + + $provider = new ChainUserProvider(array($provider1, $provider2)); + $this->assertTrue($provider->supportsClass('foo')); + } + + public function testSupportsClassWhenNotSupported() + { + $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('supportsClass') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)) + ; + + $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('supportsClass') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)) + ; + + $provider = new ChainUserProvider(array($provider1, $provider2)); + $this->assertFalse($provider->supportsClass('foo')); + } + + protected function getAccount() + { + return $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + } + + protected function getProvider() + { + return $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + } +} diff --git a/Tests/Core/User/InMemoryProviderTest.php b/Tests/Core/User/InMemoryProviderTest.php new file mode 100644 index 0000000..9230be4 --- /dev/null +++ b/Tests/Core/User/InMemoryProviderTest.php @@ -0,0 +1,62 @@ +<?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\Tests\Core\User; + +use Symfony\Component\Security\Core\User\InMemoryUserProvider; +use Symfony\Component\Security\Core\User\User; + +class InMemoryUserProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $provider = new InMemoryUserProvider(array( + 'fabien' => array( + 'password' => 'foo', + 'enabled' => false, + 'roles' => array('ROLE_USER'), + ), + )); + + $user = $provider->loadUserByUsername('fabien'); + $this->assertEquals('foo', $user->getPassword()); + $this->assertEquals(array('ROLE_USER'), $user->getRoles()); + $this->assertFalse($user->isEnabled()); + } + + public function testCreateUser() + { + $provider = new InMemoryUserProvider(); + $provider->createUser(new User('fabien', 'foo')); + + $user = $provider->loadUserByUsername('fabien'); + $this->assertEquals('foo', $user->getPassword()); + } + + /** + * @expectedException LogicException + */ + public function testCreateUserAlreadyExist() + { + $provider = new InMemoryUserProvider(); + $provider->createUser(new User('fabien', 'foo')); + $provider->createUser(new User('fabien', 'foo')); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException + */ + public function testLoadUserByUsernameDoesNotExist() + { + $provider = new InMemoryUserProvider(); + $provider->loadUserByUsername('fabien'); + } +} diff --git a/Tests/Core/User/UserTest.php b/Tests/Core/User/UserTest.php new file mode 100644 index 0000000..26e562f --- /dev/null +++ b/Tests/Core/User/UserTest.php @@ -0,0 +1,126 @@ +<?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\Tests\Core\User; + +use Symfony\Component\Security\Core\User\User; + +class UserTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\Security\Core\User\User::__construct + * @expectedException InvalidArgumentException + */ + public function testConstructorException() + { + new User('', 'superpass'); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::__construct + * @covers Symfony\Component\Security\Core\User\User::getRoles + */ + public function testGetRoles() + { + $user = new User('fabien', 'superpass'); + $this->assertEquals(array(), $user->getRoles()); + + $user = new User('fabien', 'superpass', array('ROLE_ADMIN')); + $this->assertEquals(array('ROLE_ADMIN'), $user->getRoles()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::__construct + * @covers Symfony\Component\Security\Core\User\User::getPassword + */ + public function testGetPassword() + { + $user = new User('fabien', 'superpass'); + $this->assertEquals('superpass', $user->getPassword()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::__construct + * @covers Symfony\Component\Security\Core\User\User::getUsername + */ + public function testGetUsername() + { + $user = new User('fabien', 'superpass'); + $this->assertEquals('fabien', $user->getUsername()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::getSalt + */ + public function testGetSalt() + { + $user = new User('fabien', 'superpass'); + $this->assertEquals('', $user->getSalt()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::isAccountNonExpired + */ + public function testIsAccountNonExpired() + { + $user = new User('fabien', 'superpass'); + $this->assertTrue($user->isAccountNonExpired()); + + $user = new User('fabien', 'superpass', array(), true, false); + $this->assertFalse($user->isAccountNonExpired()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::isCredentialsNonExpired + */ + public function testIsCredentialsNonExpired() + { + $user = new User('fabien', 'superpass'); + $this->assertTrue($user->isCredentialsNonExpired()); + + $user = new User('fabien', 'superpass', array(), true, true, false); + $this->assertFalse($user->isCredentialsNonExpired()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::isAccountNonLocked + */ + public function testIsAccountNonLocked() + { + $user = new User('fabien', 'superpass'); + $this->assertTrue($user->isAccountNonLocked()); + + $user = new User('fabien', 'superpass', array(), true, true, true, false); + $this->assertFalse($user->isAccountNonLocked()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::isEnabled + */ + public function testIsEnabled() + { + $user = new User('fabien', 'superpass'); + $this->assertTrue($user->isEnabled()); + + $user = new User('fabien', 'superpass', array(), false); + $this->assertFalse($user->isEnabled()); + } + + /** + * @covers Symfony\Component\Security\Core\User\User::eraseCredentials + */ + public function testEraseCredentials() + { + $user = new User('fabien', 'superpass'); + $user->eraseCredentials(); + $this->assertEquals('superpass', $user->getPassword()); + } +} diff --git a/Tests/Http/EntryPoint/BasicAuthenticationEntryPointTest.php b/Tests/Http/EntryPoint/BasicAuthenticationEntryPointTest.php new file mode 100644 index 0000000..3b4b2c9 --- /dev/null +++ b/Tests/Http/EntryPoint/BasicAuthenticationEntryPointTest.php @@ -0,0 +1,43 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\EntryPoint; + +use Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint; +use Symfony\Component\Security\Core\Exception\AuthenticationException; + +class BasicAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testStart() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + + $authException = new AuthenticationException('The exception message'); + + $entryPoint = new BasicAuthenticationEntryPoint('TheRealmName'); + $response = $entryPoint->start($request, $authException); + + $this->assertEquals('Basic realm="TheRealmName"', $response->headers->get('WWW-Authenticate')); + $this->assertEquals(401, $response->getStatusCode()); + $this->assertAttributeEquals('The exception message', 'statusText', $response); + } + + public function testStartWithoutAuthException() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + + $entryPoint = new BasicAuthenticationEntryPoint('TheRealmName'); + + $response = $entryPoint->start($request); + + $this->assertEquals('Basic realm="TheRealmName"', $response->headers->get('WWW-Authenticate')); + $this->assertEquals(401, $response->getStatusCode()); + $this->assertAttributeEquals('Unauthorized', 'statusText', $response); + } +} diff --git a/Tests/Http/EntryPoint/DigestAuthenticationEntryPointTest.php b/Tests/Http/EntryPoint/DigestAuthenticationEntryPointTest.php new file mode 100644 index 0000000..77537ef --- /dev/null +++ b/Tests/Http/EntryPoint/DigestAuthenticationEntryPointTest.php @@ -0,0 +1,57 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\EntryPoint; + +use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\NonceExpiredException; + +class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testStart() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + + $authenticationException = new AuthenticationException('TheAuthenticationExceptionMessage'); + + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $response = $entryPoint->start($request, $authenticationException); + + $this->assertEquals(401, $response->getStatusCode()); + $this->assertAttributeEquals('TheAuthenticationExceptionMessage', 'statusText', $response); + $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}"$/', $response->headers->get('WWW-Authenticate')); + } + + public function testStartWithNoException() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $response = $entryPoint->start($request); + + $this->assertEquals(401, $response->getStatusCode()); + $this->assertAttributeEquals('Unauthorized', 'statusText', $response); + $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}"$/', $response->headers->get('WWW-Authenticate')); + } + + public function testStartWithNonceExpiredException() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + + $nonceExpiredException = new NonceExpiredException('TheNonceExpiredExceptionMessage'); + + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $response = $entryPoint->start($request, $nonceExpiredException); + + $this->assertEquals(401, $response->getStatusCode()); + $this->assertAttributeEquals('TheNonceExpiredExceptionMessage', 'statusText', $response); + $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}", stale="true"$/', $response->headers->get('WWW-Authenticate')); + } +} diff --git a/Tests/Http/EntryPoint/FormAuthenticationEntryPointTest.php b/Tests/Http/EntryPoint/FormAuthenticationEntryPointTest.php new file mode 100644 index 0000000..d03b91c --- /dev/null +++ b/Tests/Http/EntryPoint/FormAuthenticationEntryPointTest.php @@ -0,0 +1,66 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\EntryPoint; + +use Symfony\Component\Security\Http\EntryPoint\FormAuthenticationEntryPoint; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class FormAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + public function testStart() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + $response = $this->getMock('Symfony\Component\HttpFoundation\Response'); + + $httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils'); + $httpUtils + ->expects($this->once()) + ->method('createRedirectResponse') + ->with($this->equalTo($request), $this->equalTo('/the/login/path')) + ->will($this->returnValue($response)) + ; + + $entryPoint = new FormAuthenticationEntryPoint($httpKernel, $httpUtils, '/the/login/path', false); + + $this->assertEquals($response, $entryPoint->start($request)); + } + + public function testStartWithUseForward() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + $subRequest = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + $response = $this->getMock('Symfony\Component\HttpFoundation\Response'); + + $httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils'); + $httpUtils + ->expects($this->once()) + ->method('createRequest') + ->with($this->equalTo($request), $this->equalTo('/the/login/path')) + ->will($this->returnValue($subRequest)) + ; + + $httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $httpKernel + ->expects($this->once()) + ->method('handle') + ->with($this->equalTo($request), $this->equalTo(HttpKernelInterface::SUB_REQUEST)) + ->will($this->returnValue($response)) + ; + + $entryPoint = new FormAuthenticationEntryPoint($httpKernel, $httpUtils, '/the/login/path', true); + + $this->assertEquals($response, $entryPoint->start($request)); + } +} diff --git a/Tests/Http/EntryPoint/RetryAuthenticationEntryPointTest.php b/Tests/Http/EntryPoint/RetryAuthenticationEntryPointTest.php new file mode 100644 index 0000000..6bb5724 --- /dev/null +++ b/Tests/Http/EntryPoint/RetryAuthenticationEntryPointTest.php @@ -0,0 +1,62 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\EntryPoint; + +use Symfony\Component\Security\Http\EntryPoint\RetryAuthenticationEntryPoint; +use Symfony\Component\HttpFoundation\Request; + +class RetryAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + /** + * @dataProvider dataForStart + */ + public function testStart($httpPort, $httpsPort, $request, $expectedUrl) + { + $entryPoint = new RetryAuthenticationEntryPoint($httpPort, $httpsPort); + $response = $entryPoint->start($request); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertEquals($expectedUrl, $response->headers->get('Location')); + } + + public function dataForStart() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + return array(array()); + } + + return array( + array( + 80, + 443, + Request::create('http://localhost/foo/bar?baz=bat'), + 'https://localhost/foo/bar?baz=bat' + ), + array( + 80, + 443, + Request::create('https://localhost/foo/bar?baz=bat'), + 'http://localhost/foo/bar?baz=bat' + ), + array( + 80, + 123, + Request::create('http://localhost/foo/bar?baz=bat'), + 'https://localhost:123/foo/bar?baz=bat' + ), + array( + 8080, + 443, + Request::create('https://localhost/foo/bar?baz=bat'), + 'http://localhost:8080/foo/bar?baz=bat' + ) + ); + } +} diff --git a/Tests/Http/Firewall/AccessListenerTest.php b/Tests/Http/Firewall/AccessListenerTest.php new file mode 100644 index 0000000..1750106 --- /dev/null +++ b/Tests/Http/Firewall/AccessListenerTest.php @@ -0,0 +1,214 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\Firewall; + +use Symfony\Component\Security\Http\Firewall\AccessListener; + +class AccessListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AccessDeniedException + */ + public function testHandleWhenTheAccessDecisionManagerDecidesToRefuseAccess() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + + $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(array('foo' => 'bar'), null))) + ; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->any()) + ->method('isAuthenticated') + ->will($this->returnValue(true)) + ; + + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($token)) + ; + + $accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + $accessDecisionManager + ->expects($this->once()) + ->method('decide') + ->with($this->equalTo($token), $this->equalTo(array('foo' => 'bar')), $this->equalTo($request)) + ->will($this->returnValue(false)) + ; + + $listener = new AccessListener( + $context, + $accessDecisionManager, + $accessMap, + $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface') + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + public function testHandleWhenTheTokenIsNotAuthenticated() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + + $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(array('foo' => 'bar'), null))) + ; + + $notAuthenticatedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $notAuthenticatedToken + ->expects($this->any()) + ->method('isAuthenticated') + ->will($this->returnValue(false)) + ; + + $authenticatedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $authenticatedToken + ->expects($this->any()) + ->method('isAuthenticated') + ->will($this->returnValue(true)) + ; + + $authManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $authManager + ->expects($this->once()) + ->method('authenticate') + ->with($this->equalTo($notAuthenticatedToken)) + ->will($this->returnValue($authenticatedToken)) + ; + + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($notAuthenticatedToken)) + ; + $context + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo($authenticatedToken)) + ; + + $accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + $accessDecisionManager + ->expects($this->once()) + ->method('decide') + ->with($this->equalTo($authenticatedToken), $this->equalTo(array('foo' => 'bar')), $this->equalTo($request)) + ->will($this->returnValue(true)) + ; + + $listener = new AccessListener( + $context, + $accessDecisionManager, + $accessMap, + $authManager + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + public function testHandleWhenThereIsNoAccessMapEntryMatchingTheRequest() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + + $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(null, null))) + ; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->never()) + ->method('isAuthenticated') + ; + + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($token)) + ; + + $listener = new AccessListener( + $context, + $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'), + $accessMap, + $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface') + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException + */ + public function testHandleWhenTheSecurityContextHasNoToken() + { + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + + $listener = new AccessListener( + $context, + $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'), + $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'), + $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface') + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + + $listener->handle($event); + } +} diff --git a/Tests/Http/Firewall/AnonymousAuthenticationListenerTest.php b/Tests/Http/Firewall/AnonymousAuthenticationListenerTest.php new file mode 100644 index 0000000..1dc7a4c --- /dev/null +++ b/Tests/Http/Firewall/AnonymousAuthenticationListenerTest.php @@ -0,0 +1,53 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\Firewall; + +use Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener; + +class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + public function testHandleWithContextHavingAToken() + { + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))) + ; + $context + ->expects($this->never()) + ->method('setToken') + ; + + $listener = new AnonymousAuthenticationListener($context, 'TheKey'); + $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); + } + + public function testHandleWithContextHavingNoToken() + { + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + $context + ->expects($this->once()) + ->method('setToken') + ->with(self::logicalAnd( + $this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken'), + $this->attributeEqualTo('key', 'TheKey') + )) + ; + + $listener = new AnonymousAuthenticationListener($context, 'TheKey'); + $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); + } +} diff --git a/Tests/Http/Firewall/BasicAuthenticationListenerTest.php b/Tests/Http/Firewall/BasicAuthenticationListenerTest.php new file mode 100644 index 0000000..2eb82cf --- /dev/null +++ b/Tests/Http/Firewall/BasicAuthenticationListenerTest.php @@ -0,0 +1,189 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener; +use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; + +class BasicAuthenticationListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + public function testHandleWithValidUsernameAndPasswordServerParameters() + { + $request = new Request(array(), array(), array(), array(), array(), array( + 'PHP_AUTH_USER' => 'TheUsername', + 'PHP_AUTH_PW' => 'ThePassword' + )); + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + $context + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo($token)) + ; + + $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $authenticationManager + ->expects($this->once()) + ->method('authenticate') + ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken')) + ->will($this->returnValue($token)) + ; + + $listener = new BasicAuthenticationListener( + $context, + $authenticationManager, + 'TheProviderKey', + $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface') + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + public function testHandleWhenAuthenticationFails() + { + $request = new Request(array(), array(), array(), array(), array(), array( + 'PHP_AUTH_USER' => 'TheUsername', + 'PHP_AUTH_PW' => 'ThePassword' + )); + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + $context + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo(null)) + ; + + $response = new Response(); + + $authenticationEntryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface'); + $authenticationEntryPoint + ->expects($this->any()) + ->method('start') + ->with($this->equalTo($request), $this->isInstanceOf('Symfony\Component\Security\Core\Exception\AuthenticationException')) + ->will($this->returnValue($response)) + ; + + $listener = new BasicAuthenticationListener( + $context, + new AuthenticationProviderManager(array($this->getMock('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface'))), + 'TheProviderKey', + $authenticationEntryPoint + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + $event + ->expects($this->once()) + ->method('setResponse') + ->with($this->equalTo($response)) + ; + + $listener->handle($event); + } + + public function testHandleWithNoUsernameServerParameter() + { + $request = new Request(); + + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->never()) + ->method('getToken') + ; + + $listener = new BasicAuthenticationListener( + $context, + $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), + 'TheProviderKey', + $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface') + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + public function testHandleWithASimilarAuthenticatedToken() + { + $request = new Request(array(), array(), array(), array(), array(), array('PHP_AUTH_USER' => 'TheUsername')); + + $token = new UsernamePasswordToken('TheUsername', 'ThePassword', 'TheProviderKey', array('ROLE_FOO')); + + $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); + $context + ->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($token)) + ; + + $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $authenticationManager + ->expects($this->never()) + ->method('authenticate') + ; + + $listener = new BasicAuthenticationListener( + $context, + $authenticationManager, + 'TheProviderKey', + $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface') + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } +} diff --git a/Tests/Http/Firewall/ChannelListenerTest.php b/Tests/Http/Firewall/ChannelListenerTest.php new file mode 100644 index 0000000..7bd044d --- /dev/null +++ b/Tests/Http/Firewall/ChannelListenerTest.php @@ -0,0 +1,187 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http\Firewall; + +use Symfony\Component\Security\Http\Firewall\ChannelListener; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpFoundation\Response; + +class ChannelListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + public function testHandleWithNotSecuredRequestAndHttpChannel() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + $request + ->expects($this->any()) + ->method('isSecure') + ->will($this->returnValue(false)) + ; + + $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(array(), 'http'))) + ; + + $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface'); + $entryPoint + ->expects($this->never()) + ->method('start') + ; + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + $event + ->expects($this->never()) + ->method('setResponse') + ; + + $listener = new ChannelListener($accessMap, $entryPoint); + $listener->handle($event); + } + + public function testHandleWithSecuredRequestAndHttpsChannel() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + $request + ->expects($this->any()) + ->method('isSecure') + ->will($this->returnValue(true)) + ; + + $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(array(), 'https'))) + ; + + $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface'); + $entryPoint + ->expects($this->never()) + ->method('start') + ; + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + $event + ->expects($this->never()) + ->method('setResponse') + ; + + $listener = new ChannelListener($accessMap, $entryPoint); + $listener->handle($event); + } + + public function testHandleWithNotSecuredRequestAndHttpsChannel() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + $request + ->expects($this->any()) + ->method('isSecure') + ->will($this->returnValue(false)) + ; + + $response = new Response(); + + $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(array(), 'https'))) + ; + + $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface'); + $entryPoint + ->expects($this->once()) + ->method('start') + ->with($this->equalTo($request)) + ->will($this->returnValue($response)) + ; + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + $event + ->expects($this->once()) + ->method('setResponse') + ->with($this->equalTo($response)) + ; + + $listener = new ChannelListener($accessMap, $entryPoint); + $listener->handle($event); + } + + public function testHandleWithSecuredRequestAndHttpChannel() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + $request + ->expects($this->any()) + ->method('isSecure') + ->will($this->returnValue(true)) + ; + + $response = new Response(); + + $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(array(), 'http'))) + ; + + $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface'); + $entryPoint + ->expects($this->once()) + ->method('start') + ->with($this->equalTo($request)) + ->will($this->returnValue($response)) + ; + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + $event + ->expects($this->once()) + ->method('setResponse') + ->with($this->equalTo($response)) + ; + + $listener = new ChannelListener($accessMap, $entryPoint); + $listener->handle($event); + } +} diff --git a/Tests/Http/Firewall/ContextListenerTest.php b/Tests/Http/Firewall/ContextListenerTest.php new file mode 100644 index 0000000..7260eb8 --- /dev/null +++ b/Tests/Http/Firewall/ContextListenerTest.php @@ -0,0 +1,119 @@ +<?php + +namespace Symfony\Test\Component\Security\Http\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\SecurityContext; +use Symfony\Component\Security\Http\Firewall\ContextListener; + +class ContextListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + + $this->securityContext = new SecurityContext( + $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), + $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface') + ); + } + + protected function tearDown() + { + unset($this->securityContext); + } + + public function testOnKernelResponseWillAddSession() + { + $session = $this->runSessionOnKernelResponse( + new UsernamePasswordToken('test1', 'pass1', 'phpunit'), + null + ); + + $token = unserialize($session->get('_security_session')); + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $token); + $this->assertEquals('test1', $token->getUsername()); + } + + public function testOnKernelResponseWillReplaceSession() + { + $session = $this->runSessionOnKernelResponse( + new UsernamePasswordToken('test1', 'pass1', 'phpunit'), + 'C:10:"serialized"' + ); + + $token = unserialize($session->get('_security_session')); + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $token); + $this->assertEquals('test1', $token->getUsername()); + } + + public function testOnKernelResponseWillRemoveSession() + { + $session = $this->runSessionOnKernelResponse( + null, + 'C:10:"serialized"' + ); + + $this->assertFalse($session->has('_security_session')); + } + + protected function runSessionOnKernelResponse($newToken, $original = null) + { + $session = new Session(new MockArraySessionStorage()); + + if ($original !== null) { + $session->set('_security_session', $original); + } + + $this->securityContext->setToken($newToken); + + $request = new Request(); + $request->setSession($session); + + $event = new FilterResponseEvent( + $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), + $request, + HttpKernelInterface::MASTER_REQUEST, + new Response() + ); + + $listener = new ContextListener($this->securityContext, array(), 'session'); + $listener->onKernelResponse($event); + + return $session; + } + + public function testOnKernelResponseWithoutSession() + { + $this->securityContext->setToken(new UsernamePasswordToken('test1', 'pass1', 'phpunit')); + $request = new Request(); + + $event = new FilterResponseEvent( + $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), + $request, + HttpKernelInterface::MASTER_REQUEST, + new Response() + ); + + $listener = new ContextListener($this->securityContext, array(), 'session'); + $listener->onKernelResponse($event); + + $this->assertFalse($request->hasSession()); + } +} diff --git a/Tests/Http/Firewall/LogoutListenerTest.php b/Tests/Http/Firewall/LogoutListenerTest.php new file mode 100644 index 0000000..d9067be --- /dev/null +++ b/Tests/Http/Firewall/LogoutListenerTest.php @@ -0,0 +1,256 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Http\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Http\Firewall\LogoutListener; + +class LogoutListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + public function testHandleUnmatchedPath() + { + list($listener, $context, $httpUtils, $options) = $this->getListener(); + + list($event, $request) = $this->getGetResponseEvent(); + + $event->expects($this->never()) + ->method('setResponse'); + + $httpUtils->expects($this->once()) + ->method('checkRequestPath') + ->with($request, $options['logout_path']) + ->will($this->returnValue(false)); + + $listener->handle($event); + } + + public function testHandleMatchedPathWithSuccessHandlerAndCsrfValidation() + { + $successHandler = $this->getSuccessHandler(); + $csrfProvider = $this->getCsrfProvider(); + + list($listener, $context, $httpUtils, $options) = $this->getListener($successHandler, $csrfProvider); + + list($event, $request) = $this->getGetResponseEvent(); + + $request->query->set('_csrf_token', $csrfToken = 'token'); + + $httpUtils->expects($this->once()) + ->method('checkRequestPath') + ->with($request, $options['logout_path']) + ->will($this->returnValue(true)); + + $csrfProvider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('logout', $csrfToken) + ->will($this->returnValue(true)); + + $successHandler->expects($this->once()) + ->method('onLogoutSuccess') + ->with($request) + ->will($this->returnValue($response = new Response())); + + $context->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token = $this->getToken())); + + $handler = $this->getHandler(); + $handler->expects($this->once()) + ->method('logout') + ->with($request, $response, $token); + + $context->expects($this->once()) + ->method('setToken') + ->with(null); + + $event->expects($this->once()) + ->method('setResponse') + ->with($response); + + $listener->addHandler($handler); + + $listener->handle($event); + } + + public function testHandleMatchedPathWithoutSuccessHandlerAndCsrfValidation() + { + list($listener, $context, $httpUtils, $options) = $this->getListener(); + + list($event, $request) = $this->getGetResponseEvent(); + + $httpUtils->expects($this->once()) + ->method('checkRequestPath') + ->with($request, $options['logout_path']) + ->will($this->returnValue(true)); + + $httpUtils->expects($this->once()) + ->method('createRedirectResponse') + ->with($request, $options['target_url']) + ->will($this->returnValue($response = new Response())); + + $context->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token = $this->getToken())); + + $handler = $this->getHandler(); + $handler->expects($this->once()) + ->method('logout') + ->with($request, $response, $token); + + $context->expects($this->once()) + ->method('setToken') + ->with(null); + + $event->expects($this->once()) + ->method('setResponse') + ->with($response); + + $listener->addHandler($handler); + + $listener->handle($event); + } + + /** + * @expectedException RuntimeException + */ + public function testSuccessHandlerReturnsNonResponse() + { + $successHandler = $this->getSuccessHandler(); + + list($listener, $context, $httpUtils, $options) = $this->getListener($successHandler); + + list($event, $request) = $this->getGetResponseEvent(); + + $httpUtils->expects($this->once()) + ->method('checkRequestPath') + ->with($request, $options['logout_path']) + ->will($this->returnValue(true)); + + $successHandler->expects($this->once()) + ->method('onLogoutSuccess') + ->with($request) + ->will($this->returnValue(null)); + + $listener->handle($event); + } + + /** + * @expectedException Symfony\Component\Security\Core\Exception\LogoutException + */ + public function testCsrfValidationFails() + { + $csrfProvider = $this->getCsrfProvider(); + + list($listener, $context, $httpUtils, $options) = $this->getListener(null, $csrfProvider); + + list($event, $request) = $this->getGetResponseEvent(); + + $request->query->set('_csrf_token', $csrfToken = 'token'); + + $httpUtils->expects($this->once()) + ->method('checkRequestPath') + ->with($request, $options['logout_path']) + ->will($this->returnValue(true)); + + $csrfProvider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('logout', $csrfToken) + ->will($this->returnValue(false)); + + $listener->handle($event); + } + + private function getCsrfProvider() + { + return $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + } + + private function getContext() + { + return $this->getMockBuilder('Symfony\Component\Security\Core\SecurityContext') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getGetResponseEvent() + { + $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + + $event->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request = new Request())); + + return array($event, $request); + } + + private function getHandler() + { + return $this->getMock('Symfony\Component\Security\Http\Logout\LogoutHandlerInterface'); + } + + private function getHttpUtils() + { + return $this->getMockBuilder('Symfony\Component\Security\Http\HttpUtils') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getListener($successHandler = null, $csrfProvider = null) + { + $listener = new LogoutListener( + $context = $this->getContext(), + $httpUtils = $this->getHttpUtils(), + $options = array( + 'csrf_parameter' => '_csrf_token', + 'intention' => 'logout', + 'logout_path' => '/logout', + 'target_url' => '/', + ), + $successHandler, + $csrfProvider + ); + + return array($listener, $context, $httpUtils, $options); + } + + private function getSuccessHandler() + { + return $this->getMock('Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface'); + } + + private function getToken() + { + return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } +} diff --git a/Tests/Http/Firewall/RememberMeListenerTest.php b/Tests/Http/Firewall/RememberMeListenerTest.php new file mode 100644 index 0000000..e6cc10d --- /dev/null +++ b/Tests/Http/Firewall/RememberMeListenerTest.php @@ -0,0 +1,199 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Http\Firewall; + +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Firewall\RememberMeListener; +use Symfony\Component\HttpFoundation\Request; + +class RememberMeListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + public function testOnCoreSecurityDoesNotTryToPopulateNonEmptySecurityContext() + { + list($listener, $context, $service,,) = $this->getListener(); + + $context + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))) + ; + + $context + ->expects($this->never()) + ->method('setToken') + ; + + $this->assertNull($listener->handle($this->getGetResponseEvent())); + } + + public function testOnCoreSecurityDoesNothingWhenNoCookieIsSet() + { + list($listener, $context, $service,,) = $this->getListener(); + + $context + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + + $service + ->expects($this->once()) + ->method('autoLogin') + ->will($this->returnValue(null)) + ; + + $event = $this->getGetResponseEvent(); + $event + ->expects($this->once()) + ->method('getRequest') + ->will($this->returnValue(new Request())) + ; + + $this->assertNull($listener->handle($event)); + } + + public function testOnCoreSecurityIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation() + { + list($listener, $context, $service, $manager,) = $this->getListener(); + + $context + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + + $service + ->expects($this->once()) + ->method('autoLogin') + ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))) + ; + + $service + ->expects($this->once()) + ->method('loginFail') + ; + + $exception = new AuthenticationException('Authentication failed.'); + $manager + ->expects($this->once()) + ->method('authenticate') + ->will($this->throwException($exception)) + ; + + $event = $this->getGetResponseEvent(); + $event + ->expects($this->once()) + ->method('getRequest') + ->will($this->returnValue(new Request())) + ; + + $listener->handle($event); + } + + public function testOnCoreSecurity() + { + list($listener, $context, $service, $manager,) = $this->getListener(); + + $context + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $service + ->expects($this->once()) + ->method('autoLogin') + ->will($this->returnValue($token)) + ; + + $context + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo($token)) + ; + + $manager + ->expects($this->once()) + ->method('authenticate') + ->will($this->returnValue($token)) + ; + + $event = $this->getGetResponseEvent(); + $event + ->expects($this->once()) + ->method('getRequest') + ->will($this->returnValue(new Request())) + ; + + $listener->handle($event); + } + + protected function getGetResponseEvent() + { + return $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + } + + protected function getFilterResponseEvent() + { + return $this->getMock('Symfony\Component\HttpKernel\Event\FilterResponseEvent', array(), array(), '', false); + } + + protected function getListener() + { + $listener = new RememberMeListener( + $context = $this->getContext(), + $service = $this->getService(), + $manager = $this->getManager(), + $logger = $this->getLogger() + ); + + return array($listener, $context, $service, $manager, $logger); + } + + protected function getLogger() + { + return $this->getMock('Symfony\Component\HttpKernel\Log\LoggerInterface'); + } + + protected function getManager() + { + return $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + } + + protected function getService() + { + return $this->getMock('Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface'); + } + + protected function getContext() + { + return $this->getMockBuilder('Symfony\Component\Security\Core\SecurityContext') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/Tests/Http/FirewallMapTest.php b/Tests/Http/FirewallMapTest.php new file mode 100644 index 0000000..923a9f0 --- /dev/null +++ b/Tests/Http/FirewallMapTest.php @@ -0,0 +1,119 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http; + +use Symfony\Component\Security\Http\FirewallMap; +use Symfony\Component\HttpFoundation\Request; + +class FirewallMapTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testGetListeners() + { + $map = new FirewallMap(); + + $request = new Request(); + + $notMatchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher'); + $notMatchingMatcher + ->expects($this->once()) + ->method('matches') + ->with($this->equalTo($request)) + ->will($this->returnValue(false)) + ; + + $map->add($notMatchingMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'))); + + $matchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher'); + $matchingMatcher + ->expects($this->once()) + ->method('matches') + ->with($this->equalTo($request)) + ->will($this->returnValue(true)) + ; + $theListener = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'); + $theException = $this->getMock('Symfony\Component\Security\Http\Firewall\ExceptionListener', array(), array(), '', false); + + $map->add($matchingMatcher, array($theListener), $theException); + + $tooLateMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher'); + $tooLateMatcher + ->expects($this->never()) + ->method('matches') + ; + + $map->add($tooLateMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'))); + + list($listeners, $exception) = $map->getListeners($request); + + $this->assertEquals(array($theListener), $listeners); + $this->assertEquals($theException, $exception); + } + + public function testGetListenersWithAnEntryHavingNoRequestMatcher() + { + $map = new FirewallMap(); + + $request = new Request(); + + $notMatchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher'); + $notMatchingMatcher + ->expects($this->once()) + ->method('matches') + ->with($this->equalTo($request)) + ->will($this->returnValue(false)) + ; + + $map->add($notMatchingMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'))); + + $theListener = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'); + $theException = $this->getMock('Symfony\Component\Security\Http\Firewall\ExceptionListener', array(), array(), '', false); + + $map->add(null, array($theListener), $theException); + + $tooLateMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher'); + $tooLateMatcher + ->expects($this->never()) + ->method('matches') + ; + + $map->add($tooLateMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'))); + + list($listeners, $exception) = $map->getListeners($request); + + $this->assertEquals(array($theListener), $listeners); + $this->assertEquals($theException, $exception); + } + + public function testGetListenersWithNoMatchingEntry() + { + $map = new FirewallMap(); + + $request = new Request(); + + $notMatchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher'); + $notMatchingMatcher + ->expects($this->once()) + ->method('matches') + ->with($this->equalTo($request)) + ->will($this->returnValue(false)) + ; + + $map->add($notMatchingMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'))); + + list($listeners, $exception) = $map->getListeners($request); + + $this->assertEquals(array(), $listeners); + $this->assertNull($exception); + } +} diff --git a/Tests/Http/FirewallTest.php b/Tests/Http/FirewallTest.php new file mode 100644 index 0000000..d3ad1e2 --- /dev/null +++ b/Tests/Http/FirewallTest.php @@ -0,0 +1,114 @@ +<?php + +namespace Symfony\Component\Security\Tests\Http; + +use Symfony\Component\Security\Http\Firewall; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class FirewallTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + public function testOnKernelRequestRegistersExceptionListener() + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + + $listener = $this->getMock('Symfony\Component\Security\Http\Firewall\ExceptionListener', array(), array(), '', false); + $listener + ->expects($this->once()) + ->method('register') + ->with($this->equalTo($dispatcher)) + ; + + $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false); + + $map = $this->getMock('Symfony\Component\Security\Http\FirewallMapInterface'); + $map + ->expects($this->once()) + ->method('getListeners') + ->with($this->equalTo($request)) + ->will($this->returnValue(array(array(), $listener))) + ; + + $event = new GetResponseEvent($this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), $request, HttpKernelInterface::MASTER_REQUEST); + + $firewall = new Firewall($map, $dispatcher); + $firewall->onKernelRequest($event); + } + + public function testOnKernelRequestStopsWhenThereIsAResponse() + { + $response = $this->getMock('Symfony\Component\HttpFoundation\Response'); + + $first = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'); + $first + ->expects($this->once()) + ->method('handle') + ; + + $second = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface'); + $second + ->expects($this->never()) + ->method('handle') + ; + + $map = $this->getMock('Symfony\Component\Security\Http\FirewallMapInterface'); + $map + ->expects($this->once()) + ->method('getListeners') + ->will($this->returnValue(array(array($first, $second), null))) + ; + + $event = $this->getMock( + 'Symfony\Component\HttpKernel\Event\GetResponseEvent', + array('hasResponse'), + array( + $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), + $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false), + HttpKernelInterface::MASTER_REQUEST + ) + ); + $event + ->expects($this->once()) + ->method('hasResponse') + ->will($this->returnValue(true)) + ; + + $firewall = new Firewall($map, $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface')); + $firewall->onKernelRequest($event); + } + + public function testOnKernelRequestWithSubRequest() + { + $map = $this->getMock('Symfony\Component\Security\Http\FirewallMapInterface'); + $map + ->expects($this->never()) + ->method('getListeners') + ; + + $event = new GetResponseEvent( + $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), + $this->getMock('Symfony\Component\HttpFoundation\Request'), + HttpKernelInterface::SUB_REQUEST + ); + + $firewall = new Firewall($map, $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface')); + $firewall->onKernelRequest($event); + + $this->assertFalse($event->hasResponse()); + } +} diff --git a/Tests/Http/HttpUtilsTest.php b/Tests/Http/HttpUtilsTest.php new file mode 100644 index 0000000..9771355 --- /dev/null +++ b/Tests/Http/HttpUtilsTest.php @@ -0,0 +1,151 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Http; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; + +class HttpUtilsTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + if (!class_exists('Symfony\Component\Routing\Router')) { + $this->markTestSkipped('The "Routing" component is not available'); + } + } + + public function testCreateRedirectResponse() + { + $utils = new HttpUtils($this->getRouter()); + + // absolute path + $response = $utils->createRedirectResponse($this->getRequest(), '/foobar'); + $this->assertTrue($response->isRedirect('http://localhost/foobar')); + $this->assertEquals(302, $response->getStatusCode()); + + // absolute URL + $response = $utils->createRedirectResponse($this->getRequest(), 'http://symfony.com/'); + $this->assertTrue($response->isRedirect('http://symfony.com/')); + + // route name + $utils = new HttpUtils($router = $this->getMockBuilder('Symfony\Component\Routing\Router')->disableOriginalConstructor()->getMock()); + $router + ->expects($this->any()) + ->method('generate') + ->with('foobar', array(), true) + ->will($this->returnValue('http://localhost/foo/bar')) + ; + $router + ->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext'))) + ; + $response = $utils->createRedirectResponse($this->getRequest(), 'foobar'); + $this->assertTrue($response->isRedirect('http://localhost/foo/bar')); + } + + public function testCreateRequest() + { + $utils = new HttpUtils($this->getRouter()); + + // absolute path + $request = $this->getRequest(); + $request->server->set('Foo', 'bar'); + $subRequest = $utils->createRequest($request, '/foobar'); + + $this->assertEquals('GET', $subRequest->getMethod()); + $this->assertEquals('/foobar', $subRequest->getPathInfo()); + $this->assertEquals('bar', $subRequest->server->get('Foo')); + + // route name + $utils = new HttpUtils($router = $this->getMockBuilder('Symfony\Component\Routing\Router')->disableOriginalConstructor()->getMock()); + $router + ->expects($this->once()) + ->method('generate') + ->will($this->returnValue('/foo/bar')) + ; + $router + ->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext'))) + ; + $subRequest = $utils->createRequest($this->getRequest(), 'foobar'); + $this->assertEquals('/foo/bar', $subRequest->getPathInfo()); + + // absolute URL + $subRequest = $utils->createRequest($this->getRequest(), 'http://symfony.com/'); + $this->assertEquals('/', $subRequest->getPathInfo()); + } + + public function testCheckRequestPath() + { + $utils = new HttpUtils($this->getRouter()); + + $this->assertTrue($utils->checkRequestPath($this->getRequest(), '/')); + $this->assertFalse($utils->checkRequestPath($this->getRequest(), '/foo')); + + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->any()) + ->method('match') + ->will($this->throwException(new ResourceNotFoundException())) + ; + $utils = new HttpUtils($router); + $this->assertFalse($utils->checkRequestPath($this->getRequest(), 'foobar')); + + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->any()) + ->method('match') + ->will($this->returnValue(array('_route' => 'foobar'))) + ; + $utils = new HttpUtils($router); + $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo/bar'), 'foobar')); + } + + /** + * @expectedException \RuntimeException + */ + public function testCheckRequestPathWithRouterLoadingException() + { + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->any()) + ->method('match') + ->will($this->throwException(new \RuntimeException())) + ; + $utils = new HttpUtils($router); + $utils->checkRequestPath($this->getRequest(), 'foobar'); + } + + private function getRouter() + { + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->any()) + ->method('generate') + ->will($this->returnValue('/foo/bar')) + ; + + return $router; + } + + private function getRequest($path = '/') + { + return Request::create($path, 'get'); + } +} diff --git a/Tests/Http/Logout/CookieClearingLogoutHandlerTest.php b/Tests/Http/Logout/CookieClearingLogoutHandlerTest.php new file mode 100644 index 0000000..b32a813 --- /dev/null +++ b/Tests/Http/Logout/CookieClearingLogoutHandlerTest.php @@ -0,0 +1,56 @@ +<?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\Tests\Http\Logout; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler; + +class CookieClearingLogoutHandlerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testLogout() + { + $request = new Request(); + $response = new Response(); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $handler = new CookieClearingLogoutHandler(array('foo' => array('path' => '/foo', 'domain' => 'foo.foo'), 'foo2' => array('path' => null, 'domain' => null))); + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + + $handler->logout($request, $response, $token); + + $cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertCount(2, $cookies); + + $cookie = $cookies['foo.foo']['/foo']['foo']; + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('/foo', $cookie->getPath()); + $this->assertEquals('foo.foo', $cookie->getDomain()); + $this->assertTrue($cookie->isCleared()); + + $cookie = $cookies['']['/']['foo2']; + $this->assertStringStartsWith('foo2', $cookie->getName()); + $this->assertEquals('/', $cookie->getPath()); + $this->assertNull($cookie->getDomain()); + $this->assertTrue($cookie->isCleared()); + } +} diff --git a/Tests/Http/Logout/SessionLogoutHandlerTest.php b/Tests/Http/Logout/SessionLogoutHandlerTest.php new file mode 100644 index 0000000..8e2dd28 --- /dev/null +++ b/Tests/Http/Logout/SessionLogoutHandlerTest.php @@ -0,0 +1,47 @@ +<?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\Tests\Http\Logout; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Http\Logout\SessionLogoutHandler; + +class SessionLogoutHandlerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testLogout() + { + $handler = new SessionLogoutHandler(); + + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $response = new Response(); + $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array(), array(), '', false); + + $request + ->expects($this->once()) + ->method('getSession') + ->will($this->returnValue($session)) + ; + + $session + ->expects($this->once()) + ->method('invalidate') + ; + + $handler->logout($request, $response, $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + } +} diff --git a/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php b/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php new file mode 100644 index 0000000..2c80c2e --- /dev/null +++ b/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php @@ -0,0 +1,268 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Http\RememberMe; + +use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testGetRememberMeParameter() + { + $service = $this->getService(null, array('remember_me_parameter' => 'foo')); + + $this->assertEquals('foo', $service->getRememberMeParameter()); + } + + public function testGetKey() + { + $service = $this->getService(); + $this->assertEquals('fookey', $service->getKey()); + } + + public function testAutoLoginReturnsNullWhenNoCookie() + { + $service = $this->getService(null, array('name' => 'foo')); + + $this->assertNull($service->autoLogin(new Request())); + } + + /** + * @expectedException \RuntimeException + */ + public function testAutoLoginThrowsExceptionWhenImplementationDoesNotReturnUserInterface() + { + $service = $this->getService(null, array('name' => 'foo')); + $request = new Request; + $request->cookies->set('foo', 'foo'); + + $service + ->expects($this->once()) + ->method('processAutoLoginCookie') + ->will($this->returnValue(null)) + ; + + $service->autoLogin($request); + } + + public function testAutoLogin() + { + $service = $this->getService(null, array('name' => 'foo')); + $request = new Request(); + $request->cookies->set('foo', 'foo'); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue(array())) + ; + + $service + ->expects($this->once()) + ->method('processAutoLoginCookie') + ->will($this->returnValue($user)) + ; + + $returnedToken = $service->autoLogin($request); + + $this->assertSame($user, $returnedToken->getUser()); + $this->assertSame('fookey', $returnedToken->getKey()); + $this->assertSame('fookey', $returnedToken->getProviderKey()); + } + + public function testLogout() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null)); + $request = new Request(); + $response = new Response(); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $service->logout($request, $response, $token); + + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testLoginFail() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null)); + $request = new Request(); + + $service->loginFail($request); + + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testLoginSuccessIsNotProcessedWhenTokenDoesNotContainUserInterfaceImplementation() + { + $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true)); + $request = new Request; + $response = new Response; + $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue('foo')) + ; + + $service + ->expects($this->never()) + ->method('onLoginSuccess') + ; + + $this->assertFalse($request->request->has('foo')); + + $service->loginSuccess($request, $response, $token); + } + + public function testLoginSuccessIsNotProcessedWhenRememberMeIsNotRequested() + { + $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo')); + $request = new Request; + $response = new Response; + $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($account)) + ; + + $service + ->expects($this->never()) + ->method('onLoginSuccess') + ->will($this->returnValue(null)) + ; + + $this->assertFalse($request->request->has('foo')); + + $service->loginSuccess($request, $response, $token); + } + + public function testLoginSuccessWhenRememberMeAlwaysIsTrue() + { + $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true)); + $request = new Request; + $response = new Response; + $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($account)) + ; + + $service + ->expects($this->once()) + ->method('onLoginSuccess') + ->will($this->returnValue(null)) + ; + + $service->loginSuccess($request, $response, $token); + } + + /** + * @dataProvider getPositiveRememberMeParameterValues + */ + public function testLoginSuccessWhenRememberMeParameterWithPathIsPositive($value) + { + $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo[bar]')); + + $request = new Request; + $request->request->set('foo', array('bar' => $value)); + $response = new Response; + $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($account)) + ; + + $service + ->expects($this->once()) + ->method('onLoginSuccess') + ->will($this->returnValue(true)) + ; + + $service->loginSuccess($request, $response, $token); + } + + /** + * @dataProvider getPositiveRememberMeParameterValues + */ + public function testLoginSuccessWhenRememberMeParameterIsPositive($value) + { + $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo')); + + $request = new Request; + $request->request->set('foo', $value); + $response = new Response; + $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($account)) + ; + + $service + ->expects($this->once()) + ->method('onLoginSuccess') + ->will($this->returnValue(true)) + ; + + $service->loginSuccess($request, $response, $token); + } + + public function getPositiveRememberMeParameterValues() + { + return array( + array('true'), + array('1'), + array('on'), + array('yes'), + ); + } + + protected function getService($userProvider = null, $options = array(), $logger = null) + { + if (null === $userProvider) { + $userProvider = $this->getProvider(); + } + + return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array( + array($userProvider), 'fookey', 'fookey', $options, $logger + )); + } + + protected function getProvider() + { + $provider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $provider + ->expects($this->any()) + ->method('supportsClass') + ->will($this->returnValue(true)) + ; + + return $provider; + } +} diff --git a/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php new file mode 100644 index 0000000..b37df91 --- /dev/null +++ b/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -0,0 +1,335 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Http\RememberMe; + +use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; + +use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; +use Symfony\Component\Security\Core\Exception\TokenNotFoundException; +use Symfony\Component\Security\Core\Exception\CookieTheftException; + +class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testAutoLoginReturnsNullWhenNoCookie() + { + $service = $this->getService(null, array('name' => 'foo')); + + $this->assertNull($service->autoLogin(new Request())); + } + + public function testAutoLoginThrowsExceptionOnInvalidCookie() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => false, 'remember_me_parameter' => 'foo')); + $request = new Request; + $request->request->set('foo', 'true'); + $request->cookies->set('foo', 'foo'); + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testAutoLoginThrowsExceptionOnNonExistentToken() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => false, 'remember_me_parameter' => 'foo')); + $request = new Request; + $request->request->set('foo', 'true'); + $request->cookies->set('foo', $this->encodeCookie(array( + $series = 'fooseries', + $tokenValue = 'foovalue', + ))); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->once()) + ->method('loadTokenBySeries') + ->will($this->throwException(new TokenNotFoundException('Token not found.'))) + ; + $service->setTokenProvider($tokenProvider); + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testAutoLoginReturnsNullOnNonExistentUser() + { + $userProvider = $this->getProvider(); + $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600, 'secure' => false, 'httponly' => false)); + $request = new Request; + $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue'))); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->once()) + ->method('loadTokenBySeries') + ->will($this->returnValue(new PersistentToken('fooclass', 'fooname', 'fooseries', 'foovalue', new \DateTime()))) + ; + $service->setTokenProvider($tokenProvider); + + $userProvider + ->expects($this->once()) + ->method('loadUserByUsername') + ->will($this->throwException(new UsernameNotFoundException('user not found'))) + ; + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); + } + + public function testAutoLoginThrowsExceptionOnStolenCookieAndRemovesItFromThePersistentBackend() + { + $userProvider = $this->getProvider(); + $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true)); + $request = new Request; + $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue'))); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $service->setTokenProvider($tokenProvider); + + $tokenProvider + ->expects($this->once()) + ->method('loadTokenBySeries') + ->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'anotherFooValue', new \DateTime()))) + ; + + $tokenProvider + ->expects($this->once()) + ->method('deleteTokenBySeries') + ->with($this->equalTo('fooseries')) + ->will($this->returnValue(null)) + ; + + try { + $service->autoLogin($request); + $this->fail('Expected CookieTheftException was not thrown.'); + } catch (CookieTheftException $theft) { } + + $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); + } + + public function testAutoLoginDoesNotAcceptAnExpiredCookie() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600)); + $request = new Request; + $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue'))); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->once()) + ->method('loadTokenBySeries') + ->with($this->equalTo('fooseries')) + ->will($this->returnValue(new PersistentToken('fooclass', 'username', 'fooseries', 'foovalue', new \DateTime('yesterday')))) + ; + $service->setTokenProvider($tokenProvider); + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); + } + + public function testAutoLogin() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue(array('ROLE_FOO'))) + ; + + $userProvider = $this->getProvider(); + $userProvider + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foouser')) + ->will($this->returnValue($user)) + ; + + $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'secure' => false, 'httponly' => false, 'always_remember_me' => true, 'lifetime' => 3600)); + $request = new Request; + $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue'))); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->once()) + ->method('loadTokenBySeries') + ->with($this->equalTo('fooseries')) + ->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'foovalue', new \DateTime()))) + ; + $service->setTokenProvider($tokenProvider); + + $returnedToken = $service->autoLogin($request); + + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); + $this->assertSame($user, $returnedToken->getUser()); + $this->assertEquals('fookey', $returnedToken->getKey()); + $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); + } + + public function testLogout() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo')); + $request = new Request(); + $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue'))); + $response = new Response(); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->once()) + ->method('deleteTokenBySeries') + ->with($this->equalTo('fooseries')) + ->will($this->returnValue(null)) + ; + $service->setTokenProvider($tokenProvider); + + $service->logout($request, $response, $token); + + $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME); + $this->assertTrue($cookie->isCleared()); + $this->assertEquals('/foo', $cookie->getPath()); + $this->assertEquals('foodomain.foo', $cookie->getDomain()); + } + + public function testLogoutSimplyIgnoresNonSetRequestCookie() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null)); + $request = new Request; + $response = new Response; + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->never()) + ->method('deleteTokenBySeries') + ; + $service->setTokenProvider($tokenProvider); + + $service->logout($request, $response, $token); + + $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME); + $this->assertTrue($cookie->isCleared()); + $this->assertEquals('/', $cookie->getPath()); + $this->assertNull($cookie->getDomain()); + } + + public function testLogoutSimplyIgnoresInvalidCookie() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null)); + $request = new Request; + $request->cookies->set('foo', 'somefoovalue'); + $response = new Response; + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->never()) + ->method('deleteTokenBySeries') + ; + $service->setTokenProvider($tokenProvider); + + $service->logout($request, $response, $token); + + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testLoginFail() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null)); + $request = new Request(); + + $this->assertFalse($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); + $service->loginFail($request); + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testLoginSuccessSetsCookieWhenLoggedInWithNonRememberMeTokenInterfaceImplementation() + { + $service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true)); + $request = new Request; + $response = new Response; + + $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $account + ->expects($this->once()) + ->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)) + ; + + $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface'); + $tokenProvider + ->expects($this->once()) + ->method('createNewToken') + ; + $service->setTokenProvider($tokenProvider); + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + + $service->loginSuccess($request, $response, $token); + + $cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $cookie = $cookies['myfoodomain.foo']['/foo/path']['foo']; + $this->assertFalse($cookie->isCleared()); + $this->assertTrue($cookie->isSecure()); + $this->assertTrue($cookie->isHttpOnly()); + $this->assertTrue($cookie->getExpiresTime() > time() + 3590 && $cookie->getExpiresTime() < time() + 3610); + $this->assertEquals('myfoodomain.foo', $cookie->getDomain()); + $this->assertEquals('/foo/path', $cookie->getPath()); + } + + protected function encodeCookie(array $parts) + { + $service = $this->getService(); + $r = new \ReflectionMethod($service, 'encodeCookie'); + $r->setAccessible(true); + + return $r->invoke($service, $parts); + } + + protected function getService($userProvider = null, $options = array(), $logger = null) + { + if (null === $userProvider) { + $userProvider = $this->getProvider(); + } + + return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); + } + + protected function getProvider() + { + $provider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $provider + ->expects($this->any()) + ->method('supportsClass') + ->will($this->returnValue(true)) + ; + + return $provider; + } +} diff --git a/Tests/Http/RememberMe/TokenBasedRememberMeServicesTest.php b/Tests/Http/RememberMe/TokenBasedRememberMeServicesTest.php new file mode 100644 index 0000000..9933c4f --- /dev/null +++ b/Tests/Http/RememberMe/TokenBasedRememberMeServicesTest.php @@ -0,0 +1,280 @@ +<?php + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\Security\Tests\Http\RememberMe; + +use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; + +use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; +use Symfony\Component\Security\Core\Authentication\Token\Token; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices; + +class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testAutoLoginReturnsNullWhenNoCookie() + { + $service = $this->getService(null, array('name' => 'foo')); + + $this->assertNull($service->autoLogin(new Request())); + } + + public function testAutoLoginThrowsExceptionOnInvalidCookie() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => false, 'remember_me_parameter' => 'foo')); + $request = new Request; + $request->request->set('foo', 'true'); + $request->cookies->set('foo', 'foo'); + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testAutoLoginThrowsExceptionOnNonExistentUser() + { + $userProvider = $this->getProvider(); + $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600)); + $request = new Request; + $request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time()+3600, 'foopass')); + + $userProvider + ->expects($this->once()) + ->method('loadUserByUsername') + ->will($this->throwException(new UsernameNotFoundException('user not found'))) + ; + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testAutoLoginDoesNotAcceptCookieWithInvalidHash() + { + $userProvider = $this->getProvider(); + $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600)); + $request = new Request; + $request->cookies->set('foo', base64_encode('class:'.base64_encode('foouser').':123456789:fooHash')); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('foopass')) + ; + + $userProvider + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foouser')) + ->will($this->returnValue($user)) + ; + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testAutoLoginDoesNotAcceptAnExpiredCookie() + { + $userProvider = $this->getProvider(); + $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600)); + $request = new Request; + $request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() - 1, 'foopass')); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('foopass')) + ; + + $userProvider + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foouser')) + ->will($this->returnValue($user)) + ; + + $this->assertNull($service->autoLogin($request)); + $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); + } + + public function testAutoLogin() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue(array('ROLE_FOO'))) + ; + $user + ->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('foopass')) + ; + + $userProvider = $this->getProvider(); + $userProvider + ->expects($this->once()) + ->method('loadUserByUsername') + ->with($this->equalTo('foouser')) + ->will($this->returnValue($user)) + ; + + $service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600)); + $request = new Request; + $request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time()+3600, 'foopass')); + + $returnedToken = $service->autoLogin($request); + + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); + $this->assertSame($user, $returnedToken->getUser()); + $this->assertEquals('fookey', $returnedToken->getKey()); + } + + public function testLogout() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null)); + $request = new Request(); + $response = new Response(); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $service->logout($request, $response, $token); + + $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME); + $this->assertTrue($cookie->isCleared()); + $this->assertEquals('/', $cookie->getPath()); + $this->assertNull($cookie->getDomain()); + } + + public function testLoginFail() + { + $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo')); + $request = new Request(); + $response = new Response(); + + $service->loginFail($request, $response); + + $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME); + $this->assertTrue($cookie->isCleared()); + $this->assertEquals('/foo', $cookie->getPath()); + $this->assertEquals('foodomain.foo', $cookie->getDomain()); + } + + public function testLoginSuccessIgnoresTokensWhichDoNotContainAnUserInterfaceImplementation() + { + $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true)); + $request = new Request; + $response = new Response; + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue('foo')) + ; + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + + $service->loginSuccess($request, $response, $token); + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + } + + public function testLoginSuccess() + { + $service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true)); + $request = new Request; + $response = new Response; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user + ->expects($this->once()) + ->method('getPassword') + ->will($this->returnValue('foopass')) + ; + $user + ->expects($this->once()) + ->method('getUsername') + ->will($this->returnValue('foouser')) + ; + $token + ->expects($this->atLeastOnce()) + ->method('getUser') + ->will($this->returnValue($user)) + ; + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + + $service->loginSuccess($request, $response, $token); + + $cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $cookie = $cookies['myfoodomain.foo']['/foo/path']['foo']; + $this->assertFalse($cookie->isCleared()); + $this->assertTrue($cookie->isSecure()); + $this->assertTrue($cookie->isHttpOnly()); + $this->assertTrue($cookie->getExpiresTime() > time() + 3590 && $cookie->getExpiresTime() < time() + 3610); + $this->assertEquals('myfoodomain.foo', $cookie->getDomain()); + $this->assertEquals('/foo/path', $cookie->getPath()); + } + + protected function getCookie($class, $username, $expires, $password) + { + $service = $this->getService(); + $r = new \ReflectionMethod($service, 'generateCookieValue'); + $r->setAccessible(true); + + return $r->invoke($service, $class, $username, $expires, $password); + } + + protected function encodeCookie(array $parts) + { + $service = $this->getService(); + $r = new \ReflectionMethod($service, 'encodeCookie'); + $r->setAccessible(true); + + return $r->invoke($service, $parts); + } + + protected function getService($userProvider = null, $options = array(), $logger = null) + { + if (null === $userProvider) { + $userProvider = $this->getProvider(); + } + + $service = new TokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); + + return $service; + } + + protected function getProvider() + { + $provider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $provider + ->expects($this->any()) + ->method('supportsClass') + ->will($this->returnValue(true)) + ; + + return $provider; + } +} diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php new file mode 100644 index 0000000..b154988 --- /dev/null +++ b/Tests/bootstrap.php @@ -0,0 +1,44 @@ +<?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. + */ + +spl_autoload_register(function ($class) { + foreach (array( + 'SYMFONY_HTTP_FOUNDATION' => 'HttpFoundation', + 'SYMFONY_HTTP_KERNEL' => 'HttpKernel', + 'SYMFONY_EVENT_DISPATCHER' => 'EventDispatcher', + 'SYMFONY_FORM' => 'Form', + 'SYMFONY_ROUTING' => 'Routing', + ) as $env => $name) { + if (isset($_SERVER[$env]) && 0 === strpos(ltrim($class, '/'), 'Symfony\Component\\'.$name)) { + if (file_exists($file = $_SERVER[$env].'/'.substr(str_replace('\\', '/', $class), strlen('Symfony\Component\\'.$name)).'.php')) { + require_once $file; + } + } + } + + if (isset($_SERVER['DOCTRINE_DBAL']) && 0 === strpos(ltrim($class, '/'), 'Doctrine\DBAL')) { + if (file_exists($file = $_SERVER['DOCTRINE_DBAL'].'/lib/'.str_replace('\\', '/', $class).'.php')) { + require_once $file; + } + } + + if (isset($_SERVER['DOCTRINE_COMMON']) && 0 === strpos(ltrim($class, '/'), 'Doctrine\Common')) { + if (file_exists($file = $_SERVER['DOCTRINE_COMMON'].'/lib/'.str_replace('\\', '/', $class).'.php')) { + require_once $file; + } + } + + if (0 === strpos(ltrim($class, '/'), 'Symfony\Component\Security')) { + if (file_exists($file = __DIR__.'/../'.substr(str_replace('\\', '/', $class), strlen('Symfony\Component\Security')).'.php')) { + require_once $file; + } + } +}); diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..126ba6f --- /dev/null +++ b/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="tests/bootstrap.php" +> + <testsuites> + <testsuite name="Symfony Security Component Test Suite"> + <directory>./Tests/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory>./</directory> + <exclude> + <directory>./Resources</directory> + <directory>./Tests</directory> + </exclude> + </whitelist> + </filter> +</phpunit> |