summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes2
-rw-r--r--.gitignore2
-rw-r--r--Acl/Dbal/AclProvider.php3
-rw-r--r--Acl/Dbal/MutableAclProvider.php10
-rw-r--r--Acl/Dbal/Schema.php39
-rw-r--r--Acl/Domain/Acl.php16
-rw-r--r--Acl/Domain/AclCollectionCache.php8
-rw-r--r--Acl/Domain/AuditLogger.php2
-rw-r--r--Acl/Domain/DoctrineAclCache.php2
-rw-r--r--Acl/Domain/Entry.php5
-rw-r--r--Acl/Domain/FieldEntry.php1
-rw-r--r--Acl/Domain/ObjectIdentity.php6
-rw-r--r--Acl/Domain/PermissionGrantingStrategy.php2
-rw-r--r--Acl/Domain/RoleSecurityIdentity.php1
-rw-r--r--Acl/Domain/SecurityIdentityRetrievalStrategy.php2
-rw-r--r--Acl/Domain/UserSecurityIdentity.php5
-rw-r--r--Acl/Exception/NotAllAclsFoundException.php1
-rw-r--r--Acl/Model/AclCacheInterface.php5
-rw-r--r--Acl/Model/AuditLoggerInterface.php1
-rw-r--r--Acl/Model/AuditableAclInterface.php5
-rw-r--r--Acl/Model/MutableAclInterface.php17
-rw-r--r--Acl/Model/MutableAclProviderInterface.php2
-rw-r--r--Acl/Model/SecurityIdentityInterface.php1
-rw-r--r--Acl/Permission/MaskBuilder.php7
-rw-r--r--Acl/Resources/bin/generateSql.php6
-rw-r--r--Acl/Resources/schema/db2.sql12
-rw-r--r--Acl/Resources/schema/drizzle.sql21
-rw-r--r--Acl/Resources/schema/mssql.sql22
-rw-r--r--Acl/Resources/schema/mysql.sql22
-rw-r--r--Acl/Resources/schema/oracle.sql12
-rw-r--r--Acl/Resources/schema/postgresql.sql12
-rw-r--r--Acl/Resources/schema/sqlite.sql8
-rw-r--r--CHANGELOG.md27
-rw-r--r--Core/Authentication/AuthenticationProviderManager.php19
-rw-r--r--Core/Authentication/AuthenticationTrustResolver.php2
-rw-r--r--Core/Authentication/Provider/AuthenticationProviderInterface.php2
-rw-r--r--Core/Authentication/Provider/RememberMeAuthenticationProvider.php6
-rw-r--r--Core/Authentication/Provider/UserAuthenticationProvider.php30
-rw-r--r--Core/Authentication/RememberMe/InMemoryTokenProvider.php6
-rw-r--r--Core/Authentication/RememberMe/PersistentToken.php14
-rw-r--r--Core/Authentication/RememberMe/PersistentTokenInterface.php4
-rw-r--r--Core/Authentication/RememberMe/TokenProviderInterface.php10
-rw-r--r--Core/Authentication/Token/AbstractToken.php49
-rw-r--r--Core/Authentication/Token/RememberMeToken.php2
-rw-r--r--Core/AuthenticationEvents.php19
-rw-r--r--Core/Authorization/Voter/AuthenticatedVoter.php2
-rw-r--r--Core/Encoder/EncoderFactory.php8
-rw-r--r--Core/Encoder/EncoderFactoryInterface.php8
-rw-r--r--Core/Encoder/MessageDigestPasswordEncoder.php1
-rw-r--r--Core/Encoder/PlaintextPasswordEncoder.php5
-rw-r--r--Core/Event/AuthenticationEvent.php35
-rw-r--r--Core/Event/AuthenticationFailureEvent.php37
-rw-r--r--Core/Exception/CookieTheftException.php4
-rw-r--r--Core/Exception/InvalidCsrfTokenException.php6
-rw-r--r--Core/Exception/LogoutException.php25
-rw-r--r--Core/Exception/SessionUnavailableException.php2
-rw-r--r--Core/SecurityContextInterface.php8
-rw-r--r--Core/User/ChainUserProvider.php6
-rw-r--r--Core/User/EquatableInterface.php37
-rw-r--r--Core/User/User.php40
-rw-r--r--Core/User/UserInterface.php15
-rw-r--r--Core/Util/ClassUtils.php55
-rw-r--r--Core/Validator/Constraint/UserPassword.php27
-rw-r--r--Core/Validator/Constraint/UserPasswordValidator.php46
-rw-r--r--Http/AccessMap.php2
-rw-r--r--Http/AccessMapInterface.php33
-rw-r--r--Http/Authentication/AuthenticationFailureHandlerInterface.php8
-rw-r--r--Http/Authentication/AuthenticationSuccessHandlerInterface.php8
-rw-r--r--Http/Authentication/DefaultAuthenticationFailureHandler.php87
-rw-r--r--Http/Authentication/DefaultAuthenticationSuccessHandler.php111
-rw-r--r--Http/Authorization/AccessDeniedHandlerInterface.php6
-rw-r--r--Http/Event/InteractiveLoginEvent.php16
-rw-r--r--Http/Firewall.php2
-rw-r--r--Http/Firewall/AbstractAuthenticationListener.php88
-rw-r--r--Http/Firewall/AccessListener.php4
-rw-r--r--Http/Firewall/ChannelListener.php4
-rw-r--r--Http/Firewall/ContextListener.php31
-rw-r--r--Http/Firewall/ExceptionListener.php26
-rw-r--r--Http/Firewall/LogoutListener.php72
-rw-r--r--Http/Firewall/RememberMeListener.php18
-rw-r--r--Http/Firewall/SwitchUserListener.php16
-rw-r--r--Http/Firewall/UsernamePasswordFormAuthenticationListener.php18
-rw-r--r--Http/FirewallMap.php18
-rw-r--r--Http/FirewallMapInterface.php6
-rw-r--r--Http/HttpUtils.php73
-rw-r--r--Http/Logout/CookieClearingLogoutHandler.php2
-rw-r--r--Http/Logout/DefaultLogoutSuccessHandler.php47
-rw-r--r--Http/Logout/LogoutHandlerInterface.php2
-rw-r--r--Http/Logout/LogoutSuccessHandlerInterface.php6
-rw-r--r--Http/Logout/SessionLogoutHandler.php2
-rw-r--r--Http/RememberMe/AbstractRememberMeServices.php30
-rw-r--r--Http/RememberMe/PersistentTokenBasedRememberMeServices.php20
-rw-r--r--Http/RememberMe/RememberMeServicesInterface.php4
-rw-r--r--Http/RememberMe/ResponseListener.php32
-rw-r--r--Http/RememberMe/TokenBasedRememberMeServices.php18
-rw-r--r--Http/Session/SessionAuthenticationStrategy.php6
-rw-r--r--Http/Session/SessionAuthenticationStrategyInterface.php8
-rw-r--r--README.md16
-rw-r--r--Tests/Acl/Dbal/AclProviderBenchmarkTest.php271
-rw-r--r--Tests/Acl/Dbal/AclProviderTest.php267
-rw-r--r--Tests/Acl/Dbal/MutableAclProviderTest.php492
-rw-r--r--Tests/Acl/Domain/AclTest.php521
-rw-r--r--Tests/Acl/Domain/AuditLoggerTest.php83
-rw-r--r--Tests/Acl/Domain/DoctrineAclCacheTest.php108
-rw-r--r--Tests/Acl/Domain/EntryTest.php119
-rw-r--r--Tests/Acl/Domain/FieldEntryTest.php74
-rw-r--r--Tests/Acl/Domain/ObjectIdentityRetrievalStrategyTest.php41
-rw-r--r--Tests/Acl/Domain/ObjectIdentityTest.php116
-rw-r--r--Tests/Acl/Domain/PermissionGrantingStrategyTest.php192
-rw-r--r--Tests/Acl/Domain/RoleSecurityIdentityTest.php55
-rw-r--r--Tests/Acl/Domain/SecurityIdentityRetrievalStrategyTest.php196
-rw-r--r--Tests/Acl/Domain/UserSecurityIdentityTest.php73
-rw-r--r--Tests/Acl/Permission/BasicPermissionMapTest.php23
-rw-r--r--Tests/Acl/Permission/MaskBuilderTest.php103
-rw-r--r--Tests/Acl/Voter/AclVoterTest.php405
-rw-r--r--Tests/Core/Authentication/AuthenticationProviderManagerTest.php138
-rw-r--r--Tests/Core/Authentication/AuthenticationTrustResolverTest.php72
-rw-r--r--Tests/Core/Authentication/Provider/AnonymousAuthenticationProviderTest.php66
-rw-r--r--Tests/Core/Authentication/Provider/DaoAuthenticationProviderTest.php300
-rw-r--r--Tests/Core/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php133
-rw-r--r--Tests/Core/Authentication/Provider/RememberMeAuthenticationProviderTest.php111
-rw-r--r--Tests/Core/Authentication/Provider/UserAuthenticationProviderTest.php206
-rw-r--r--Tests/Core/Authentication/RememberMe/InMemoryTokenProviderTest.php63
-rw-r--r--Tests/Core/Authentication/RememberMe/PersistentTokenTest.php29
-rw-r--r--Tests/Core/Authentication/Token/AbstractTokenTest.php244
-rw-r--r--Tests/Core/Authentication/Token/AnonymousTokenTest.php45
-rw-r--r--Tests/Core/Authentication/Token/PreAuthenticatedTokenTest.php48
-rw-r--r--Tests/Core/Authentication/Token/RememerMeTokenTest.php83
-rw-r--r--Tests/Core/Authentication/Token/UsernamePasswordTokenTest.php58
-rw-r--r--Tests/Core/Authorization/AccessDecisionManagerTest.php151
-rw-r--r--Tests/Core/Authorization/Voter/AuthenticatedVoterTest.php78
-rw-r--r--Tests/Core/Authorization/Voter/RoleHierarchyVoterTest.php36
-rw-r--r--Tests/Core/Authorization/Voter/RoleVoterTest.php62
-rw-r--r--Tests/Core/Encoder/BasePasswordEncoderTest.php85
-rw-r--r--Tests/Core/Encoder/EncoderFactoryTest.php94
-rw-r--r--Tests/Core/Encoder/MessageDigestPasswordEncoderTest.php45
-rw-r--r--Tests/Core/Encoder/PlaintextPasswordEncoderTest.php39
-rw-r--r--Tests/Core/Role/RoleHierarchyTest.php32
-rw-r--r--Tests/Core/Role/RoleTest.php24
-rw-r--r--Tests/Core/Role/SwitchUserRoleTest.php31
-rw-r--r--Tests/Core/SecurityContextTest.php92
-rw-r--r--Tests/Core/User/AccountCheckerTest.php108
-rw-r--r--Tests/Core/User/ChainUserProviderTest.php185
-rw-r--r--Tests/Core/User/InMemoryProviderTest.php62
-rw-r--r--Tests/Core/User/UserTest.php126
-rw-r--r--Tests/Core/Util/ClassUtilsTest.php41
-rw-r--r--Tests/Http/EntryPoint/BasicAuthenticationEntryPointTest.php52
-rw-r--r--Tests/Http/EntryPoint/DigestAuthenticationEntryPointTest.php66
-rw-r--r--Tests/Http/EntryPoint/FormAuthenticationEntryPointTest.php75
-rw-r--r--Tests/Http/EntryPoint/RetryAuthenticationEntryPointTest.php71
-rw-r--r--Tests/Http/Firewall/AccessListenerTest.php223
-rw-r--r--Tests/Http/Firewall/AnonymousAuthenticationListenerTest.php62
-rw-r--r--Tests/Http/Firewall/BasicAuthenticationListenerTest.php198
-rw-r--r--Tests/Http/Firewall/ChannelListenerTest.php196
-rw-r--r--Tests/Http/Firewall/ContextListenerTest.php171
-rw-r--r--Tests/Http/Firewall/LogoutListenerTest.php258
-rw-r--r--Tests/Http/Firewall/RememberMeListenerTest.php199
-rw-r--r--Tests/Http/FirewallMapTest.php128
-rw-r--r--Tests/Http/FirewallTest.php123
-rw-r--r--Tests/Http/HttpUtilsTest.php151
-rw-r--r--Tests/Http/Logout/CookieClearingLogoutHandlerTest.php56
-rw-r--r--Tests/Http/Logout/SessionLogoutHandlerTest.php47
-rw-r--r--Tests/Http/RememberMe/AbstractRememberMeServicesTest.php268
-rw-r--r--Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php335
-rw-r--r--Tests/Http/RememberMe/TokenBasedRememberMeServicesTest.php280
-rw-r--r--Tests/bootstrap.php22
-rw-r--r--composer.json33
-rw-r--r--phpunit.xml.dist29
168 files changed, 10184 insertions, 542 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..8048151
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+/Tests export-ignore
+phpunit.xml.dist export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d1502b0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+vendor/
+composer.lock
diff --git a/Acl/Dbal/AclProvider.php b/Acl/Dbal/AclProvider.php
index b14b6bf..ada4f22 100644
--- a/Acl/Dbal/AclProvider.php
+++ b/Acl/Dbal/AclProvider.php
@@ -348,7 +348,6 @@ QUERY;
* This method is called when an ACL instance is retrieved from the cache.
*
* @param AclInterface $acl
- * @return void
*/
private function updateAceIdentityMap(AclInterface $acl)
{
@@ -397,8 +396,6 @@ QUERY;
* map to ensure every ACE only gets instantiated once.
*
* @param array &$aces
- *
- * @return void
*/
private function doUpdateAceIdentityMap(array &$aces)
{
diff --git a/Acl/Dbal/MutableAclProvider.php b/Acl/Dbal/MutableAclProvider.php
index 242c2a9..9a20f61 100644
--- a/Acl/Dbal/MutableAclProvider.php
+++ b/Acl/Dbal/MutableAclProvider.php
@@ -17,7 +17,6 @@ use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException;
use Symfony\Component\Security\Acl\Exception\ConcurrentModificationException;
-use Symfony\Component\Security\Acl\Exception\Exception;
use Symfony\Component\Security\Acl\Model\AclCacheInterface;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\EntryInterface;
@@ -148,7 +147,6 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
* @param string $propertyName
* @param mixed $oldValue
* @param mixed $newValue
- * @return void
*/
public function propertyChanged($sender, $propertyName, $oldValue, $newValue)
{
@@ -643,7 +641,6 @@ QUERY;
* Creates the ACL for the passed object identity
*
* @param ObjectIdentityInterface $oid
- * @return void
*/
private function createObjectIdentity(ObjectIdentityInterface $oid)
{
@@ -695,7 +692,6 @@ QUERY;
* Deletes all ACEs for the given object identity primary key.
*
* @param integer $oidPK
- * @return void
*/
private function deleteAccessControlEntries($oidPK)
{
@@ -706,7 +702,6 @@ QUERY;
* Deletes the object identity from the database.
*
* @param integer $pk
- * @return void
*/
private function deleteObjectIdentity($pk)
{
@@ -717,7 +712,6 @@ QUERY;
* Deletes all entries from the relations table from the database.
*
* @param integer $pk
- * @return void
*/
private function deleteObjectIdentityRelations($pk)
{
@@ -728,7 +722,6 @@ QUERY;
* This regenerates the ancestor table which is used for fast read access.
*
* @param AclInterface $acl
- * @return void
*/
private function regenerateAncestorRelations(AclInterface $acl)
{
@@ -749,7 +742,6 @@ QUERY;
*
* @param string $name
* @param array $changes
- * @return void
*/
private function updateFieldAceProperty($name, array $changes)
{
@@ -806,7 +798,6 @@ QUERY;
*
* @param string $name
* @param array $changes
- * @return void
*/
private function updateAceProperty($name, array $changes)
{
@@ -860,7 +851,6 @@ QUERY;
* Persists the changes which were made to ACEs to the database.
*
* @param \SplObjectStorage $aces
- * @return void
*/
private function updateAces(\SplObjectStorage $aces)
{
diff --git a/Acl/Dbal/Schema.php b/Acl/Dbal/Schema.php
index dd8cf08..fd25926 100644
--- a/Acl/Dbal/Schema.php
+++ b/Acl/Dbal/Schema.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Acl\Dbal;
use Doctrine\DBAL\Schema\Schema as BaseSchema;
+use Doctrine\DBAL\Connection;
/**
* The schema used for the ACL system.
@@ -25,12 +26,14 @@ final class Schema extends BaseSchema
/**
* Constructor
*
- * @param array $options the names for tables
- * @return void
+ * @param array $options the names for tables
+ * @param Connection $connection
*/
- public function __construct(array $options)
+ public function __construct(array $options, Connection $connection = null)
{
- parent::__construct();
+ $schemaConfig = null === $connection ? null : $connection->getSchemaManager()->createSchemaConfig();
+
+ parent::__construct(array(), array(), $schemaConfig);
$this->options = $options;
@@ -42,9 +45,23 @@ final class Schema extends BaseSchema
}
/**
- * Adds the class table to the schema
+ * Merges ACL schema with the given schema.
*
- * @return void
+ * @param BaseSchema $schema
+ */
+ public function addToSchema(BaseSchema $schema)
+ {
+ foreach ($this->getTables() as $table) {
+ $schema->_addTable($table);
+ }
+
+ foreach ($this->getSequences() as $sequence) {
+ $schema->_addSequence($sequence);
+ }
+ }
+
+ /**
+ * Adds the class table to the schema
*/
protected function addClassTable()
{
@@ -57,8 +74,6 @@ final class Schema extends BaseSchema
/**
* Adds the entry table to the schema
- *
- * @return void
*/
protected function addEntryTable()
{
@@ -87,8 +102,6 @@ final class Schema extends BaseSchema
/**
* Adds the object identity table to the schema
- *
- * @return void
*/
protected function addObjectIdentitiesTable()
{
@@ -104,13 +117,11 @@ final class Schema extends BaseSchema
$table->addUniqueIndex(array('object_identifier', 'class_id'));
$table->addIndex(array('parent_object_identity_id'));
- $table->addForeignKeyConstraint($table, array('parent_object_identity_id'), array('id'), array('onDelete' => 'RESTRICT', 'onUpdate' => 'RESTRICT'));
+ $table->addForeignKeyConstraint($table, array('parent_object_identity_id'), array('id'));
}
/**
* Adds the object identity relation table to the schema
- *
- * @return void
*/
protected function addObjectIdentityAncestorsTable()
{
@@ -128,8 +139,6 @@ final class Schema extends BaseSchema
/**
* Adds the security identity table to the schema
- *
- * @return void
*/
protected function addSecurityIdentitiesTable()
{
diff --git a/Acl/Domain/Acl.php b/Acl/Domain/Acl.php
index 32b0faf..4665c0e 100644
--- a/Acl/Domain/Acl.php
+++ b/Acl/Domain/Acl.php
@@ -54,7 +54,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param PermissionGrantingStrategyInterface $permissionGrantingStrategy
* @param array $loadedSids
* @param Boolean $entriesInheriting
- * @return void
*/
public function __construct($id, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSids = array(), $entriesInheriting)
{
@@ -75,7 +74,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* Adds a property changed listener
*
* @param PropertyChangedListener $listener
- * @return void
*/
public function addPropertyChangedListener(PropertyChangedListener $listener)
{
@@ -281,7 +279,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* Implementation for the \Serializable interface
*
* @param string $serialized
- * @return void
*/
public function unserialize($serialized)
{
@@ -313,9 +310,9 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
/**
* {@inheritDoc}
*/
- public function setParentAcl(AclInterface $acl)
+ public function setParentAcl(AclInterface $acl = null)
{
- if (null === $acl->getId()) {
+ if (null !== $acl && null === $acl->getId()) {
throw new \InvalidArgumentException('$acl must have an ID.');
}
@@ -403,7 +400,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param string $property
* @param integer $index
* @throws \OutOfBoundsException
- * @return void
*/
private function deleteAce($property, $index)
{
@@ -429,7 +425,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param integer $index
* @param string $field
* @throws \OutOfBoundsException
- * @return void
*/
private function deleteFieldAce($property, $index, $field)
{
@@ -459,7 +454,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param string $strategy
* @throws \OutOfBoundsException
* @throws \InvalidArgumentException
- * @return void
*/
private function insertAce($property, $index, $mask, SecurityIdentityInterface $sid, $granting, $strategy = null)
{
@@ -509,7 +503,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param string $strategy
* @throws \InvalidArgumentException
* @throws \OutOfBoundsException
- * @return void
*/
private function insertFieldAce($property, $index, $field, $mask, SecurityIdentityInterface $sid, $granting, $strategy = null)
{
@@ -563,7 +556,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param integer $mask
* @param string $strategy
* @throws \OutOfBoundsException
- * @return void
*/
private function updateAce($property, $index, $mask, $strategy = null)
{
@@ -591,7 +583,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param Boolean $auditSuccess
* @param Boolean $auditFailure
* @throws \OutOfBoundsException
- * @return void
*/
private function updateAuditing(array &$aces, $index, $auditSuccess, $auditFailure)
{
@@ -620,7 +611,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param string $strategy
* @throws \InvalidArgumentException
* @throws \OutOfBoundsException
- * @return void
*/
private function updateFieldAce($property, $index, $field, $mask, $strategy = null)
{
@@ -650,7 +640,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param string $name
* @param mixed $oldValue
* @param mixed $newValue
- * @return void
*/
private function onPropertyChanged($name, $oldValue, $newValue)
{
@@ -666,7 +655,6 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged
* @param string $name
* @param mixed $oldValue
* @param mixed $newValue
- * @return void
*/
private function onEntryPropertyChanged(EntryInterface $entry, $name, $oldValue, $newValue)
{
diff --git a/Acl/Domain/AclCollectionCache.php b/Acl/Domain/AclCollectionCache.php
index 12d7053..88c017c 100644
--- a/Acl/Domain/AclCollectionCache.php
+++ b/Acl/Domain/AclCollectionCache.php
@@ -29,10 +29,9 @@ class AclCollectionCache
/**
* Constructor.
*
- * @param AclProviderInterface $aclProvider
- * @param ObjectIdentityRetrievalStrategy $oidRetrievalStrategy
- * @param SecurityIdentityRetrievalStrategy $sidRetrievalStrategy
- * @return void
+ * @param AclProviderInterface $aclProvider
+ * @param ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy
+ * @param SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy
*/
public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy)
{
@@ -47,7 +46,6 @@ class AclCollectionCache
*
* @param mixed $collection anything that can be passed to foreach()
* @param array $tokens an array of TokenInterface implementations
- * @return void
*/
public function cache($collection, array $tokens = array())
{
diff --git a/Acl/Domain/AuditLogger.php b/Acl/Domain/AuditLogger.php
index 2a6461c..8174873 100644
--- a/Acl/Domain/AuditLogger.php
+++ b/Acl/Domain/AuditLogger.php
@@ -27,7 +27,6 @@ abstract class AuditLogger implements AuditLoggerInterface
*
* @param Boolean $granted
* @param EntryInterface $ace
- * @return void
*/
public function logIfNeeded($granted, EntryInterface $ace)
{
@@ -47,7 +46,6 @@ abstract class AuditLogger implements AuditLoggerInterface
*
* @param Boolean $granted
* @param EntryInterface $ace
- * @return void
*/
abstract protected function doLog($granted, EntryInterface $ace);
}
diff --git a/Acl/Domain/DoctrineAclCache.php b/Acl/Domain/DoctrineAclCache.php
index 21e5149..731f98c 100644
--- a/Acl/Domain/DoctrineAclCache.php
+++ b/Acl/Domain/DoctrineAclCache.php
@@ -36,8 +36,6 @@ class DoctrineAclCache implements AclCacheInterface
* @param Cache $cache
* @param PermissionGrantingStrategyInterface $permissionGrantingStrategy
* @param string $prefix
- *
- * @return void
*/
public function __construct(Cache $cache, PermissionGrantingStrategyInterface $permissionGrantingStrategy, $prefix = self::PREFIX)
{
diff --git a/Acl/Domain/Entry.php b/Acl/Domain/Entry.php
index 9a4f560..42449c4 100644
--- a/Acl/Domain/Entry.php
+++ b/Acl/Domain/Entry.php
@@ -126,7 +126,6 @@ class Entry implements AuditableEntryInterface
* AclInterface instead.
*
* @param Boolean $boolean
- * @return void
*/
public function setAuditFailure($boolean)
{
@@ -140,7 +139,6 @@ class Entry implements AuditableEntryInterface
* AclInterface instead.
*
* @param Boolean $boolean
- * @return void
*/
public function setAuditSuccess($boolean)
{
@@ -154,7 +152,6 @@ class Entry implements AuditableEntryInterface
* AclInterface instead.
*
* @param integer $mask
- * @return void
*/
public function setMask($mask)
{
@@ -168,7 +165,6 @@ class Entry implements AuditableEntryInterface
* AclInterface instead.
*
* @param string $strategy
- * @return void
*/
public function setStrategy($strategy)
{
@@ -197,7 +193,6 @@ class Entry implements AuditableEntryInterface
* Implementation of \Serializable
*
* @param string $serialized
- * @return void
*/
public function unserialize($serialized)
{
diff --git a/Acl/Domain/FieldEntry.php b/Acl/Domain/FieldEntry.php
index 4167ba4..f057367 100644
--- a/Acl/Domain/FieldEntry.php
+++ b/Acl/Domain/FieldEntry.php
@@ -36,7 +36,6 @@ class FieldEntry extends Entry implements FieldEntryInterface
* @param Boolean $granting
* @param Boolean $auditFailure
* @param Boolean $auditSuccess
- * @return void
*/
public function __construct($id, AclInterface $acl, $field, SecurityIdentityInterface $sid, $strategy, $mask, $granting, $auditFailure, $auditSuccess)
{
diff --git a/Acl/Domain/ObjectIdentity.php b/Acl/Domain/ObjectIdentity.php
index 927a1b8..da98f5e 100644
--- a/Acl/Domain/ObjectIdentity.php
+++ b/Acl/Domain/ObjectIdentity.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Security\Acl\Domain;
+use Symfony\Component\Security\Core\Util\ClassUtils;
use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException;
use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
@@ -30,7 +31,6 @@ final class ObjectIdentity implements ObjectIdentityInterface
*
* @param string $identifier
* @param string $type
- * @return void
*/
public function __construct($identifier, $type)
{
@@ -60,9 +60,9 @@ final class ObjectIdentity implements ObjectIdentityInterface
try {
if ($domainObject instanceof DomainObjectInterface) {
- return new self($domainObject->getObjectIdentifier(), get_class($domainObject));
+ return new self($domainObject->getObjectIdentifier(), ClassUtils::getRealClass($domainObject));
} elseif (method_exists($domainObject, 'getId')) {
- return new self($domainObject->getId(), get_class($domainObject));
+ return new self($domainObject->getId(), ClassUtils::getRealClass($domainObject));
}
} catch (\InvalidArgumentException $invalid) {
throw new InvalidDomainObjectException($invalid->getMessage(), 0, $invalid);
diff --git a/Acl/Domain/PermissionGrantingStrategy.php b/Acl/Domain/PermissionGrantingStrategy.php
index b145ef3..09e227c 100644
--- a/Acl/Domain/PermissionGrantingStrategy.php
+++ b/Acl/Domain/PermissionGrantingStrategy.php
@@ -16,7 +16,6 @@ use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\AuditLoggerInterface;
use Symfony\Component\Security\Acl\Model\EntryInterface;
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
-use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
/**
* The permission granting strategy to apply to the access control list.
@@ -35,7 +34,6 @@ class PermissionGrantingStrategy implements PermissionGrantingStrategyInterface
* Sets the audit logger
*
* @param AuditLoggerInterface $auditLogger
- * @return void
*/
public function setAuditLogger(AuditLoggerInterface $auditLogger)
{
diff --git a/Acl/Domain/RoleSecurityIdentity.php b/Acl/Domain/RoleSecurityIdentity.php
index 51d3d0c..0d3d0d2 100644
--- a/Acl/Domain/RoleSecurityIdentity.php
+++ b/Acl/Domain/RoleSecurityIdentity.php
@@ -27,7 +27,6 @@ final class RoleSecurityIdentity implements SecurityIdentityInterface
* Constructor
*
* @param mixed $role a Role instance, or its string representation
- * @return void
*/
public function __construct($role)
{
diff --git a/Acl/Domain/SecurityIdentityRetrievalStrategy.php b/Acl/Domain/SecurityIdentityRetrievalStrategy.php
index 67312b2..dbc0530 100644
--- a/Acl/Domain/SecurityIdentityRetrievalStrategy.php
+++ b/Acl/Domain/SecurityIdentityRetrievalStrategy.php
@@ -34,8 +34,6 @@ class SecurityIdentityRetrievalStrategy implements SecurityIdentityRetrievalStra
*
* @param RoleHierarchyInterface $roleHierarchy
* @param AuthenticationTrustResolver $authenticationTrustResolver
- *
- * @return void
*/
public function __construct(RoleHierarchyInterface $roleHierarchy, AuthenticationTrustResolver $authenticationTrustResolver)
{
diff --git a/Acl/Domain/UserSecurityIdentity.php b/Acl/Domain/UserSecurityIdentity.php
index 34051e8..ebb0056 100644
--- a/Acl/Domain/UserSecurityIdentity.php
+++ b/Acl/Domain/UserSecurityIdentity.php
@@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Acl\Domain;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\Util\ClassUtils;
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
/**
@@ -52,7 +53,7 @@ final class UserSecurityIdentity implements SecurityIdentityInterface
*/
public static function fromAccount(UserInterface $user)
{
- return new self($user->getUsername(), get_class($user));
+ return new self($user->getUsername(), ClassUtils::getRealClass($user));
}
/**
@@ -69,7 +70,7 @@ final class UserSecurityIdentity implements SecurityIdentityInterface
return self::fromAccount($user);
}
- return new self((string) $user, is_object($user)? get_class($user) : get_class($token));
+ return new self((string) $user, is_object($user) ? ClassUtils::getRealClass($user) : ClassUtils::getRealClass($token));
}
/**
diff --git a/Acl/Exception/NotAllAclsFoundException.php b/Acl/Exception/NotAllAclsFoundException.php
index 8e7a08f..3c02495 100644
--- a/Acl/Exception/NotAllAclsFoundException.php
+++ b/Acl/Exception/NotAllAclsFoundException.php
@@ -28,7 +28,6 @@ class NotAllAclsFoundException extends AclNotFoundException
* Sets the partial result
*
* @param \SplObjectStorage $result
- * @return void
*/
public function setPartialResult(\SplObjectStorage $result)
{
diff --git a/Acl/Model/AclCacheInterface.php b/Acl/Model/AclCacheInterface.php
index c353822..ea9604e 100644
--- a/Acl/Model/AclCacheInterface.php
+++ b/Acl/Model/AclCacheInterface.php
@@ -22,7 +22,6 @@ interface AclCacheInterface
* Removes an ACL from the cache
*
* @param string $primaryKey a serialized primary key
- * @return void
*/
public function evictFromCacheById($primaryKey);
@@ -32,7 +31,6 @@ interface AclCacheInterface
* The ACL which is returned, must reference the passed object identity.
*
* @param ObjectIdentityInterface $oid
- * @return void
*/
public function evictFromCacheByIdentity(ObjectIdentityInterface $oid);
@@ -56,14 +54,11 @@ interface AclCacheInterface
* Stores a new ACL in the cache
*
* @param AclInterface $acl
- * @return void
*/
public function putInCache(AclInterface $acl);
/**
* Removes all ACLs from the cache
- *
- * @return void
*/
public function clearCache();
}
diff --git a/Acl/Model/AuditLoggerInterface.php b/Acl/Model/AuditLoggerInterface.php
index 511989a..09bcbb8 100644
--- a/Acl/Model/AuditLoggerInterface.php
+++ b/Acl/Model/AuditLoggerInterface.php
@@ -24,7 +24,6 @@ interface AuditLoggerInterface
*
* @param Boolean $granted
* @param EntryInterface $ace
- * @return void
*/
public function logIfNeeded($granted, EntryInterface $ace);
}
diff --git a/Acl/Model/AuditableAclInterface.php b/Acl/Model/AuditableAclInterface.php
index b231da5..14b4c04 100644
--- a/Acl/Model/AuditableAclInterface.php
+++ b/Acl/Model/AuditableAclInterface.php
@@ -24,7 +24,6 @@ interface AuditableAclInterface extends MutableAclInterface
* @param integer $index
* @param Boolean $auditSuccess
* @param Boolean $auditFailure
- * @return void
*/
public function updateClassAuditing($index, $auditSuccess, $auditFailure);
@@ -35,9 +34,7 @@ interface AuditableAclInterface extends MutableAclInterface
* @param string $field
* @param Boolean $auditSuccess
* @param Boolean $auditFailure
- * @return void
*/
-
public function updateClassFieldAuditing($index, $field, $auditSuccess, $auditFailure);
/**
@@ -46,7 +43,6 @@ interface AuditableAclInterface extends MutableAclInterface
* @param integer $index
* @param Boolean $auditSuccess
* @param Boolean $auditFailure
- * @return void
*/
public function updateObjectAuditing($index, $auditSuccess, $auditFailure);
@@ -57,7 +53,6 @@ interface AuditableAclInterface extends MutableAclInterface
* @param string $field
* @param Boolean $auditSuccess
* @param Boolean $auditFailure
- * @return void
*/
public function updateObjectFieldAuditing($index, $field, $auditSuccess, $auditFailure);
}
diff --git a/Acl/Model/MutableAclInterface.php b/Acl/Model/MutableAclInterface.php
index 11835db..9028aa9 100644
--- a/Acl/Model/MutableAclInterface.php
+++ b/Acl/Model/MutableAclInterface.php
@@ -25,7 +25,6 @@ interface MutableAclInterface extends AclInterface
* Deletes a class-based ACE
*
* @param integer $index
- * @return void
*/
public function deleteClassAce($index);
@@ -34,7 +33,6 @@ interface MutableAclInterface extends AclInterface
*
* @param integer $index
* @param string $field
- * @return void
*/
public function deleteClassFieldAce($index, $field);
@@ -42,7 +40,6 @@ interface MutableAclInterface extends AclInterface
* Deletes an object-based ACE
*
* @param integer $index
- * @return void
*/
public function deleteObjectAce($index);
@@ -51,7 +48,6 @@ interface MutableAclInterface extends AclInterface
*
* @param integer $index
* @param string $field
- * @return void
*/
public function deleteObjectFieldAce($index, $field);
@@ -70,7 +66,6 @@ interface MutableAclInterface extends AclInterface
* @param integer $index
* @param Boolean $granting
* @param string $strategy
- * @return void
*/
public function insertClassAce(SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null);
@@ -83,7 +78,6 @@ interface MutableAclInterface extends AclInterface
* @param integer $index
* @param Boolean $granting
* @param string $strategy
- * @return void
*/
public function insertClassFieldAce($field, SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null);
@@ -95,7 +89,6 @@ interface MutableAclInterface extends AclInterface
* @param integer $index
* @param Boolean $granting
* @param string $strategy
- * @return void
*/
public function insertObjectAce(SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null);
@@ -108,7 +101,6 @@ interface MutableAclInterface extends AclInterface
* @param integer $index
* @param Boolean $granting
* @param string $strategy
- * @return void
*/
public function insertObjectFieldAce($field, SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null);
@@ -116,17 +108,16 @@ interface MutableAclInterface extends AclInterface
* Sets whether entries are inherited
*
* @param Boolean $boolean
- * @return void
*/
public function setEntriesInheriting($boolean);
/**
* Sets the parent ACL
*
- * @param AclInterface $acl
+ * @param AclInterface|null $acl
* @return void
*/
- public function setParentAcl(AclInterface $acl);
+ public function setParentAcl(AclInterface $acl = null);
/**
* Updates a class-based ACE
@@ -134,7 +125,6 @@ interface MutableAclInterface extends AclInterface
* @param integer $index
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
- * @return void
*/
public function updateClassAce($index, $mask, $strategy = null);
@@ -145,7 +135,6 @@ interface MutableAclInterface extends AclInterface
* @param string $field
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
- * @return void
*/
public function updateClassFieldAce($index, $field, $mask, $strategy = null);
@@ -155,7 +144,6 @@ interface MutableAclInterface extends AclInterface
* @param integer $index
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
- * @return void
*/
public function updateObjectAce($index, $mask, $strategy = null);
@@ -166,7 +154,6 @@ interface MutableAclInterface extends AclInterface
* @param string $field
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
- * @return void
*/
public function updateObjectFieldAce($index, $field, $mask, $strategy = null);
}
diff --git a/Acl/Model/MutableAclProviderInterface.php b/Acl/Model/MutableAclProviderInterface.php
index 94d9bcc..cb34b72 100644
--- a/Acl/Model/MutableAclProviderInterface.php
+++ b/Acl/Model/MutableAclProviderInterface.php
@@ -35,7 +35,6 @@ interface MutableAclProviderInterface extends AclProviderInterface
* want child ACLs to be deleted, you will have to set their parent ACL to null.
*
* @param ObjectIdentityInterface $oid
- * @return void
*/
public function deleteAcl(ObjectIdentityInterface $oid);
@@ -46,7 +45,6 @@ interface MutableAclProviderInterface extends AclProviderInterface
* Changes to parent ACLs are not persisted.
*
* @param MutableAclInterface $acl
- * @return void
*/
public function updateAcl(MutableAclInterface $acl);
}
diff --git a/Acl/Model/SecurityIdentityInterface.php b/Acl/Model/SecurityIdentityInterface.php
index 185b119..0a24a54 100644
--- a/Acl/Model/SecurityIdentityInterface.php
+++ b/Acl/Model/SecurityIdentityInterface.php
@@ -25,7 +25,6 @@ interface SecurityIdentityInterface
* not rely on referential equality.
*
* @param SecurityIdentityInterface $identity
- * @return void
*/
public function equals(SecurityIdentityInterface $identity);
}
diff --git a/Acl/Permission/MaskBuilder.php b/Acl/Permission/MaskBuilder.php
index 908de95..df1fa7c 100644
--- a/Acl/Permission/MaskBuilder.php
+++ b/Acl/Permission/MaskBuilder.php
@@ -73,7 +73,6 @@ class MaskBuilder
* Constructor
*
* @param integer $mask optional; defaults to 0
- * @return void
*/
public function __construct($mask = 0)
{
@@ -88,7 +87,7 @@ class MaskBuilder
* Adds a mask to the permission
*
* @param mixed $mask
- * @return PermissionBuilder
+ * @return MaskBuilder
*/
public function add($mask)
{
@@ -141,7 +140,7 @@ class MaskBuilder
* Removes a mask from the permission
*
* @param mixed $mask
- * @return PermissionBuilder
+ * @return MaskBuilder
*/
public function remove($mask)
{
@@ -159,7 +158,7 @@ class MaskBuilder
/**
* Resets the PermissionBuilder
*
- * @return PermissionBuilder
+ * @return MaskBuilder
*/
public function reset()
{
diff --git a/Acl/Resources/bin/generateSql.php b/Acl/Resources/bin/generateSql.php
index df3b761..d65874b 100644
--- a/Acl/Resources/bin/generateSql.php
+++ b/Acl/Resources/bin/generateSql.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
require_once __DIR__.'/../../../../ClassLoader/UniversalClassLoader.php';
diff --git a/Acl/Resources/schema/db2.sql b/Acl/Resources/schema/db2.sql
index de30c3b..2d10c14 100644
--- a/Acl/Resources/schema/db2.sql
+++ b/Acl/Resources/schema/db2.sql
@@ -30,14 +30,14 @@ CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
-ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE RESTRICT ON DELETE RESTRICT
+ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities(id) ON UPDATE CASCADE ON DELETE CASCADE \ No newline at end of file
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE \ No newline at end of file
diff --git a/Acl/Resources/schema/drizzle.sql b/Acl/Resources/schema/drizzle.sql
new file mode 100644
index 0000000..378f114
--- /dev/null
+++ b/Acl/Resources/schema/drizzle.sql
@@ -0,0 +1,21 @@
+CREATE TABLE acl_classes (id INT AUTO_INCREMENT NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY(id), UNIQUE INDEX UNIQ_69DD750638A36066 (class_type))
+
+CREATE TABLE acl_security_identities (id INT AUTO_INCREMENT NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL, PRIMARY KEY(id), UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 (identifier, username))
+
+CREATE TABLE acl_object_identities (id INT AUTO_INCREMENT NOT NULL, parent_object_identity_id INT DEFAULT NULL, class_id INT NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting BOOLEAN NOT NULL, PRIMARY KEY(id), UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 (object_identifier, class_id), INDEX IDX_9407E54977FA751A (parent_object_identity_id))
+
+CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id), INDEX IDX_825DE2993D9AB4A6 (object_identity_id), INDEX IDX_825DE299C671CEA1 (ancestor_id))
+
+CREATE TABLE acl_entries (id INT AUTO_INCREMENT NOT NULL, class_id INT NOT NULL, object_identity_id INT DEFAULT NULL, security_identity_id INT NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order INT NOT NULL, mask INT NOT NULL, granting BOOLEAN NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success BOOLEAN NOT NULL, audit_failure BOOLEAN NOT NULL, PRIMARY KEY(id), UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 (class_id, object_identity_id, field_name, ace_order), INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 (class_id, object_identity_id, security_identity_id), INDEX IDX_46C8B806EA000B10 (class_id), INDEX IDX_46C8B8063D9AB4A6 (object_identity_id), INDEX IDX_46C8B806DF9183C9 (security_identity_id))
+
+ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
+
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
+
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
+
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
+
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
+
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE \ No newline at end of file
diff --git a/Acl/Resources/schema/mssql.sql b/Acl/Resources/schema/mssql.sql
index 86bffe0..1b4733c 100644
--- a/Acl/Resources/schema/mssql.sql
+++ b/Acl/Resources/schema/mssql.sql
@@ -1,24 +1,24 @@
-CREATE TABLE acl_classes (id INT IDENTITY NOT NULL, class_type NVARCHAR(200) NOT NULL, PRIMARY KEY(id))
+CREATE TABLE acl_classes (id INT UNSIGNED IDENTITY NOT NULL, class_type NVARCHAR(200) NOT NULL, PRIMARY KEY (id))
CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) WHERE class_type IS NOT NULL
-CREATE TABLE acl_security_identities (id INT IDENTITY NOT NULL, identifier NVARCHAR(200) NOT NULL, username BIT NOT NULL, PRIMARY KEY(id))
+CREATE TABLE acl_security_identities (id INT UNSIGNED IDENTITY NOT NULL, identifier NVARCHAR(200) NOT NULL, username BIT NOT NULL, PRIMARY KEY (id))
CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) WHERE identifier IS NOT NULL AND username IS NOT NULL
-CREATE TABLE acl_object_identities (id INT IDENTITY NOT NULL, parent_object_identity_id INT DEFAULT NULL, class_id INT NOT NULL, object_identifier NVARCHAR(100) NOT NULL, entries_inheriting BIT NOT NULL, PRIMARY KEY(id))
+CREATE TABLE acl_object_identities (id INT UNSIGNED IDENTITY NOT NULL, parent_object_identity_id INT UNSIGNED DEFAULT NULL, class_id INT UNSIGNED NOT NULL, object_identifier NVARCHAR(100) NOT NULL, entries_inheriting BIT NOT NULL, PRIMARY KEY (id))
CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id) WHERE object_identifier IS NOT NULL AND class_id IS NOT NULL
CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id)
-CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id))
+CREATE TABLE acl_object_identity_ancestors (object_identity_id INT UNSIGNED NOT NULL, ancestor_id INT UNSIGNED NOT NULL, PRIMARY KEY (object_identity_id, ancestor_id))
CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id)
CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
-CREATE TABLE acl_entries (id INT IDENTITY NOT NULL, class_id INT NOT NULL, object_identity_id INT DEFAULT NULL, security_identity_id INT NOT NULL, field_name NVARCHAR(50) DEFAULT NULL, ace_order SMALLINT NOT NULL, mask INT NOT NULL, granting BIT NOT NULL, granting_strategy NVARCHAR(30) NOT NULL, audit_success BIT NOT NULL, audit_failure BIT NOT NULL, PRIMARY KEY(id))
+CREATE TABLE acl_entries (id INT UNSIGNED IDENTITY NOT NULL, class_id INT UNSIGNED NOT NULL, object_identity_id INT UNSIGNED DEFAULT NULL, security_identity_id INT UNSIGNED NOT NULL, field_name NVARCHAR(50) DEFAULT NULL, ace_order SMALLINT UNSIGNED NOT NULL, mask INT NOT NULL, granting BIT NOT NULL, granting_strategy NVARCHAR(30) NOT NULL, audit_success BIT NOT NULL, audit_failure BIT NOT NULL, PRIMARY KEY (id))
CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order) WHERE class_id IS NOT NULL AND object_identity_id IS NOT NULL AND field_name IS NOT NULL AND ace_order IS NOT NULL
@@ -30,14 +30,14 @@ CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
-ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE RESTRICT ON DELETE RESTRICT
+ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities(id) ON UPDATE CASCADE ON DELETE CASCADE \ No newline at end of file
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE \ No newline at end of file
diff --git a/Acl/Resources/schema/mysql.sql b/Acl/Resources/schema/mysql.sql
index 0d2d50f..db01e95 100644
--- a/Acl/Resources/schema/mysql.sql
+++ b/Acl/Resources/schema/mysql.sql
@@ -1,21 +1,21 @@
-CREATE TABLE acl_classes (id INT AUTO_INCREMENT NOT NULL, class_type VARCHAR(200) NOT NULL, UNIQUE INDEX UNIQ_69DD750638A36066 (class_type), PRIMARY KEY(id)) ENGINE = InnoDB
+CREATE TABLE acl_classes (id INT UNSIGNED AUTO_INCREMENT NOT NULL, class_type VARCHAR(200) NOT NULL, UNIQUE INDEX UNIQ_69DD750638A36066 (class_type), PRIMARY KEY(id)) ENGINE = InnoDB
-CREATE TABLE acl_security_identities (id INT AUTO_INCREMENT NOT NULL, identifier VARCHAR(200) NOT NULL, username TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 (identifier, username), PRIMARY KEY(id)) ENGINE = InnoDB
+CREATE TABLE acl_security_identities (id INT UNSIGNED AUTO_INCREMENT NOT NULL, identifier VARCHAR(200) NOT NULL, username TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 (identifier, username), PRIMARY KEY(id)) ENGINE = InnoDB
-CREATE TABLE acl_object_identities (id INT AUTO_INCREMENT NOT NULL, parent_object_identity_id INT DEFAULT NULL, class_id INT NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 (object_identifier, class_id), INDEX IDX_9407E54977FA751A (parent_object_identity_id), PRIMARY KEY(id)) ENGINE = InnoDB
+CREATE TABLE acl_object_identities (id INT UNSIGNED AUTO_INCREMENT NOT NULL, parent_object_identity_id INT UNSIGNED DEFAULT NULL, class_id INT UNSIGNED NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 (object_identifier, class_id), INDEX IDX_9407E54977FA751A (parent_object_identity_id), PRIMARY KEY(id)) ENGINE = InnoDB
-CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, INDEX IDX_825DE2993D9AB4A6 (object_identity_id), INDEX IDX_825DE299C671CEA1 (ancestor_id), PRIMARY KEY(object_identity_id, ancestor_id)) ENGINE = InnoDB
+CREATE TABLE acl_object_identity_ancestors (object_identity_id INT UNSIGNED NOT NULL, ancestor_id INT UNSIGNED NOT NULL, INDEX IDX_825DE2993D9AB4A6 (object_identity_id), INDEX IDX_825DE299C671CEA1 (ancestor_id), PRIMARY KEY(object_identity_id, ancestor_id)) ENGINE = InnoDB
-CREATE TABLE acl_entries (id INT AUTO_INCREMENT NOT NULL, class_id INT NOT NULL, object_identity_id INT DEFAULT NULL, security_identity_id INT NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order SMALLINT NOT NULL, mask INT NOT NULL, granting TINYINT(1) NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success TINYINT(1) NOT NULL, audit_failure TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 (class_id, object_identity_id, field_name, ace_order), INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 (class_id, object_identity_id, security_identity_id), INDEX IDX_46C8B806EA000B10 (class_id), INDEX IDX_46C8B8063D9AB4A6 (object_identity_id), INDEX IDX_46C8B806DF9183C9 (security_identity_id), PRIMARY KEY(id)) ENGINE = InnoDB
+CREATE TABLE acl_entries (id INT UNSIGNED AUTO_INCREMENT NOT NULL, class_id INT UNSIGNED NOT NULL, object_identity_id INT UNSIGNED DEFAULT NULL, security_identity_id INT UNSIGNED NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order SMALLINT UNSIGNED NOT NULL, mask INT NOT NULL, granting TINYINT(1) NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success TINYINT(1) NOT NULL, audit_failure TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 (class_id, object_identity_id, field_name, ace_order), INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 (class_id, object_identity_id, security_identity_id), INDEX IDX_46C8B806EA000B10 (class_id), INDEX IDX_46C8B8063D9AB4A6 (object_identity_id), INDEX IDX_46C8B806DF9183C9 (security_identity_id), PRIMARY KEY(id)) ENGINE = InnoDB
-ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE RESTRICT ON DELETE RESTRICT
+ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities(id) ON UPDATE CASCADE ON DELETE CASCADE \ No newline at end of file
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE \ No newline at end of file
diff --git a/Acl/Resources/schema/oracle.sql b/Acl/Resources/schema/oracle.sql
index 3c4afcc..9896cf0 100644
--- a/Acl/Resources/schema/oracle.sql
+++ b/Acl/Resources/schema/oracle.sql
@@ -162,14 +162,14 @@ CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
-ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities(id) ON DELETE RESTRICT
+ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities(id) ON DELETE CASCADE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes(id) ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON DELETE CASCADE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities(id) ON DELETE CASCADE \ No newline at end of file
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON DELETE CASCADE \ No newline at end of file
diff --git a/Acl/Resources/schema/postgresql.sql b/Acl/Resources/schema/postgresql.sql
index 0d59c46..05ca439 100644
--- a/Acl/Resources/schema/postgresql.sql
+++ b/Acl/Resources/schema/postgresql.sql
@@ -30,14 +30,14 @@ CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
-ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE RESTRICT ON DELETE RESTRICT NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) NOT DEFERRABLE INITIALLY IMMEDIATE
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
-ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
-ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE \ No newline at end of file
+ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE \ No newline at end of file
diff --git a/Acl/Resources/schema/sqlite.sql b/Acl/Resources/schema/sqlite.sql
index 784df73..21c9f67 100644
--- a/Acl/Resources/schema/sqlite.sql
+++ b/Acl/Resources/schema/sqlite.sql
@@ -1,12 +1,12 @@
-CREATE TABLE acl_classes (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, class_type VARCHAR(200) NOT NULL)
+CREATE TABLE acl_classes (id INTEGER NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY("id"))
CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type)
-CREATE TABLE acl_security_identities (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL)
+CREATE TABLE acl_security_identities (id INTEGER NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL, PRIMARY KEY("id"))
CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username)
-CREATE TABLE acl_object_identities (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_object_identity_id INTEGER DEFAULT NULL, class_id INTEGER NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting BOOLEAN NOT NULL)
+CREATE TABLE acl_object_identities (id INTEGER NOT NULL, parent_object_identity_id INTEGER DEFAULT NULL, class_id INTEGER NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting BOOLEAN NOT NULL, PRIMARY KEY("id"))
CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id)
@@ -18,7 +18,7 @@ CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_ident
CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
-CREATE TABLE acl_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, class_id INTEGER NOT NULL, object_identity_id INTEGER DEFAULT NULL, security_identity_id INTEGER NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order INTEGER NOT NULL, mask INTEGER NOT NULL, granting BOOLEAN NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success BOOLEAN NOT NULL, audit_failure BOOLEAN NOT NULL)
+CREATE TABLE acl_entries (id INTEGER NOT NULL, class_id INTEGER NOT NULL, object_identity_id INTEGER DEFAULT NULL, security_identity_id INTEGER NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order INTEGER NOT NULL, mask INTEGER NOT NULL, granting BOOLEAN NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success BOOLEAN NOT NULL, audit_failure BOOLEAN NOT NULL, PRIMARY KEY("id"))
CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order)
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c570ac3
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,27 @@
+CHANGELOG
+=========
+
+2.1.0
+-----
+
+ * [BC BREAK] The signature of ExceptionListener has changed
+ * changed the HttpUtils constructor signature to take a UrlGenerator and a UrlMatcher instead of a Router
+ * EncoderFactoryInterface::getEncoder() can now also take a class name as an argument
+ * allow switching to the user that is already impersonated
+ * added support for the remember_me parameter in the query
+ * added AccessMapInterface
+ * [BC BREAK] moved user comparison logic out of UserInterface
+ * made the logout path check configurable
+ * after login, the user is now redirected to `default_target_path` if
+ `use_referer` is true and the referrer is the `login_path`.
+ * added a way to remove a token from a session
+ * [BC BREAK] changed `MutableAclInterface::setParentAcl` to accept `null`,
+ review your implementation to reflect this change.
+ * `ObjectIdentity::fromDomainObject`, `UserSecurityIdentity::fromAccount` and
+ `UserSecurityIdentity::fromToken` now return correct identities for proxies
+ objects (e.g. Doctrine proxies)
+ * [BC BREAK] moved the default authentication success and failure handling to
+ separate classes. The order of arguments in the constructor of the
+ `AbstractAuthenticationListener` has changed.
+ * [BC BREAK] moved the default logout success handling to a separate class. The
+ order of arguments in the constructor of `LogoutListener` has changed.
diff --git a/Core/Authentication/AuthenticationProviderManager.php b/Core/Authentication/AuthenticationProviderManager.php
index a82b9fb..7ca46c0 100644
--- a/Core/Authentication/AuthenticationProviderManager.php
+++ b/Core/Authentication/AuthenticationProviderManager.php
@@ -11,6 +11,10 @@
namespace Symfony\Component\Security\Core\Authentication;
+use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
+use Symfony\Component\Security\Core\Event\AuthenticationEvent;
+use Symfony\Component\Security\Core\AuthenticationEvents;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
@@ -22,11 +26,13 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
* instances to authenticate a Token.
*
* @author Fabien Potencier <fabien@symfony.com>
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AuthenticationProviderManager implements AuthenticationManagerInterface
{
private $providers;
private $eraseCredentials;
+ private $eventDispatcher;
/**
* Constructor.
@@ -44,6 +50,11 @@ class AuthenticationProviderManager implements AuthenticationManagerInterface
$this->eraseCredentials = (Boolean) $eraseCredentials;
}
+ public function setEventDispatcher(EventDispatcherInterface $dispatcher)
+ {
+ $this->eventDispatcher = $dispatcher;
+ }
+
/**
* {@inheritdoc}
*/
@@ -77,6 +88,10 @@ class AuthenticationProviderManager implements AuthenticationManagerInterface
$result->eraseCredentials();
}
+ if (null !== $this->eventDispatcher) {
+ $this->eventDispatcher->dispatch(AuthenticationEvents::AUTHENTICATION_SUCCESS, new AuthenticationEvent($result));
+ }
+
return $result;
}
@@ -84,6 +99,10 @@ class AuthenticationProviderManager implements AuthenticationManagerInterface
$lastException = new ProviderNotFoundException(sprintf('No Authentication Provider found for token of class "%s".', get_class($token)));
}
+ if (null !== $this->eventDispatcher) {
+ $this->eventDispatcher->dispatch(AuthenticationEvents::AUTHENTICATION_FAILURE, new AuthenticationFailureEvent($token, $lastException));
+ }
+
$lastException->setExtraInformation($token);
throw $lastException;
diff --git a/Core/Authentication/AuthenticationTrustResolver.php b/Core/Authentication/AuthenticationTrustResolver.php
index 8ca28fb..9b3ff3d 100644
--- a/Core/Authentication/AuthenticationTrustResolver.php
+++ b/Core/Authentication/AuthenticationTrustResolver.php
@@ -28,8 +28,6 @@ class AuthenticationTrustResolver implements AuthenticationTrustResolverInterfac
*
* @param string $anonymousClass
* @param string $rememberMeClass
- *
- * @return void
*/
public function __construct($anonymousClass, $rememberMeClass)
{
diff --git a/Core/Authentication/Provider/AuthenticationProviderInterface.php b/Core/Authentication/Provider/AuthenticationProviderInterface.php
index c843216..956adf1 100644
--- a/Core/Authentication/Provider/AuthenticationProviderInterface.php
+++ b/Core/Authentication/Provider/AuthenticationProviderInterface.php
@@ -15,7 +15,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
/**
- * AuthenticationProviderInterface is the interface for for all authentication
+ * AuthenticationProviderInterface is the interface for all authentication
* providers.
*
* Concrete implementations processes specific Token instances.
diff --git a/Core/Authentication/Provider/RememberMeAuthenticationProvider.php b/Core/Authentication/Provider/RememberMeAuthenticationProvider.php
index b7f3125..4175907 100644
--- a/Core/Authentication/Provider/RememberMeAuthenticationProvider.php
+++ b/Core/Authentication/Provider/RememberMeAuthenticationProvider.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Authentication\Provider;
diff --git a/Core/Authentication/Provider/UserAuthenticationProvider.php b/Core/Authentication/Provider/UserAuthenticationProvider.php
index 23aa816..32d7971 100644
--- a/Core/Authentication/Provider/UserAuthenticationProvider.php
+++ b/Core/Authentication/Provider/UserAuthenticationProvider.php
@@ -65,26 +65,34 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
try {
$user = $this->retrieveUser($username, $token);
-
- if (!$user instanceof UserInterface) {
- throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.');
+ } catch (UsernameNotFoundException $notFound) {
+ if ($this->hideUserNotFoundExceptions) {
+ throw new BadCredentialsException('Bad credentials', 0, $notFound);
}
+ throw $notFound;
+ }
+
+ if (!$user instanceof UserInterface) {
+ throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.');
+ }
+
+ try {
$this->userChecker->checkPreAuth($user);
$this->checkAuthentication($user, $token);
$this->userChecker->checkPostAuth($user);
-
- $authenticatedToken = new UsernamePasswordToken($user, $token->getCredentials(), $this->providerKey, $user->getRoles());
- $authenticatedToken->setAttributes($token->getAttributes());
-
- return $authenticatedToken;
- } catch (UsernameNotFoundException $notFound) {
+ } catch (BadCredentialsException $e) {
if ($this->hideUserNotFoundExceptions) {
- throw new BadCredentialsException('Bad credentials', 0, $notFound);
+ throw new BadCredentialsException('Bad credentials', 0, $e);
}
- throw $notFound;
+ throw $e;
}
+
+ $authenticatedToken = new UsernamePasswordToken($user, $token->getCredentials(), $this->providerKey, $user->getRoles());
+ $authenticatedToken->setAttributes($token->getAttributes());
+
+ return $authenticatedToken;
}
/**
diff --git a/Core/Authentication/RememberMe/InMemoryTokenProvider.php b/Core/Authentication/RememberMe/InMemoryTokenProvider.php
index 4653900..a15c2b4 100644
--- a/Core/Authentication/RememberMe/InMemoryTokenProvider.php
+++ b/Core/Authentication/RememberMe/InMemoryTokenProvider.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Authentication\RememberMe;
diff --git a/Core/Authentication/RememberMe/PersistentToken.php b/Core/Authentication/RememberMe/PersistentToken.php
index d9029f5..88b0413 100644
--- a/Core/Authentication/RememberMe/PersistentToken.php
+++ b/Core/Authentication/RememberMe/PersistentToken.php
@@ -1,7 +1,5 @@
<?php
-namespace Symfony\Component\Security\Core\Authentication\RememberMe;
-
/*
* This file is part of the Symfony package.
*
@@ -11,6 +9,8 @@ namespace Symfony\Component\Security\Core\Authentication\RememberMe;
* file that was distributed with this source code.
*/
+namespace Symfony\Component\Security\Core\Authentication\RememberMe;
+
/**
* This class is only used by PersistentTokenRememberMeServices internally.
*
@@ -27,11 +27,11 @@ final class PersistentToken implements PersistentTokenInterface
/**
* Constructor
*
- * @param string $class
- * @param string $username
- * @param string $series
- * @param string $tokenValue
- * @param DateTime $lastUsed
+ * @param string $class
+ * @param string $username
+ * @param string $series
+ * @param string $tokenValue
+ * @param \DateTime $lastUsed
*/
public function __construct($class, $username, $series, $tokenValue, \DateTime $lastUsed)
{
diff --git a/Core/Authentication/RememberMe/PersistentTokenInterface.php b/Core/Authentication/RememberMe/PersistentTokenInterface.php
index 327ffe2..6e9d891 100644
--- a/Core/Authentication/RememberMe/PersistentTokenInterface.php
+++ b/Core/Authentication/RememberMe/PersistentTokenInterface.php
@@ -1,7 +1,5 @@
<?php
-namespace Symfony\Component\Security\Core\Authentication\RememberMe;
-
/*
* This file is part of the Symfony package.
*
@@ -11,6 +9,8 @@ namespace Symfony\Component\Security\Core\Authentication\RememberMe;
* file that was distributed with this source code.
*/
+namespace Symfony\Component\Security\Core\Authentication\RememberMe;
+
/**
* Interface to be implemented by persistent token classes (such as
* Doctrine entities representing a remember-me token)
diff --git a/Core/Authentication/RememberMe/TokenProviderInterface.php b/Core/Authentication/RememberMe/TokenProviderInterface.php
index 7ef60ac..0c6f75e 100644
--- a/Core/Authentication/RememberMe/TokenProviderInterface.php
+++ b/Core/Authentication/RememberMe/TokenProviderInterface.php
@@ -1,7 +1,5 @@
<?php
-namespace Symfony\Component\Security\Core\Authentication\RememberMe;
-
/*
* This file is part of the Symfony package.
*
@@ -11,6 +9,8 @@ namespace Symfony\Component\Security\Core\Authentication\RememberMe;
* file that was distributed with this source code.
*/
+namespace Symfony\Component\Security\Core\Authentication\RememberMe;
+
/**
* Interface for TokenProviders
*
@@ -39,9 +39,9 @@ interface TokenProviderInterface
/**
* Updates the token according to this data.
*
- * @param string $series
- * @param string $tokenValue
- * @param DateTime $lastUsed
+ * @param string $series
+ * @param string $tokenValue
+ * @param \DateTime $lastUsed
*/
public function updateToken($series, $tokenValue, \DateTime $lastUsed);
diff --git a/Core/Authentication/Token/AbstractToken.php b/Core/Authentication/Token/AbstractToken.php
index 8e008e5..ed6e8de 100644
--- a/Core/Authentication/Token/AbstractToken.php
+++ b/Core/Authentication/Token/AbstractToken.php
@@ -14,6 +14,8 @@ namespace Symfony\Component\Security\Core\Authentication\Token;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\User\AdvancedUserInterface;
+use Symfony\Component\Security\Core\User\EquatableInterface;
/**
* Base class for Token instances.
@@ -96,7 +98,7 @@ abstract class AbstractToken implements TokenInterface
if (!$user instanceof UserInterface) {
$changed = true;
} else {
- $changed = !$this->user->equals($user);
+ $changed = $this->hasUserChanged($user);
}
} elseif ($user instanceof UserInterface) {
$changed = true;
@@ -229,4 +231,49 @@ abstract class AbstractToken implements TokenInterface
return sprintf('%s(user="%s", authenticated=%s, roles="%s")', $class, $this->getUsername(), json_encode($this->authenticated), implode(', ', $roles));
}
+
+ private function hasUserChanged(UserInterface $user)
+ {
+ if (!($this->user instanceof UserInterface)) {
+ throw new \BadMethodCallException('Method "hasUserChanged" should be called when current user class is instance of "UserInterface".');
+ }
+
+ if ($this->user instanceof EquatableInterface) {
+ return ! (Boolean) $this->user->isEqualTo($user);
+ }
+
+ if ($this->user->getPassword() !== $user->getPassword()) {
+ return true;
+ }
+
+ if ($this->user->getSalt() !== $user->getSalt()) {
+ return true;
+ }
+
+ if ($this->user->getUsername() !== $user->getUsername()) {
+ return true;
+ }
+
+ if ($this->user instanceof AdvancedUserInterface && $user instanceof AdvancedUserInterface) {
+ if ($this->user->isAccountNonExpired() !== $user->isAccountNonExpired()) {
+ return true;
+ }
+
+ if ($this->user->isAccountNonLocked() !== $user->isAccountNonLocked()) {
+ return true;
+ }
+
+ if ($this->user->isCredentialsNonExpired() !== $user->isCredentialsNonExpired()) {
+ return true;
+ }
+
+ if ($this->user->isEnabled() !== $user->isEnabled()) {
+ return true;
+ }
+ } elseif ($this->user instanceof AdvancedUserInterface xor $user instanceof AdvancedUserInterface) {
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/Core/Authentication/Token/RememberMeToken.php b/Core/Authentication/Token/RememberMeToken.php
index 7ac9e1c..de50e5c 100644
--- a/Core/Authentication/Token/RememberMeToken.php
+++ b/Core/Authentication/Token/RememberMeToken.php
@@ -52,7 +52,7 @@ class RememberMeToken extends AbstractToken
public function setAuthenticated($authenticated)
{
if ($authenticated) {
- throw new \RuntimeException('You cannot set this token to authenticated after creation.');
+ throw new \LogicException('You cannot set this token to authenticated after creation.');
}
parent::setAuthenticated(false);
diff --git a/Core/AuthenticationEvents.php b/Core/AuthenticationEvents.php
new file mode 100644
index 0000000..1e0e6ff
--- /dev/null
+++ b/Core/AuthenticationEvents.php
@@ -0,0 +1,19 @@
+<?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\Core;
+
+final class AuthenticationEvents
+{
+ const AUTHENTICATION_SUCCESS = 'security.authentication.success';
+
+ const AUTHENTICATION_FAILURE = 'security.authentication.failure';
+}
diff --git a/Core/Authorization/Voter/AuthenticatedVoter.php b/Core/Authorization/Voter/AuthenticatedVoter.php
index d750e33..5847e0d 100644
--- a/Core/Authorization/Voter/AuthenticatedVoter.php
+++ b/Core/Authorization/Voter/AuthenticatedVoter.php
@@ -35,8 +35,6 @@ class AuthenticatedVoter implements VoterInterface
* Constructor.
*
* @param AuthenticationTrustResolverInterface $authenticationTrustResolver
- *
- * @return void
*/
public function __construct(AuthenticationTrustResolverInterface $authenticationTrustResolver)
{
diff --git a/Core/Encoder/EncoderFactory.php b/Core/Encoder/EncoderFactory.php
index 738706a..9429441 100644
--- a/Core/Encoder/EncoderFactory.php
+++ b/Core/Encoder/EncoderFactory.php
@@ -11,8 +11,6 @@
namespace Symfony\Component\Security\Core\Encoder;
-use Symfony\Component\Security\Core\User\UserInterface;
-
/**
* A generic encoder factory implementation
*
@@ -30,10 +28,10 @@ class EncoderFactory implements EncoderFactoryInterface
/**
* {@inheritDoc}
*/
- public function getEncoder(UserInterface $user)
+ public function getEncoder($user)
{
foreach ($this->encoders as $class => $encoder) {
- if (!$user instanceof $class) {
+ if ((is_object($user) && !$user instanceof $class) || (!is_object($user) && !is_subclass_of($user, $class) && $user != $class)) {
continue;
}
@@ -44,7 +42,7 @@ class EncoderFactory implements EncoderFactoryInterface
return $this->encoders[$class];
}
- throw new \RuntimeException(sprintf('No encoder has been configured for account "%s".', get_class($user)));
+ throw new \RuntimeException(sprintf('No encoder has been configured for account "%s".', is_object($user) ? get_class($user) : $user));
}
/**
diff --git a/Core/Encoder/EncoderFactoryInterface.php b/Core/Encoder/EncoderFactoryInterface.php
index 978f907..2b9834b 100644
--- a/Core/Encoder/EncoderFactoryInterface.php
+++ b/Core/Encoder/EncoderFactoryInterface.php
@@ -23,9 +23,11 @@ interface EncoderFactoryInterface
/**
* Returns the password encoder to use for the given account.
*
- * @param UserInterface $user
+ * @param UserInterface|string $user A UserInterface instance or a class name
*
- * @return PasswordEncoderInterface never null
+ * @return PasswordEncoderInterface
+ *
+ * @throws \RuntimeException when no password encoder could be found for the user
*/
- public function getEncoder(UserInterface $user);
+ public function getEncoder($user);
}
diff --git a/Core/Encoder/MessageDigestPasswordEncoder.php b/Core/Encoder/MessageDigestPasswordEncoder.php
index a5b2c81..a8bd553 100644
--- a/Core/Encoder/MessageDigestPasswordEncoder.php
+++ b/Core/Encoder/MessageDigestPasswordEncoder.php
@@ -20,6 +20,7 @@ class MessageDigestPasswordEncoder extends BasePasswordEncoder
{
private $algorithm;
private $encodeHashAsBase64;
+ private $iterations;
/**
* Constructor.
diff --git a/Core/Encoder/PlaintextPasswordEncoder.php b/Core/Encoder/PlaintextPasswordEncoder.php
index 21a9a97..c21f3cd 100644
--- a/Core/Encoder/PlaintextPasswordEncoder.php
+++ b/Core/Encoder/PlaintextPasswordEncoder.php
@@ -20,6 +20,11 @@ class PlaintextPasswordEncoder extends BasePasswordEncoder
{
private $ignorePasswordCase;
+ /**
+ * Constructor.
+ *
+ * @param Boolean $ignorePasswordCase Compare password case-insensitive
+ */
public function __construct($ignorePasswordCase = false)
{
$this->ignorePasswordCase = $ignorePasswordCase;
diff --git a/Core/Event/AuthenticationEvent.php b/Core/Event/AuthenticationEvent.php
new file mode 100644
index 0000000..132cea9
--- /dev/null
+++ b/Core/Event/AuthenticationEvent.php
@@ -0,0 +1,35 @@
+<?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\Core\Event;
+
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * This is a general purpose authentication event.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class AuthenticationEvent extends Event
+{
+ private $authenticationToken;
+
+ public function __construct(TokenInterface $token)
+ {
+ $this->authenticationToken = $token;
+ }
+
+ public function getAuthenticationToken()
+ {
+ return $this->authenticationToken;
+ }
+}
diff --git a/Core/Event/AuthenticationFailureEvent.php b/Core/Event/AuthenticationFailureEvent.php
new file mode 100644
index 0000000..6705fc9
--- /dev/null
+++ b/Core/Event/AuthenticationFailureEvent.php
@@ -0,0 +1,37 @@
+<?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\Core\Event;
+
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+/**
+ * This event is dispatched on authentication failure.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class AuthenticationFailureEvent extends AuthenticationEvent
+{
+ private $authenticationException;
+
+ public function __construct(TokenInterface $token, AuthenticationException $ex)
+ {
+ parent::__construct($token);
+
+ $this->authenticationException = $ex;
+ }
+
+ public function getAuthenticationException()
+ {
+ return $this->authenticationException;
+ }
+}
diff --git a/Core/Exception/CookieTheftException.php b/Core/Exception/CookieTheftException.php
index 09ec79e..2ada78d 100644
--- a/Core/Exception/CookieTheftException.php
+++ b/Core/Exception/CookieTheftException.php
@@ -1,7 +1,5 @@
<?php
-namespace Symfony\Component\Security\Core\Exception;
-
/*
* This file is part of the Symfony package.
*
@@ -11,6 +9,8 @@ namespace Symfony\Component\Security\Core\Exception;
* file that was distributed with this source code.
*/
+namespace Symfony\Component\Security\Core\Exception;
+
/**
* This exception is thrown when the RememberMeServices implementation
* detects that a presented cookie has already been used by someone else.
diff --git a/Core/Exception/InvalidCsrfTokenException.php b/Core/Exception/InvalidCsrfTokenException.php
index 51adb69..4181bac 100644
--- a/Core/Exception/InvalidCsrfTokenException.php
+++ b/Core/Exception/InvalidCsrfTokenException.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Exception;
diff --git a/Core/Exception/LogoutException.php b/Core/Exception/LogoutException.php
new file mode 100644
index 0000000..2bb954f
--- /dev/null
+++ b/Core/Exception/LogoutException.php
@@ -0,0 +1,25 @@
+<?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\Core\Exception;
+
+/**
+ * LogoutException is thrown when the account cannot be logged out.
+ *
+ * @author Jeremy Mikola <jmikola@gmail.com>
+ */
+class LogoutException extends \RuntimeException
+{
+ public function __construct($message = 'Logout Exception', \Exception $previous = null)
+ {
+ parent::__construct($message, 403, $previous);
+ }
+}
diff --git a/Core/Exception/SessionUnavailableException.php b/Core/Exception/SessionUnavailableException.php
index a00a503..519164a 100644
--- a/Core/Exception/SessionUnavailableException.php
+++ b/Core/Exception/SessionUnavailableException.php
@@ -16,7 +16,7 @@ namespace Symfony\Component\Security\Core\Exception;
*
* Possible reasons for this are:
*
- * a) The session timed-out because the user waited too long.
+ * a) The session timed out because the user waited too long.
* b) The user has disabled cookies, and a new session is started on each
* request.
*
diff --git a/Core/SecurityContextInterface.php b/Core/SecurityContextInterface.php
index 8fdd453..78d6477 100644
--- a/Core/SecurityContextInterface.php
+++ b/Core/SecurityContextInterface.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core;
@@ -35,8 +35,6 @@ interface SecurityContextInterface
* Sets the authentication token.
*
* @param TokenInterface $token
- *
- * @return void
*/
public function setToken(TokenInterface $token = null);
diff --git a/Core/User/ChainUserProvider.php b/Core/User/ChainUserProvider.php
index 14a0dec..376ba1c 100644
--- a/Core/User/ChainUserProvider.php
+++ b/Core/User/ChainUserProvider.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\User;
diff --git a/Core/User/EquatableInterface.php b/Core/User/EquatableInterface.php
new file mode 100644
index 0000000..645b77c
--- /dev/null
+++ b/Core/User/EquatableInterface.php
@@ -0,0 +1,37 @@
+<?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\Core\User;
+
+/**
+ * EquatableInterface used to test if two objects are equal in security
+ * and re-authentication context.
+ *
+ * @author Dariusz Górecki <darek.krk@gmail.com>
+ */
+interface EquatableInterface
+{
+ /**
+ * The equality comparison should neither be done by referential equality
+ * nor by comparing identities (i.e. getId() === getId()).
+ *
+ * However, you do not need to compare every attribute, but only those that
+ * are relevant for assessing whether re-authentication is required.
+ *
+ * Also implementation should consider that $user instance may implement
+ * the extended user interface `AdvancedUserInterface`.
+ *
+ * @param UserInterface $user
+ *
+ * @return Boolean
+ */
+ public function isEqualTo(UserInterface $user);
+}
diff --git a/Core/User/User.php b/Core/User/User.php
index 0106a8a..b378e1b 100644
--- a/Core/User/User.php
+++ b/Core/User/User.php
@@ -113,44 +113,4 @@ final class User implements AdvancedUserInterface
public function eraseCredentials()
{
}
-
- /**
- * {@inheritDoc}
- */
- public function equals(UserInterface $user)
- {
- if (!$user instanceof User) {
- return false;
- }
-
- if ($this->password !== $user->getPassword()) {
- return false;
- }
-
- if ($this->getSalt() !== $user->getSalt()) {
- return false;
- }
-
- if ($this->username !== $user->getUsername()) {
- return false;
- }
-
- if ($this->accountNonExpired !== $user->isAccountNonExpired()) {
- return false;
- }
-
- if ($this->accountNonLocked !== $user->isAccountNonLocked()) {
- return false;
- }
-
- if ($this->credentialsNonExpired !== $user->isCredentialsNonExpired()) {
- return false;
- }
-
- if ($this->enabled !== $user->isEnabled()) {
- return false;
- }
-
- return true;
- }
}
diff --git a/Core/User/UserInterface.php b/Core/User/UserInterface.php
index ccb6fbd..ed96ca9 100644
--- a/Core/User/UserInterface.php
+++ b/Core/User/UserInterface.php
@@ -84,19 +84,4 @@ interface UserInterface
* @return void
*/
public function eraseCredentials();
-
- /**
- * Returns whether or not the given user is equivalent to *this* user.
- *
- * The equality comparison should neither be done by referential equality
- * nor by comparing identities (i.e. getId() === getId()).
- *
- * However, you do not need to compare every attribute, but only those that
- * are relevant for assessing whether re-authentication is required.
- *
- * @param UserInterface $user
- *
- * @return Boolean
- */
- public function equals(UserInterface $user);
}
diff --git a/Core/Util/ClassUtils.php b/Core/Util/ClassUtils.php
new file mode 100644
index 0000000..7b583a3
--- /dev/null
+++ b/Core/Util/ClassUtils.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\Core\Util;
+
+/**
+ * Class related functionality for objects that
+ * might or might not be proxy objects at the moment.
+ *
+ * @see Doctrine\Common\Util\ClassUtils
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ * @author Johannes Schmitt <schmittjoh@gmail.com>
+ */
+class ClassUtils
+{
+ /**
+ * Marker for Proxy class names.
+ *
+ * @var string
+ */
+ const MARKER = '__CG__';
+
+ /**
+ * Length of the proxy marker
+ *
+ * @var int
+ */
+ const MARKER_LENGTH = 6;
+
+ /**
+ * Gets the real class name of a class name that could be a proxy.
+ *
+ * @param string|object
+ * @return string
+ */
+ public static function getRealClass($object)
+ {
+ $class = is_object($object) ? get_class($object) : $object;
+
+ if (false === $pos = strrpos($class, '\\'.self::MARKER.'\\')) {
+ return $class;
+ }
+
+ return substr($class, $pos + self::MARKER_LENGTH + 2);
+ }
+}
diff --git a/Core/Validator/Constraint/UserPassword.php b/Core/Validator/Constraint/UserPassword.php
new file mode 100644
index 0000000..3279e02
--- /dev/null
+++ b/Core/Validator/Constraint/UserPassword.php
@@ -0,0 +1,27 @@
+<?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\Core\Validator\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * @Annotation
+ */
+class UserPassword extends Constraint
+{
+ public $message = 'This value should be the user current password.';
+
+ public function validatedBy()
+ {
+ return 'security.validator.user_password';
+ }
+}
diff --git a/Core/Validator/Constraint/UserPasswordValidator.php b/Core/Validator/Constraint/UserPasswordValidator.php
new file mode 100644
index 0000000..a54906b
--- /dev/null
+++ b/Core/Validator/Constraint/UserPasswordValidator.php
@@ -0,0 +1,46 @@
+<?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\Core\Validator\Constraint;
+
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
+
+class UserPasswordValidator extends ConstraintValidator
+{
+ private $securityContext;
+ private $encoderFactory;
+
+ public function __construct(SecurityContextInterface $securityContext, EncoderFactoryInterface $encoderFactory)
+ {
+ $this->securityContext = $securityContext;
+ $this->encoderFactory = $encoderFactory;
+ }
+
+ public function validate($password, Constraint $constraint)
+ {
+ $user = $this->securityContext->getToken()->getUser();
+
+ if (!$user instanceof UserInterface) {
+ throw new ConstraintDefinitionException('The User must extend UserInterface');
+ }
+
+ $encoder = $this->encoderFactory->getEncoder($user);
+
+ if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
+ $this->context->addViolation($constraint->message);
+ }
+ }
+}
diff --git a/Http/AccessMap.php b/Http/AccessMap.php
index 6d12b42..de78e15 100644
--- a/Http/AccessMap.php
+++ b/Http/AccessMap.php
@@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Request;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
-class AccessMap
+class AccessMap implements AccessMapInterface
{
private $map = array();
diff --git a/Http/AccessMapInterface.php b/Http/AccessMapInterface.php
new file mode 100644
index 0000000..7d15fee
--- /dev/null
+++ b/Http/AccessMapInterface.php
@@ -0,0 +1,33 @@
+<?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\Http;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * AccessMap allows configuration of different access control rules for
+ * specific parts of the website.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Kris Wallsmith <kris@symfony.com>
+ */
+interface AccessMapInterface
+{
+ /**
+ * Returns security attributes and required channel for the supplied request.
+ *
+ * @param Request $request The current request
+ *
+ * @return array A tuple of security attributes and the required channel
+ */
+ public function getPatterns(Request $request);
+}
diff --git a/Http/Authentication/AuthenticationFailureHandlerInterface.php b/Http/Authentication/AuthenticationFailureHandlerInterface.php
index 69b5426..8dbd29a 100644
--- a/Http/Authentication/AuthenticationFailureHandlerInterface.php
+++ b/Http/Authentication/AuthenticationFailureHandlerInterface.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authentication;
@@ -33,7 +33,7 @@ interface AuthenticationFailureHandlerInterface
* @param Request $request
* @param AuthenticationException $exception
*
- * @return Response the response to return
+ * @return Response The response to return, never null
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception);
}
diff --git a/Http/Authentication/AuthenticationSuccessHandlerInterface.php b/Http/Authentication/AuthenticationSuccessHandlerInterface.php
index ed44ee5..5c08e73 100644
--- a/Http/Authentication/AuthenticationSuccessHandlerInterface.php
+++ b/Http/Authentication/AuthenticationSuccessHandlerInterface.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authentication;
@@ -33,7 +33,7 @@ interface AuthenticationSuccessHandlerInterface
* @param Request $request
* @param TokenInterface $token
*
- * @return Response the response to return
+ * @return Response never null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token);
}
diff --git a/Http/Authentication/DefaultAuthenticationFailureHandler.php b/Http/Authentication/DefaultAuthenticationFailureHandler.php
new file mode 100644
index 0000000..61d77a8
--- /dev/null
+++ b/Http/Authentication/DefaultAuthenticationFailureHandler.php
@@ -0,0 +1,87 @@
+<?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\Http\Authentication;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Http\HttpUtils;
+
+/**
+ * Class with the default authentication failure handling logic.
+ *
+ * Can be optionally be extended from by the developer to alter the behaviour
+ * while keeping the default behaviour.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ * @author Alexander <iam.asm89@gmail.com>
+ */
+class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface
+{
+ protected $httpKernel;
+ protected $httpUtils;
+ protected $logger;
+ protected $options;
+
+ /**
+ * Constructor.
+ *
+ * @param HttpKernelInterface $httpKernel
+ * @param HttpUtils $httpUtils
+ * @param array $options Options for processing a failed authentication attempt.
+ * @param LoggerInterface $logger Optional logger
+ */
+ public function __construct(HttpKernelInterface $httpKernel, HttpUtils $httpUtils, array $options, LoggerInterface $logger = null)
+ {
+ $this->httpKernel = $httpKernel;
+ $this->httpUtils = $httpUtils;
+ $this->logger = $logger;
+
+ $this->options = array_merge(array(
+ 'failure_path' => null,
+ 'failure_forward' => false,
+ 'login_path' => '/login',
+ ), $options);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
+ {
+ if (null === $this->options['failure_path']) {
+ $this->options['failure_path'] = $this->options['login_path'];
+ }
+
+ if ($this->options['failure_forward']) {
+ if (null !== $this->logger) {
+ $this->logger->debug(sprintf('Forwarding to %s', $this->options['failure_path']));
+ }
+
+ $subRequest = $this->httpUtils->createRequest($request, $this->options['failure_path']);
+ $subRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);
+
+ return $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
+ }
+
+ if (null !== $this->logger) {
+ $this->logger->debug(sprintf('Redirecting to %s', $this->options['failure_path']));
+ }
+
+ $request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);
+
+ return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']);
+ }
+}
diff --git a/Http/Authentication/DefaultAuthenticationSuccessHandler.php b/Http/Authentication/DefaultAuthenticationSuccessHandler.php
new file mode 100644
index 0000000..dc7cbe5
--- /dev/null
+++ b/Http/Authentication/DefaultAuthenticationSuccessHandler.php
@@ -0,0 +1,111 @@
+<?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\Http\Authentication;
+
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\HttpUtils;
+
+/**
+ * Class with the default authentication success handling logic.
+ *
+ * Can be optionally be extended from by the developer to alter the behaviour
+ * while keeping the default behaviour.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ * @author Alexander <iam.asm89@gmail.com>
+ */
+class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
+{
+ protected $httpUtils;
+ protected $options;
+ protected $providerKey;
+
+ /**
+ * Constructor.
+ *
+ * @param HttpUtils $httpUtils
+ * @param array $options Options for processing a successful authentication attempt.
+ */
+ public function __construct(HttpUtils $httpUtils, array $options)
+ {
+ $this->httpUtils = $httpUtils;
+
+ $this->options = array_merge(array(
+ 'always_use_default_target_path' => false,
+ 'default_target_path' => '/',
+ 'login_path' => '/login',
+ 'target_path_parameter' => '_target_path',
+ 'use_referer' => false,
+ ), $options);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function onAuthenticationSuccess(Request $request, TokenInterface $token)
+ {
+ return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
+ }
+
+
+ /**
+ * Get the provider key.
+ *
+ * @return string
+ */
+ public function getProviderKey()
+ {
+ return $this->providerKey;
+ }
+
+ /**
+ * Set the provider key.
+ *
+ * @param string $providerKey
+ */
+ public function setProviderKey($providerKey)
+ {
+ $this->providerKey = $providerKey;
+ }
+
+ /**
+ * Builds the target URL according to the defined options.
+ *
+ * @param Request $request
+ *
+ * @return string
+ */
+ protected function determineTargetUrl(Request $request)
+ {
+ if ($this->options['always_use_default_target_path']) {
+ return $this->options['default_target_path'];
+ }
+
+ if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) {
+ return $targetUrl;
+ }
+
+ if (null !== $this->providerKey && $targetUrl = $request->getSession()->get('_security.'.$this->providerKey.'.target_path')) {
+ $request->getSession()->remove('_security.'.$this->providerKey.'.target_path');
+
+ return $targetUrl;
+ }
+
+ if ($this->options['use_referer'] && ($targetUrl = $request->headers->get('Referer')) && $targetUrl !== $this->httpUtils->generateUri($request, $this->options['login_path'])) {
+ return $targetUrl;
+ }
+
+ return $this->options['default_target_path'];
+ }
+}
diff --git a/Http/Authorization/AccessDeniedHandlerInterface.php b/Http/Authorization/AccessDeniedHandlerInterface.php
index 3811bf0..a10a5d0 100644
--- a/Http/Authorization/AccessDeniedHandlerInterface.php
+++ b/Http/Authorization/AccessDeniedHandlerInterface.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authorization;
diff --git a/Http/Event/InteractiveLoginEvent.php b/Http/Event/InteractiveLoginEvent.php
index f242501..2225d92 100644
--- a/Http/Event/InteractiveLoginEvent.php
+++ b/Http/Event/InteractiveLoginEvent.php
@@ -21,17 +21,33 @@ class InteractiveLoginEvent extends Event
private $authenticationToken;
+ /**
+ * Constructor.
+ *
+ * @param Request $request A Request instance
+ * @param TokenInterface $authenticationToken A TokenInterface instance
+ */
public function __construct(Request $request, TokenInterface $authenticationToken)
{
$this->request = $request;
$this->authenticationToken = $authenticationToken;
}
+ /**
+ * Gets the request.
+ *
+ * @return Request A Request instance
+ */
public function getRequest()
{
return $this->request;
}
+ /**
+ * Gets the authentication token.
+ *
+ * @return TokenInterface A TokenInterface instance
+ */
public function getAuthenticationToken()
{
return $this->authenticationToken;
diff --git a/Http/Firewall.php b/Http/Firewall.php
index 91eb6a9..a590fd9 100644
--- a/Http/Firewall.php
+++ b/Http/Firewall.php
@@ -33,7 +33,7 @@ class Firewall
/**
* Constructor.
*
- * @param FirewallMap $map A FirewallMap instance
+ * @param FirewallMapInterface $map A FirewallMapInterface instance
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
*/
public function __construct(FirewallMapInterface $map, EventDispatcherInterface $dispatcher)
diff --git a/Http/Firewall/AbstractAuthenticationListener.php b/Http/Firewall/AbstractAuthenticationListener.php
index 8403ec7..410fb73 100644
--- a/Http/Firewall/AbstractAuthenticationListener.php
+++ b/Http/Firewall/AbstractAuthenticationListener.php
@@ -20,7 +20,6 @@ use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterfac
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\SessionUnavailableException;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -70,14 +69,14 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
* @param SessionAuthenticationStrategyInterface $sessionStrategy
* @param HttpUtils $httpUtils An HttpUtilsInterface instance
* @param string $providerKey
+ * @param AuthenticationSuccessHandlerInterface $successHandler
+ * @param AuthenticationFailureHandlerInterface $failureHandler
* @param array $options An array of options for the processing of a
* successful, or failed authentication attempt
- * @param AuthenticationSuccessHandlerInterface $successHandler
- * @param AuthenticationFailureHandlerInterface $failureHandler
- * @param LoggerInterface $logger A LoggerInterface instance
- * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
+ * @param LoggerInterface $logger A LoggerInterface instance
+ * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
*/
- public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
+ public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
@@ -91,13 +90,6 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
$this->failureHandler = $failureHandler;
$this->options = array_merge(array(
'check_path' => '/login_check',
- 'login_path' => '/login',
- 'always_use_default_target_path' => false,
- 'default_target_path' => '/',
- 'target_path_parameter' => '_target_path',
- 'use_referer' => false,
- 'failure_path' => null,
- 'failure_forward' => false,
), $options);
$this->logger = $logger;
$this->dispatcher = $dispatcher;
@@ -133,7 +125,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
try {
if (!$request->hasPreviousSession()) {
- throw new SessionUnavailableException('Your session has timed-out, or you have disabled cookies.');
+ throw new SessionUnavailableException('Your session has timed out, or you have disabled cookies.');
}
if (null === $returnValue = $this->attemptAuthentication($request)) {
@@ -177,7 +169,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
*
* @param Request $request A Request instance
*
- * @return TokenInterface The authenticated token, or null if full authentication is not possible
+ * @return TokenInterface|Response|null The authenticated token, null if full authentication is not possible, or a Response
*
* @throws AuthenticationException if the authentication fails
*/
@@ -191,32 +183,13 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
$this->securityContext->setToken(null);
- if (null !== $this->failureHandler) {
- return $this->failureHandler->onAuthenticationFailure($request, $failed);
- }
-
- if (null === $this->options['failure_path']) {
- $this->options['failure_path'] = $this->options['login_path'];
- }
-
- if ($this->options['failure_forward']) {
- if (null !== $this->logger) {
- $this->logger->debug(sprintf('Forwarding to %s', $this->options['failure_path']));
- }
-
- $subRequest = $this->httpUtils->createRequest($request, $this->options['failure_path']);
- $subRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $failed);
+ $response = $this->failureHandler->onAuthenticationFailure($request, $failed);
- return $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
+ if (!$response instanceof Response) {
+ throw new \RuntimeException('Authentication Failure Handler did not return a Response.');
}
- if (null !== $this->logger) {
- $this->logger->debug(sprintf('Redirecting to %s', $this->options['failure_path']));
- }
-
- $request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $failed);
-
- return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']);
+ return $response;
}
private function onSuccess(GetResponseEvent $event, Request $request, TokenInterface $token)
@@ -236,10 +209,10 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
}
- if (null !== $this->successHandler) {
- $response = $this->successHandler->onAuthenticationSuccess($request, $token);
- } else {
- $response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
+ $response = $this->successHandler->onAuthenticationSuccess($request, $token);
+
+ if (!$response instanceof Response) {
+ throw new \RuntimeException('Authentication Success Handler did not return a Response.');
}
if (null !== $this->rememberMeServices) {
@@ -248,35 +221,4 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
return $response;
}
-
- /**
- * Builds the target URL according to the defined options.
- *
- * @param Request $request
- *
- * @return string
- */
- private function determineTargetUrl(Request $request)
- {
- if ($this->options['always_use_default_target_path']) {
- return $this->options['default_target_path'];
- }
-
- if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) {
- return $targetUrl;
- }
-
- $session = $request->getSession();
- if ($targetUrl = $session->get('_security.target_path')) {
- $session->remove('_security.target_path');
-
- return $targetUrl;
- }
-
- if ($this->options['use_referer'] && $targetUrl = $request->headers->get('Referer')) {
- return $targetUrl;
- }
-
- return $this->options['default_target_path'];
- }
}
diff --git a/Http/Firewall/AccessListener.php b/Http/Firewall/AccessListener.php
index 877b6c3..3e2d3a5 100644
--- a/Http/Firewall/AccessListener.php
+++ b/Http/Firewall/AccessListener.php
@@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
-use Symfony\Component\Security\Http\AccessMap;
+use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -33,7 +33,7 @@ class AccessListener implements ListenerInterface
private $authManager;
private $logger;
- public function __construct(SecurityContextInterface $context, AccessDecisionManagerInterface $accessDecisionManager, AccessMap $map, AuthenticationManagerInterface $authManager, LoggerInterface $logger = null)
+ public function __construct(SecurityContextInterface $context, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager, LoggerInterface $logger = null)
{
$this->context = $context;
$this->accessDecisionManager = $accessDecisionManager;
diff --git a/Http/Firewall/ChannelListener.php b/Http/Firewall/ChannelListener.php
index 847753f..9b0f8c6 100644
--- a/Http/Firewall/ChannelListener.php
+++ b/Http/Firewall/ChannelListener.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Http\AccessMap;
+use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -28,7 +28,7 @@ class ChannelListener implements ListenerInterface
private $authenticationEntryPoint;
private $logger;
- public function __construct(AccessMap $map, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
+ public function __construct(AccessMapInterface $map, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
{
$this->map = $map;
$this->authenticationEntryPoint = $authenticationEntryPoint;
diff --git a/Http/Firewall/ContextListener.php b/Http/Firewall/ContextListener.php
index 6f267cf..b1ba24b 100644
--- a/Http/Firewall/ContextListener.php
+++ b/Http/Firewall/ContextListener.php
@@ -22,6 +22,7 @@ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
@@ -36,6 +37,7 @@ class ContextListener implements ListenerInterface
private $contextKey;
private $logger;
private $userProviders;
+ private $dispatcher;
public function __construct(SecurityContextInterface $context, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
@@ -43,14 +45,17 @@ class ContextListener implements ListenerInterface
throw new \InvalidArgumentException('$contextKey must not be empty.');
}
+ foreach ($userProviders as $userProvider) {
+ if (!$userProvider instanceof UserProviderInterface) {
+ throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "Symfony\Component\Security\Core\User\UserProviderInterface".', get_class($userProvider)));
+ }
+ }
+
$this->context = $context;
$this->userProviders = $userProviders;
$this->contextKey = $contextKey;
$this->logger = $logger;
-
- if (null !== $dispatcher) {
- $dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
- }
+ $this->dispatcher = $dispatcher;
}
/**
@@ -60,6 +65,10 @@ class ContextListener implements ListenerInterface
*/
public function handle(GetResponseEvent $event)
{
+ if (null !== $this->dispatcher && HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
+ $this->dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
+ }
+
$request = $event->getRequest();
$session = $request->hasPreviousSession() ? $request->getSession() : null;
@@ -103,19 +112,19 @@ class ContextListener implements ListenerInterface
return;
}
- if (null === $token = $this->context->getToken()) {
- return;
+ if (null !== $this->logger) {
+ $this->logger->debug('Write SecurityContext in the session');
}
- if (null === $token || $token instanceof AnonymousToken) {
+ if (null === $session = $event->getRequest()->getSession()) {
return;
}
- if (null !== $this->logger) {
- $this->logger->debug('Write SecurityContext in the session');
+ if ((null === $token = $this->context->getToken()) || ($token instanceof AnonymousToken)) {
+ $session->remove('_security_'.$this->contextKey);
+ } else {
+ $session->set('_security_'.$this->contextKey, serialize($token));
}
-
- $event->getRequest()->getSession()->set('_security_'.$this->contextKey, serialize($token));
}
/**
diff --git a/Http/Firewall/ExceptionListener.php b/Http/Firewall/ExceptionListener.php
index b704262..f134f9c 100644
--- a/Http/Firewall/ExceptionListener.php
+++ b/Http/Firewall/ExceptionListener.php
@@ -15,12 +15,12 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
-use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException;
+use Symfony\Component\Security\Core\Exception\LogoutException;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
@@ -39,6 +39,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class ExceptionListener
{
private $context;
+ private $providerKey;
private $accessDeniedHandler;
private $authenticationEntryPoint;
private $authenticationTrustResolver;
@@ -46,11 +47,12 @@ class ExceptionListener
private $logger;
private $httpUtils;
- public function __construct(SecurityContextInterface $context, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null)
+ public function __construct(SecurityContextInterface $context, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null)
{
$this->context = $context;
$this->accessDeniedHandler = $accessDeniedHandler;
$this->httpUtils = $httpUtils;
+ $this->providerKey = $providerKey;
$this->authenticationEntryPoint = $authenticationEntryPoint;
$this->authenticationTrustResolver = $trustResolver;
$this->errorPage = $errorPage;
@@ -95,10 +97,12 @@ class ExceptionListener
return;
}
} elseif ($exception instanceof AccessDeniedException) {
+ $event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception));
+
$token = $this->context->getToken();
if (!$this->authenticationTrustResolver->isFullFledged($token)) {
if (null !== $this->logger) {
- $this->logger->debug('Access denied (user is not fully authenticated); redirecting to authentication entry point');
+ $this->logger->debug(sprintf('Access is denied (user is not fully authenticated) by "%s" at line %s; redirecting to authentication entry point', $exception->getFile(), $exception->getLine()));
}
try {
@@ -110,7 +114,7 @@ class ExceptionListener
}
} else {
if (null !== $this->logger) {
- $this->logger->debug('Access is denied (and user is neither anonymous, nor remember-me)');
+ $this->logger->debug(sprintf('Access is denied (and user is neither anonymous, nor remember-me) by "%s" at line %s', $exception->getFile(), $exception->getLine()));
}
try {
@@ -125,10 +129,7 @@ class ExceptionListener
$subRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $exception);
$response = $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);
- $response->setStatusCode(403);
} else {
- $event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception));
-
return;
}
} catch (\Exception $e) {
@@ -141,6 +142,12 @@ class ExceptionListener
return;
}
}
+ } elseif ($exception instanceof LogoutException) {
+ if (null !== $this->logger) {
+ $this->logger->info(sprintf('Logout exception occurred; wrapping with AccessDeniedHttpException (%s)', $exception->getMessage()));
+ }
+
+ return;
} else {
return;
}
@@ -160,10 +167,9 @@ class ExceptionListener
$this->setTargetPath($request);
- if ($authException instanceof AccountStatusException && ($token = $this->context->getToken()) instanceof UsernamePasswordToken) {
+ if ($authException instanceof AccountStatusException) {
// remove the security token to prevent infinite redirect loops
$this->context->setToken(null);
- $request->getSession()->remove('_security_'.$token->getProviderKey());
}
return $this->authenticationEntryPoint->start($request, $authException);
@@ -173,7 +179,7 @@ class ExceptionListener
{
// session isn't required when using http basic authentication mechanism for example
if ($request->hasSession() && $request->isMethodSafe()) {
- $request->getSession()->set('_security.target_path', $request->getUri());
+ $request->getSession()->set('_security.' . $this->providerKey . '.target_path', $request->getUri());
}
}
}
diff --git a/Http/Firewall/LogoutListener.php b/Http/Firewall/LogoutListener.php
index 07244f5..32a0511 100644
--- a/Http/Firewall/LogoutListener.php
+++ b/Http/Firewall/LogoutListener.php
@@ -11,13 +11,15 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
-
-use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
-use Symfony\Component\Security\Core\SecurityContextInterface;
-use Symfony\Component\Security\Http\HttpUtils;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Exception\LogoutException;
+use Symfony\Component\Security\Http\HttpUtils;
+use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
+use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
/**
* LogoutListener logout users.
@@ -27,28 +29,32 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class LogoutListener implements ListenerInterface
{
private $securityContext;
- private $logoutPath;
- private $targetUrl;
+ private $options;
private $handlers;
private $successHandler;
private $httpUtils;
+ private $csrfProvider;
/**
* Constructor
*
* @param SecurityContextInterface $securityContext
* @param HttpUtils $httpUtils An HttpUtilsInterface instance
- * @param string $logoutPath The path that starts the logout process
- * @param string $targetUrl The URL to redirect to after logout
- * @param LogoutSuccessHandlerInterface $successHandler
+ * @param LogoutSuccessHandlerInterface $successHandler A LogoutSuccessHandlerInterface instance
+ * @param array $options An array of options to process a logout attempt
+ * @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance
*/
- public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, $logoutPath, $targetUrl = '/', LogoutSuccessHandlerInterface $successHandler = null)
+ public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, LogoutSuccessHandlerInterface $successHandler, array $options = array(), CsrfProviderInterface $csrfProvider = null)
{
$this->securityContext = $securityContext;
$this->httpUtils = $httpUtils;
- $this->logoutPath = $logoutPath;
- $this->targetUrl = $targetUrl;
+ $this->options = array_merge(array(
+ 'csrf_parameter' => '_csrf_token',
+ 'intention' => 'logout',
+ 'logout_path' => '/logout',
+ ), $options);
$this->successHandler = $successHandler;
+ $this->csrfProvider = $csrfProvider;
$this->handlers = array();
}
@@ -56,8 +62,6 @@ class LogoutListener implements ListenerInterface
* Adds a logout handler
*
* @param LogoutHandlerInterface $handler
- *
- * @return void
*/
public function addHandler(LogoutHandlerInterface $handler)
{
@@ -67,24 +71,32 @@ class LogoutListener implements ListenerInterface
/**
* Performs the logout if requested
*
+ * If a CsrfProviderInterface instance is available, it will be used to
+ * validate the request.
+ *
* @param GetResponseEvent $event A GetResponseEvent instance
+ * @throws InvalidCsrfTokenException if the CSRF token is invalid
+ * @throws RuntimeException if the LogoutSuccessHandlerInterface instance does not return a response
*/
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
- if (!$this->httpUtils->checkRequestPath($request, $this->logoutPath)) {
+ if (!$this->requiresLogout($request)) {
return;
}
- if (null !== $this->successHandler) {
- $response = $this->successHandler->onLogoutSuccess($request);
+ if (null !== $this->csrfProvider) {
+ $csrfToken = $request->get($this->options['csrf_parameter'], null, true);
- if (!$response instanceof Response) {
- throw new \RuntimeException('Logout Success Handler did not return a Response.');
+ if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
+ throw new LogoutException('Invalid CSRF token.');
}
- } else {
- $response = $this->httpUtils->createRedirectResponse($request, $this->targetUrl);
+ }
+
+ $response = $this->successHandler->onLogoutSuccess($request);
+ if (!$response instanceof Response) {
+ throw new \RuntimeException('Logout Success Handler did not return a Response.');
}
// handle multiple logout attempts gracefully
@@ -98,4 +110,20 @@ class LogoutListener implements ListenerInterface
$event->setResponse($response);
}
+
+ /**
+ * Whether this request is asking for logout.
+ *
+ * The default implementation only processed requests to a specific path,
+ * but a subclass could change this to logout requests where
+ * certain parameters is present.
+ *
+ * @param Request $request
+ *
+ * @return Boolean
+ */
+ protected function requiresLogout(Request $request)
+ {
+ return $this->httpUtils->checkRequestPath($request, $this->options['logout_path']);
+ }
}
diff --git a/Http/Firewall/RememberMeListener.php b/Http/Firewall/RememberMeListener.php
index ccda113..3614f79 100644
--- a/Http/Firewall/RememberMeListener.php
+++ b/Http/Firewall/RememberMeListener.php
@@ -1,5 +1,14 @@
<?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\Http\Firewall;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
@@ -12,15 +21,6 @@ use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-/*
- * 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.
- */
-
/**
* RememberMeListener implements authentication capabilities via a cookie
*
diff --git a/Http/Firewall/SwitchUserListener.php b/Http/Firewall/SwitchUserListener.php
index 9780860..7f0aa78 100644
--- a/Http/Firewall/SwitchUserListener.php
+++ b/Http/Firewall/SwitchUserListener.php
@@ -86,9 +86,7 @@ class SwitchUserListener implements ListenerInterface
try {
$this->securityContext->setToken($this->attemptSwitchUser($request));
} catch (AuthenticationException $e) {
- if (null !== $this->logger) {
- $this->logger->warn(sprintf('Switch User failed: "%s"', $e->getMessage()));
- }
+ throw new \LogicException(sprintf('Switch User failed: "%s"', $e->getMessage()));
}
}
@@ -108,8 +106,14 @@ class SwitchUserListener implements ListenerInterface
private function attemptSwitchUser(Request $request)
{
$token = $this->securityContext->getToken();
- if (false !== $this->getOriginalToken($token)) {
- throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername()));
+ $originalToken = $this->getOriginalToken($token);
+
+ if (false !== $originalToken) {
+ if ($token->getUsername() === $request->get($this->usernameParameter)) {
+ return $token;
+ } else {
+ throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername()));
+ }
}
if (false === $this->accessDecisionManager->decide($token, array($this->role))) {
@@ -148,7 +152,7 @@ class SwitchUserListener implements ListenerInterface
private function attemptExitUser(Request $request)
{
if (false === $original = $this->getOriginalToken($this->securityContext->getToken())) {
- throw new AuthenticationCredentialsNotFoundException(sprintf('Could not find original Token object.'));
+ throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
}
if (null !== $this->dispatcher) {
diff --git a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
index bd2cec1..22330a8 100644
--- a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
+++ b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
@@ -37,15 +37,15 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
/**
* {@inheritdoc}
*/
- public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
+ public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
- parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, array_merge(array(
+ parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'intention' => 'authenticate',
'post_only' => true,
- ), $options), $successHandler, $failureHandler, $logger, $dispatcher);
+ ), $options), $logger, $dispatcher);
$this->csrfProvider = $csrfProvider;
}
@@ -53,6 +53,18 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
/**
* {@inheritdoc}
*/
+ protected function requiresAuthentication(Request $request)
+ {
+ if ($this->options['post_only'] && !$request->isMethod('post')) {
+ return false;
+ }
+
+ return parent::requiresAuthentication($request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
protected function attemptAuthentication(Request $request)
{
if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
diff --git a/Http/FirewallMap.php b/Http/FirewallMap.php
index d5fc331..dfc0984 100644
--- a/Http/FirewallMap.php
+++ b/Http/FirewallMap.php
@@ -1,20 +1,20 @@
<?php
-namespace Symfony\Component\Security\Http;
-
-use Symfony\Component\HttpFoundation\RequestMatcherInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Security\Http\Firewall\ExceptionListener;
-
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
+namespace Symfony\Component\Security\Http;
+
+use Symfony\Component\HttpFoundation\RequestMatcherInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\Firewall\ExceptionListener;
+
/**
* FirewallMap allows configuration of different firewalls for specific parts
* of the website.
diff --git a/Http/FirewallMapInterface.php b/Http/FirewallMapInterface.php
index 57afc75..336125f 100644
--- a/Http/FirewallMapInterface.php
+++ b/Http/FirewallMapInterface.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http;
diff --git a/Http/HttpUtils.php b/Http/HttpUtils.php
index cac130e..1c87e77 100644
--- a/Http/HttpUtils.php
+++ b/Http/HttpUtils.php
@@ -15,7 +15,8 @@ use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\Routing\RouterInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
@@ -26,16 +27,19 @@ use Symfony\Component\Routing\Exception\ResourceNotFoundException;
*/
class HttpUtils
{
- private $router;
+ private $urlGenerator;
+ private $urlMatcher;
/**
* Constructor.
*
- * @param RouterInterface $router An RouterInterface instance
+ * @param UrlGeneratorInterface $urlGenerator A UrlGeneratorInterface instance
+ * @param UrlMatcherInterface $urlMatcher A UrlMatcherInterface instance
*/
- public function __construct(RouterInterface $router = null)
+ public function __construct(UrlGeneratorInterface $urlGenerator = null, UrlMatcherInterface $urlMatcher = null)
{
- $this->router = $router;
+ $this->urlGenerator = $urlGenerator;
+ $this->urlMatcher = $urlMatcher;
}
/**
@@ -49,14 +53,7 @@ class HttpUtils
*/
public function createRedirectResponse(Request $request, $path, $status = 302)
{
- if ('/' === $path[0]) {
- $path = $request->getUriForPath($path);
- } elseif (0 !== strpos($path, 'http')) {
- $this->resetLocale($request);
- $path = $this->generateUrl($path, true);
- }
-
- return new RedirectResponse($path, $status);
+ return new RedirectResponse($this->generateUri($request, $path), $status);
}
/**
@@ -69,15 +66,7 @@ class HttpUtils
*/
public function createRequest(Request $request, $path)
{
- if ($path && '/' !== $path[0] && 0 !== strpos($path, 'http')) {
- $this->resetLocale($request);
- $path = $this->generateUrl($path, true);
- }
- if (0 !== strpos($path, 'http')) {
- $path = $request->getUriForPath($path);
- }
-
- $newRequest = Request::create($path, 'get', array(), $request->cookies->all(), array(), $request->server->all());
+ $newRequest = Request::create($this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all());
if ($session = $request->getSession()) {
$newRequest->setSession($session);
}
@@ -99,7 +88,7 @@ class HttpUtils
* Checks that a given path matches the Request.
*
* @param Request $request A Request instance
- * @param string $path A path (an absolute path (/foo) or a route name (foo))
+ * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo))
*
* @return Boolean true if the path is the same as the one from the Request, false otherwise
*/
@@ -107,7 +96,7 @@ class HttpUtils
{
if ('/' !== $path[0]) {
try {
- $parameters = $this->router->match($request->getPathInfo());
+ $parameters = $this->urlMatcher->match($request->getPathInfo());
return $path === $parameters['_route'];
} catch (MethodNotAllowedException $e) {
@@ -120,33 +109,33 @@ class HttpUtils
return $path === $request->getPathInfo();
}
- // hack (don't have a better solution for now)
- private function resetLocale(Request $request)
+ /**
+ * Generates a URI, based on the given path or absolute URL.
+ *
+ * @param Request $request A Request instance
+ * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo))
+ *
+ * @return string An absolute URL
+ */
+ public function generateUri($request, $path)
{
- $context = $this->router->getContext();
- if ($context->getParameter('_locale')) {
- return;
+ if (0 === strpos($path, 'http') || !$path) {
+ return $path;
}
- try {
- $parameters = $this->router->match($request->getPathInfo());
-
- if (isset($parameters['_locale'])) {
- $context->setParameter('_locale', $parameters['_locale']);
- } elseif ($session = $request->getSession()) {
- $context->setParameter('_locale', $session->getLocale());
- }
- } catch (\Exception $e) {
- // let's hope user doesn't use the locale in the path
+ if ('/' === $path[0]) {
+ return $request->getUriForPath($path);
}
+
+ return $this->generateUrl($path, true);
}
private function generateUrl($route, $absolute = false)
{
- if (null === $this->router) {
- throw new \LogicException('You must provide a RouterInterface instance to be able to use routes.');
+ if (null === $this->urlGenerator) {
+ throw new \LogicException('You must provide a UrlGeneratorInterface instance to be able to use routes.');
}
- return $this->router->generate($route, array(), $absolute);
+ return $this->urlGenerator->generate($route, array(), $absolute);
}
}
diff --git a/Http/Logout/CookieClearingLogoutHandler.php b/Http/Logout/CookieClearingLogoutHandler.php
index ddb24e3..6838be5 100644
--- a/Http/Logout/CookieClearingLogoutHandler.php
+++ b/Http/Logout/CookieClearingLogoutHandler.php
@@ -40,8 +40,6 @@ class CookieClearingLogoutHandler implements LogoutHandlerInterface
* @param Request $request
* @param Response $response
* @param TokenInterface $token
- *
- * @return void
*/
public function logout(Request $request, Response $response, TokenInterface $token)
{
diff --git a/Http/Logout/DefaultLogoutSuccessHandler.php b/Http/Logout/DefaultLogoutSuccessHandler.php
new file mode 100644
index 0000000..e06cb6d
--- /dev/null
+++ b/Http/Logout/DefaultLogoutSuccessHandler.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\Http\Logout;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\HttpUtils;
+use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
+
+/**
+ * Default logout success handler will redirect users to a configured path.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Alexander <iam.asm89@gmail.com>
+ */
+class DefaultLogoutSuccessHandler implements LogoutSuccessHandlerInterface
+{
+ protected $httpUtils;
+ protected $targetUrl;
+
+ /**
+ * @param HttpUtils $httpUtils
+ * @param string $targetUrl
+ */
+ public function __construct(HttpUtils $httpUtils, $targetUrl = '/')
+ {
+ $this->httpUtils = $httpUtils;
+
+ $this->targetUrl = $targetUrl;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function onLogoutSuccess(Request $request)
+ {
+ return $this->httpUtils->createRedirectResponse($request, $this->targetUrl);
+ }
+}
diff --git a/Http/Logout/LogoutHandlerInterface.php b/Http/Logout/LogoutHandlerInterface.php
index 6780e4d..5e1cea8 100644
--- a/Http/Logout/LogoutHandlerInterface.php
+++ b/Http/Logout/LogoutHandlerInterface.php
@@ -30,8 +30,6 @@ interface LogoutHandlerInterface
* @param Request $request
* @param Response $response
* @param TokenInterface $token
- *
- * @return void
*/
public function logout(Request $request, Response $response, TokenInterface $token);
}
diff --git a/Http/Logout/LogoutSuccessHandlerInterface.php b/Http/Logout/LogoutSuccessHandlerInterface.php
index ad878e6..61642a8 100644
--- a/Http/Logout/LogoutSuccessHandlerInterface.php
+++ b/Http/Logout/LogoutSuccessHandlerInterface.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Logout;
diff --git a/Http/Logout/SessionLogoutHandler.php b/Http/Logout/SessionLogoutHandler.php
index 0a7e5cd..e91cf17 100644
--- a/Http/Logout/SessionLogoutHandler.php
+++ b/Http/Logout/SessionLogoutHandler.php
@@ -28,8 +28,6 @@ class SessionLogoutHandler implements LogoutHandlerInterface
* @param Request $request
* @param Response $response
* @param TokenInterface $token
- *
- * @return void
*/
public function logout(Request $request, Response $response, TokenInterface $token)
{
diff --git a/Http/RememberMe/AbstractRememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php
index 92472ee..4f7c5b9 100644
--- a/Http/RememberMe/AbstractRememberMeServices.php
+++ b/Http/RememberMe/AbstractRememberMeServices.php
@@ -1,5 +1,14 @@
<?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\Http\RememberMe;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
@@ -15,15 +24,6 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
-/*
- * 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.
- */
-
/**
* Base class implementing the RememberMeServicesInterface
*
@@ -144,8 +144,6 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
* @param Request $request
* @param Response $response
* @param TokenInterface $token
- *
- * @return void
*/
public function logout(Request $request, Response $response, TokenInterface $token)
{
@@ -157,8 +155,6 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
* an attempted authentication fails.
*
* @param Request $request
- *
- * @return void
*/
final public function loginFail(Request $request)
{
@@ -173,8 +169,6 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
* @param Request $request
* @param Response $response
* @param TokenInterface $token The token that resulted in a successful authentication
- *
- * @return void
*/
final public function loginSuccess(Request $request, Response $response, TokenInterface $token)
{
@@ -224,8 +218,6 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
* @param Request $request
* @param Response $response
* @param TokenInterface $token
- *
- * @return void
*/
abstract protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token);
@@ -268,8 +260,6 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
* Deletes the remember-me cookie
*
* @param Request $request
- *
- * @return void
*/
protected function cancelCookie(Request $request)
{
@@ -293,7 +283,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
return true;
}
- $parameter = $request->request->get($this->options['remember_me_parameter'], null, true);
+ $parameter = $request->get($this->options['remember_me_parameter'], null, true);
if ($parameter === null && null !== $this->logger) {
$this->logger->debug(sprintf('Did not send remember-me cookie (remember-me parameter "%s" was not sent).', $this->options['remember_me_parameter']));
diff --git a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
index e9d22ba..8944672 100644
--- a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
+++ b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
@@ -1,5 +1,14 @@
<?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\Http\RememberMe;
use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface;
@@ -11,15 +20,6 @@ use Symfony\Component\Security\Core\Exception\CookieTheftException;
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
-/*
- * 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.
- */
-
/**
* Concrete implementation of the RememberMeServicesInterface which needs
* an implementation of TokenProviderInterface for providing remember-me
@@ -35,8 +35,6 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
* Sets the token provider
*
* @param TokenProviderInterface $tokenProvider
- *
- * @return void
*/
public function setTokenProvider(TokenProviderInterface $tokenProvider)
{
diff --git a/Http/RememberMe/RememberMeServicesInterface.php b/Http/RememberMe/RememberMeServicesInterface.php
index 116a6b1..a00dcef 100644
--- a/Http/RememberMe/RememberMeServicesInterface.php
+++ b/Http/RememberMe/RememberMeServicesInterface.php
@@ -60,8 +60,6 @@ interface RememberMeServicesInterface
* This method needs to take care of invalidating the cookie.
*
* @param Request $request
- *
- * @return void
*/
public function loginFail(Request $request);
@@ -79,8 +77,6 @@ interface RememberMeServicesInterface
* @param Request $request
* @param Response $response
* @param TokenInterface $token
- *
- * @return void
*/
public function loginSuccess(Request $request, Response $response, TokenInterface $token);
}
diff --git a/Http/RememberMe/ResponseListener.php b/Http/RememberMe/ResponseListener.php
new file mode 100644
index 0000000..6cbdcb3
--- /dev/null
+++ b/Http/RememberMe/ResponseListener.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\Http\RememberMe;
+
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+
+/**
+ * Adds remember-me cookies to the Response.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class ResponseListener
+{
+ public function onKernelResponse(FilterResponseEvent $event)
+ {
+ $request = $event->getRequest();
+ $response = $event->getResponse();
+
+ if ($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)) {
+ $response->headers->setCookie($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME));
+ }
+ }
+}
diff --git a/Http/RememberMe/TokenBasedRememberMeServices.php b/Http/RememberMe/TokenBasedRememberMeServices.php
index aa76228..bd828f2 100644
--- a/Http/RememberMe/TokenBasedRememberMeServices.php
+++ b/Http/RememberMe/TokenBasedRememberMeServices.php
@@ -1,14 +1,5 @@
<?php
-namespace Symfony\Component\Security\Http\RememberMe;
-
-use Symfony\Component\HttpFoundation\Cookie;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
-use Symfony\Component\Security\Core\Exception\AuthenticationException;
-use Symfony\Component\Security\Core\User\UserInterface;
-
/*
* This file is part of the Symfony package.
*
@@ -18,6 +9,15 @@ use Symfony\Component\Security\Core\User\UserInterface;
* file that was distributed with this source code.
*/
+namespace Symfony\Component\Security\Http\RememberMe;
+
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\User\UserInterface;
+
/**
* Concrete implementation of the RememberMeServicesInterface providing
* remember-me capabilities without requiring a TokenProvider.
diff --git a/Http/Session/SessionAuthenticationStrategy.php b/Http/Session/SessionAuthenticationStrategy.php
index 7e0c20a..e9c9826 100644
--- a/Http/Session/SessionAuthenticationStrategy.php
+++ b/Http/Session/SessionAuthenticationStrategy.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Session;
diff --git a/Http/Session/SessionAuthenticationStrategyInterface.php b/Http/Session/SessionAuthenticationStrategyInterface.php
index 38dc343..2bc292b 100644
--- a/Http/Session/SessionAuthenticationStrategyInterface.php
+++ b/Http/Session/SessionAuthenticationStrategyInterface.php
@@ -1,12 +1,12 @@
<?php
/*
- * This file is part of the Symfony framework.
+ * This file is part of the Symfony package.
*
* (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.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Session;
@@ -32,8 +32,6 @@ interface SessionAuthenticationStrategyInterface
*
* @param Request $request
* @param TokenInterface $token
- *
- * @return void
*/
public function onAuthentication(Request $request, TokenInterface $token);
}
diff --git a/README.md b/README.md
index 7aea890..7ae97b1 100644
--- a/README.md
+++ b/README.md
@@ -9,10 +9,18 @@ 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
+
+If you also want to run the unit tests that depend on other Symfony
+Components, install dev dependencies before running PHPUnit:
+
+ php composer.phar install --dev
diff --git a/Tests/Acl/Dbal/AclProviderBenchmarkTest.php b/Tests/Acl/Dbal/AclProviderBenchmarkTest.php
new file mode 100644
index 0000000..8f6a30c
--- /dev/null
+++ b/Tests/Acl/Dbal/AclProviderBenchmarkTest.php
@@ -0,0 +1,271 @@
+<?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..837daad
--- /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 testUpdateAclThrowsExceptionOnConcurrentModificationOfSharedProperties()
+ {
+ $provider = $this->getProvider();
+ $acl1 = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $acl2 = $provider->createAcl(new ObjectIdentity(2, 'Foo'));
+ $acl3 = $provider->createAcl(new ObjectIdentity(1, 'AnotherFoo'));
+ $sid = new RoleSecurityIdentity('ROLE_FOO');
+
+ $acl1->insertClassAce($sid, 1);
+ $acl3->insertClassAce($sid, 1);
+ $provider->updateAcl($acl1);
+ $provider->updateAcl($acl3);
+
+ $acl2->insertClassAce($sid, 16);
+ $provider->updateAcl($acl2);
+
+ $acl1->insertClassAce($sid, 3);
+ $acl2->insertClassAce($sid, 5);
+ try {
+ $provider->updateAcl($acl1);
+ $this->fail('Provider failed to detect a concurrent modification.');
+ } catch (ConcurrentModificationException $ex) { }
+ }
+
+ public function testUpdateAcl()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $sid = new UserSecurityIdentity('johannes', 'FooClass');
+ $acl->setEntriesInheriting(!$acl->isEntriesInheriting());
+
+ $acl->insertObjectAce($sid, 1);
+ $acl->insertClassAce($sid, 5, 0, false);
+ $acl->insertObjectAce($sid, 2, 1, true);
+ $acl->insertClassFieldAce('field', $sid, 2, 0, true);
+ $provider->updateAcl($acl);
+
+ $acl->updateObjectAce(0, 3);
+ $acl->deleteObjectAce(1);
+ $acl->updateObjectAuditing(0, true, false);
+ $acl->updateClassFieldAce(0, 'field', 15);
+ $provider->updateAcl($acl);
+
+ $reloadProvider = $this->getProvider();
+ $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
+ $this->assertNotSame($acl, $reloadedAcl);
+ $this->assertSame($acl->isEntriesInheriting(), $reloadedAcl->isEntriesInheriting());
+
+ $aces = $acl->getObjectAces();
+ $reloadedAces = $reloadedAcl->getObjectAces();
+ $this->assertEquals(count($aces), count($reloadedAces));
+ foreach ($aces as $index => $ace) {
+ $this->assertAceEquals($ace, $reloadedAces[$index]);
+ }
+ }
+
+ public function testUpdateAclWorksForChangingTheParentAcl()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+ $parentAcl = $provider->createAcl(new ObjectIdentity(1, 'AnotherFoo'));
+ $acl->setParentAcl($parentAcl);
+ $provider->updateAcl($acl);
+
+ $reloadProvider = $this->getProvider();
+ $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
+ $this->assertNotSame($acl, $reloadedAcl);
+ $this->assertSame($parentAcl->getId(), $reloadedAcl->getParentAcl()->getId());
+ }
+
+ public function testUpdateAclUpdatesChildAclsCorrectly()
+ {
+ $provider = $this->getProvider();
+ $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
+
+ $parentAcl = $provider->createAcl(new ObjectIdentity(1, 'Bar'));
+ $acl->setParentAcl($parentAcl);
+ $provider->updateAcl($acl);
+
+ $parentParentAcl = $provider->createAcl(new ObjectIdentity(1, 'Baz'));
+ $parentAcl->setParentAcl($parentParentAcl);
+ $provider->updateAcl($parentAcl);
+
+ $newParentParentAcl = $provider->createAcl(new ObjectIdentity(2, 'Baz'));
+ $parentAcl->setParentAcl($newParentParentAcl);
+ $provider->updateAcl($parentAcl);
+
+ $reloadProvider = $this->getProvider();
+ $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
+ $this->assertEquals($newParentParentAcl->getId(), $reloadedAcl->getParentAcl()->getParentAcl()->getId());
+ }
+
+ /**
+ * 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..4b67e62
--- /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..7aa3cf2
--- /dev/null
+++ b/Tests/Acl/Domain/ObjectIdentityTest.php
@@ -0,0 +1,116 @@
+<?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());
+ }
+
+ // Test that constructor never changes passed type, even with proxies
+ public function testConstructorWithProxy()
+ {
+ $id = new ObjectIdentity('fooid', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject');
+
+ $this->assertEquals('fooid', $id->getIdentifier());
+ $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject', $id->getType());
+ }
+
+ public function testFromDomainObjectPrefersInterfaceOverGetId()
+ {
+ $domainObject = $this->getMock('Symfony\Component\Security\Acl\Model\DomainObjectInterface');
+ $domainObject
+ ->expects($this->once())
+ ->method('getObjectIdentifier')
+ ->will($this->returnValue('getObjectIdentifier()'))
+ ;
+ $domainObject
+ ->expects($this->never())
+ ->method('getId')
+ ->will($this->returnValue('getId()'))
+ ;
+
+ $id = ObjectIdentity::fromDomainObject($domainObject);
+ $this->assertEquals('getObjectIdentifier()', $id->getIdentifier());
+ }
+
+ public function testFromDomainObjectWithoutInterface()
+ {
+ $id = ObjectIdentity::fromDomainObject(new TestDomainObject());
+ $this->assertEquals('getId()', $id->getIdentifier());
+ $this->assertEquals('Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject', $id->getType());
+ }
+
+ public function testFromDomainObjectWithProxy()
+ {
+ $id = ObjectIdentity::fromDomainObject(new \Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject());
+ $this->assertEquals('getId()', $id->getIdentifier());
+ $this->assertEquals('Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject', $id->getType());
+ }
+
+ /**
+ * @dataProvider getCompareData
+ */
+ public function testEquals($oid1, $oid2, $equal)
+ {
+ if ($equal) {
+ $this->assertTrue($oid1->equals($oid2));
+ } else {
+ $this->assertFalse($oid1->equals($oid2));
+ }
+ }
+
+ public function getCompareData()
+ {
+ return array(
+ array(new ObjectIdentity('123', 'foo'), new ObjectIdentity('123', 'foo'), true),
+ array(new ObjectIdentity('123', 'foo'), new ObjectIdentity(123, 'foo'), true),
+ array(new ObjectIdentity('1', 'foo'), new ObjectIdentity('2', 'foo'), false),
+ array(new ObjectIdentity('1', 'bla'), new ObjectIdentity('1', 'blub'), false),
+ );
+ }
+
+ 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()';
+ }
+ }
+}
+
+namespace Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain
+{
+ class TestDomainObject extends \Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject
+ {
+ }
+}
diff --git a/Tests/Acl/Domain/PermissionGrantingStrategyTest.php b/Tests/Acl/Domain/PermissionGrantingStrategyTest.php
new file mode 100644
index 0000000..f34bc3e
--- /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..f649653
--- /dev/null
+++ b/Tests/Acl/Domain/SecurityIdentityRetrievalStrategyTest.php
@@ -0,0 +1,196 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\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..1d6a3c5
--- /dev/null
+++ b/Tests/Acl/Domain/UserSecurityIdentityTest.php
@@ -0,0 +1,73 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\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());
+ }
+
+ // Test that constructor never changes the type, even for proxies
+ public function testConstructorWithProxy()
+ {
+ $id = new UserSecurityIdentity('foo', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\Foo');
+
+ $this->assertEquals('foo', $id->getUsername());
+ $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\Foo', $id->getClass());
+ }
+
+ /**
+ * @dataProvider getCompareData
+ */
+ public function testEquals($id1, $id2, $equal)
+ {
+ $this->assertSame($equal, $id1->equals($id2));
+ }
+
+ public function getCompareData()
+ {
+ $account = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')
+ ->setMockClassName('USI_AccountImpl')
+ ->getMock();
+ $account
+ ->expects($this->any())
+ ->method('getUsername')
+ ->will($this->returnValue('foo'))
+ ;
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($account))
+ ;
+
+ return array(
+ array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('foo', 'Foo'), true),
+ array(new UserSecurityIdentity('foo', 'Bar'), new UserSecurityIdentity('foo', 'Foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('bar', 'Foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromAccount($account), false),
+ array(new UserSecurityIdentity('bla', 'Foo'), new UserSecurityIdentity('blub', 'Foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), new RoleSecurityIdentity('foo'), false),
+ array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromToken($token), false),
+ array(new UserSecurityIdentity('foo', 'USI_AccountImpl'), UserSecurityIdentity::fromToken($token), true),
+ );
+ }
+}
diff --git a/Tests/Acl/Permission/BasicPermissionMapTest.php b/Tests/Acl/Permission/BasicPermissionMapTest.php
new file mode 100644
index 0000000..743634b
--- /dev/null
+++ b/Tests/Acl/Permission/BasicPermissionMapTest.php
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\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..4da0337
--- /dev/null
+++ b/Tests/Core/Authentication/Provider/DaoAuthenticationProviderTest.php
@@ -0,0 +1,300 @@
+<?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()
+ {
+ $encoder = $this->getMock('Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface');
+ $encoder
+ ->expects($this->never())
+ ->method('isPasswordValid')
+ ;
+
+ $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(''))
+ ;
+
+ $method->invoke(
+ $provider,
+ $this->getMock('Symfony\\Component\\Security\\Core\\User\\UserInterface'),
+ $token
+ );
+ }
+
+ public function testCheckAuthenticationWhenCredentialsAre0()
+ {
+ $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('0'))
+ ;
+
+ $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..fcc2514
--- /dev/null
+++ b/Tests/Core/Authentication/Provider/RememberMeAuthenticationProviderTest.php
@@ -0,0 +1,111 @@
+<?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\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..3944fb1
--- /dev/null
+++ b/Tests/Core/Authentication/RememberMe/InMemoryTokenProviderTest.php
@@ -0,0 +1,63 @@
+<?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\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..3903591
--- /dev/null
+++ b/Tests/Core/Authentication/RememberMe/PersistentTokenTest.php
@@ -0,0 +1,29 @@
+<?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\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..03275fa
--- /dev/null
+++ b/Tests/Core/Authentication/Token/RememerMeTokenTest.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\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..b077712
--- /dev/null
+++ b/Tests/Core/Authorization/Voter/AuthenticatedVoterTest.php
@@ -0,0 +1,78 @@
+<?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;
+
+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..2e55a4b
--- /dev/null
+++ b/Tests/Core/Encoder/EncoderFactoryTest.php
@@ -0,0 +1,94 @@
+<?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;
+use Symfony\Component\Security\Core\User\User;
+use Symfony\Component\Security\Core\User\UserInterface;
+
+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', ''));
+
+ $encoder = $factory->getEncoder(new User('user', 'pass'));
+ $expectedEncoder = new MessageDigestPasswordEncoder('sha1');
+ $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', ''));
+ }
+
+ public function testGetEncoderWithClassName()
+ {
+ $factory = new EncoderFactory(array(
+ 'Symfony\Component\Security\Core\User\UserInterface' => new MessageDigestPasswordEncoder('sha1'),
+ ));
+
+ $encoder = $factory->getEncoder('Symfony\Component\Security\Tests\Core\Encoder\SomeChildUser');
+ $expectedEncoder = new MessageDigestPasswordEncoder('sha1');
+ $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', ''));
+ }
+
+ public function testGetEncoderConfiguredForConcreteClassWithService()
+ {
+ $factory = new EncoderFactory(array(
+ 'Symfony\Component\Security\Core\User\User' => new MessageDigestPasswordEncoder('sha1'),
+ ));
+
+ $encoder = $factory->getEncoder(new User('user', 'pass'));
+ $expectedEncoder = new MessageDigestPasswordEncoder('sha1');
+ $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', ''));
+ }
+
+ public function testGetEncoderConfiguredForConcreteClassWithClassName()
+ {
+ $factory = new EncoderFactory(array(
+ 'Symfony\Component\Security\Tests\Core\Encoder\SomeUser' => new MessageDigestPasswordEncoder('sha1'),
+ ));
+
+ $encoder = $factory->getEncoder('Symfony\Component\Security\Tests\Core\Encoder\SomeChildUser');
+ $expectedEncoder = new MessageDigestPasswordEncoder('sha1');
+ $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', ''));
+ }
+}
+
+class SomeUser implements UserInterface
+{
+ public function getRoles() {}
+ public function getPassword() {}
+ public function getSalt() {}
+ public function getUsername() {}
+ public function eraseCredentials() {}
+}
+
+class SomeChildUser extends SomeUser
+{
+}
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..5edbbed
--- /dev/null
+++ b/Tests/Core/User/ChainUserProviderTest.php
@@ -0,0 +1,185 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\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/Core/Util/ClassUtilsTest.php b/Tests/Core/Util/ClassUtilsTest.php
new file mode 100644
index 0000000..16378a6
--- /dev/null
+++ b/Tests/Core/Util/ClassUtilsTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Symfony\Component\Security\Tests\Core\Util
+{
+ use Symfony\Component\Security\Core\Util\ClassUtils;
+
+ class ClassUtilsTest extends \PHPUnit_Framework_TestCase
+ {
+ static public function dataGetClass()
+ {
+ return array(
+ array('stdClass', 'stdClass'),
+ array('Symfony\Component\Security\Core\Util\ClassUtils', 'Symfony\Component\Security\Core\Util\ClassUtils'),
+ array('MyProject\Proxies\__CG__\stdClass', 'stdClass'),
+ array('MyProject\Proxies\__CG__\OtherProject\Proxies\__CG__\stdClass', 'stdClass'),
+ array('MyProject\Proxies\__CG__\Symfony\Component\Security\Tests\Core\Util\ChildObject', 'Symfony\Component\Security\Tests\Core\Util\ChildObject'),
+ array(new TestObject(), 'Symfony\Component\Security\Tests\Core\Util\TestObject'),
+ array(new \Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Core\Util\TestObject(), 'Symfony\Component\Security\Tests\Core\Util\TestObject'),
+ );
+ }
+
+ /**
+ * @dataProvider dataGetClass
+ */
+ public function testGetRealClass($object, $expectedClassName)
+ {
+ $this->assertEquals($expectedClassName, ClassUtils::getRealClass($object));
+ }
+ }
+
+ class TestObject
+ {
+ }
+}
+
+namespace Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Core\Util
+{
+ class TestObject extends \Symfony\Component\Security\Tests\Core\Util\TestObject
+ {
+ }
+}
diff --git a/Tests/Http/EntryPoint/BasicAuthenticationEntryPointTest.php b/Tests/Http/EntryPoint/BasicAuthenticationEntryPointTest.php
new file mode 100644
index 0000000..b442309
--- /dev/null
+++ b/Tests/Http/EntryPoint/BasicAuthenticationEntryPointTest.php
@@ -0,0 +1,52 @@
+<?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\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..ae0e3cc
--- /dev/null
+++ b/Tests/Http/EntryPoint/DigestAuthenticationEntryPointTest.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\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..1cf2c2d
--- /dev/null
+++ b/Tests/Http/EntryPoint/FormAuthenticationEntryPointTest.php
@@ -0,0 +1,75 @@
+<?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\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($subRequest), $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..91de1ca
--- /dev/null
+++ b/Tests/Http/EntryPoint/RetryAuthenticationEntryPointTest.php
@@ -0,0 +1,71 @@
+<?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\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..e3ffbfc
--- /dev/null
+++ b/Tests/Http/Firewall/AccessListenerTest.php
@@ -0,0 +1,223 @@
+<?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\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..9e7ea90
--- /dev/null
+++ b/Tests/Http/Firewall/AnonymousAuthenticationListenerTest.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\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..667869f
--- /dev/null
+++ b/Tests/Http/Firewall/BasicAuthenticationListenerTest.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\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..17bf0a0
--- /dev/null
+++ b/Tests/Http/Firewall/ChannelListenerTest.php
@@ -0,0 +1,196 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\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..d360ef5
--- /dev/null
+++ b/Tests/Http/Firewall/ContextListenerTest.php
@@ -0,0 +1,171 @@
+<?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\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());
+ }
+
+ /**
+ * @dataProvider provideInvalidToken
+ */
+ public function testInvalidTokenInSession($token)
+ {
+ $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $event->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request));
+ $request->expects($this->any())
+ ->method('hasPreviousSession')
+ ->will($this->returnValue(true));
+ $request->expects($this->any())
+ ->method('getSession')
+ ->will($this->returnValue($session));
+ $session->expects($this->any())
+ ->method('get')
+ ->with('_security_key123')
+ ->will($this->returnValue(serialize($token)));
+ $context->expects($this->once())
+ ->method('setToken')
+ ->with(null);
+
+ $listener = new ContextListener($context, array(), 'key123');
+ $listener->handle($event);
+ }
+
+ public function provideInvalidToken()
+ {
+ return array(
+ array(new \__PHP_Incomplete_Class()),
+ array(null),
+ );
+ }
+}
diff --git a/Tests/Http/Firewall/LogoutListenerTest.php b/Tests/Http/Firewall/LogoutListenerTest.php
new file mode 100644
index 0000000..aa0f5a7
--- /dev/null
+++ b/Tests/Http/Firewall/LogoutListenerTest.php
@@ -0,0 +1,258 @@
+<?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\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()
+ {
+ $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($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(),
+ $successHandler ?: $this->getSuccessHandler(),
+ $options = array(
+ 'csrf_parameter' => '_csrf_token',
+ 'intention' => 'logout',
+ 'logout_path' => '/logout',
+ 'target_url' => '/',
+ ),
+ $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..5f31273
--- /dev/null
+++ b/Tests/Http/Firewall/RememberMeListenerTest.php
@@ -0,0 +1,199 @@
+<?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\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..c7f13e1
--- /dev/null
+++ b/Tests/Http/FirewallMapTest.php
@@ -0,0 +1,128 @@
+<?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;
+
+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..0c1d82c
--- /dev/null
+++ b/Tests/Http/FirewallTest.php
@@ -0,0 +1,123 @@
+<?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;
+
+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..a30051f
--- /dev/null
+++ b/Tests/Http/HttpUtilsTest.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\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->getUrlGenerator());
+
+ // 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($urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
+ $urlGenerator
+ ->expects($this->any())
+ ->method('generate')
+ ->with('foobar', array(), true)
+ ->will($this->returnValue('http://localhost/foo/bar'))
+ ;
+ $urlGenerator
+ ->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->getUrlGenerator());
+
+ // 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($urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
+ $urlGenerator
+ ->expects($this->once())
+ ->method('generate')
+ ->will($this->returnValue('/foo/bar'))
+ ;
+ $urlGenerator
+ ->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->getUrlGenerator());
+
+ $this->assertTrue($utils->checkRequestPath($this->getRequest(), '/'));
+ $this->assertFalse($utils->checkRequestPath($this->getRequest(), '/foo'));
+
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('match')
+ ->will($this->throwException(new ResourceNotFoundException()))
+ ;
+ $utils = new HttpUtils(null, $urlMatcher);
+ $this->assertFalse($utils->checkRequestPath($this->getRequest(), 'foobar'));
+
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('match')
+ ->will($this->returnValue(array('_route' => 'foobar')))
+ ;
+ $utils = new HttpUtils(null, $urlMatcher);
+ $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo/bar'), 'foobar'));
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testCheckRequestPathWithUrlMatcherLoadingException()
+ {
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('match')
+ ->will($this->throwException(new \RuntimeException()))
+ ;
+ $utils = new HttpUtils(null, $urlMatcher);
+ $utils->checkRequestPath($this->getRequest(), 'foobar');
+ }
+
+ private function getUrlGenerator()
+ {
+ $urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
+ $urlGenerator
+ ->expects($this->any())
+ ->method('generate')
+ ->will($this->returnValue('/foo/bar'))
+ ;
+
+ return $urlGenerator;
+ }
+
+ 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..fc8dffb
--- /dev/null
+++ b/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php
@@ -0,0 +1,268 @@
+<?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\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..3b3691d
--- /dev/null
+++ b/Tests/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php
@@ -0,0 +1,335 @@
+<?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\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..407db02
--- /dev/null
+++ b/Tests/Http/RememberMe/TokenBasedRememberMeServicesTest.php
@@ -0,0 +1,280 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\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..84ae3a6
--- /dev/null
+++ b/Tests/bootstrap.php
@@ -0,0 +1,22 @@
+<?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) {
+ 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;
+ }
+ }
+});
+
+if (file_exists($loader = __DIR__.'/../vendor/autoload.php')) {
+ require_once $loader;
+}
diff --git a/composer.json b/composer.json
index 1bb1d56..0cf0a30 100644
--- a/composer.json
+++ b/composer.json
@@ -16,19 +16,34 @@
}
],
"require": {
- "php": ">=5.3.2",
- "symfony/event-dispatcher": "self.version",
- "symfony/http-foundation": "self.version",
- "symfony/http-kernel": "self.version"
+ "php": ">=5.3.3",
+ "symfony/event-dispatcher": "2.1.*",
+ "symfony/http-foundation": "2.1.*",
+ "symfony/http-kernel": "2.1.*"
+ },
+ "require-dev": {
+ "symfony/form": "2.1.*",
+ "symfony/routing": "2.1.*",
+ "symfony/validator": "2.1.*",
+ "doctrine/common": ">=2.2,<2.4-dev",
+ "doctrine/dbal": ">=2.2,<2.4-dev"
},
"suggest": {
- "symfony/class-loader": "self.version",
- "symfony/finder": "self.version",
- "symfony/form": "self.version",
- "symfony/routing": "self.version"
+ "symfony/class-loader": "2.1.*",
+ "symfony/finder": "2.1.*",
+ "symfony/form": "2.1.*",
+ "symfony/validator": "2.1.*",
+ "symfony/routing": "2.1.*",
+ "doctrine/dbal": "to use the built-in ACL implementation"
},
"autoload": {
"psr-0": { "Symfony\\Component\\Security": "" }
},
- "target-dir": "Symfony/Component/Security"
+ "target-dir": "Symfony/Component/Security",
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ }
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..0560cf5
--- /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>./vendor</directory>
+ <directory>./Tests</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>