fixed user refreshing after unserialization

This commit is contained in:
Johannes Schmitt 2010-12-08 13:51:26 +01:00 committed by Fabien Potencier
parent c9f08c0a68
commit 3c692bd160
21 changed files with 512 additions and 93 deletions

View File

@ -9,11 +9,21 @@ class EntityUserProvider implements UserProviderInterface
{
protected $repository;
protected $property;
protected $name;
public function __construct($em, $class, $property = null)
public function __construct($em, $name, $class, $property = null)
{
$this->repository = $em->getRepository($class);
$this->property = $property;
$this->name = $name;
}
/**
* {@inheritDoc}
*/
public function isAggregate()
{
return false;
}
/**
@ -35,6 +45,14 @@ class EntityUserProvider implements UserProviderInterface
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
}
return $user;
return array($user, $this->name);
}
/**
* {@inheritDoc}
*/
public function supports($providerName)
{
return $this->name === $providerName;
}
}

View File

@ -9,11 +9,21 @@ class DocumentUserProvider implements UserProviderInterface
{
protected $repository;
protected $property;
protected $name;
public function __construct($em, $class, $property = null)
public function __construct($em, $name, $class, $property = null)
{
$this->repository = $em->getRepository($class);
$this->property = $property;
$this->name = $name;
}
/**
* {@inheritDoc}
*/
public function isAggregate()
{
return false;
}
/**
@ -35,6 +45,14 @@ class DocumentUserProvider implements UserProviderInterface
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
}
return $user;
return array($user, $this->name);
}
/**
* {@inheritDoc}
*/
public function supports($providerName)
{
return $this->name === $providerName;
}
}

View File

@ -126,12 +126,22 @@ class SecurityExtension extends Extension
protected function createFirewalls($config, ContainerBuilder $container)
{
$providerIds = $this->createAuthenticationProviders($config, $container);
$providerIds = $this->createUserProviders($config, $container);
if (!$firewalls = $this->fixConfig($config, 'firewall')) {
return;
}
// make the ContextListener aware of the configured user providers
$definition = $container->getDefinition('security.context_listener');
$arguments = $definition->getArguments();
$userProviders = array();
foreach (array_keys($providerIds) as $userProviderId) {
$userProviders[] = new Reference($userProviderId);
}
$arguments[1] = $userProviders;
$definition->setArguments($arguments);
// load service templates
$c = new ContainerBuilder($container->getParameterBag());
$loader = new XmlFileLoader($c, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config'));
@ -204,8 +214,8 @@ class SecurityExtension extends Extension
// Logout listener
if (array_key_exists('logout', $firewall)) {
$listenerId = 'security.logout_listener.'.$id;
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.logout_listener'));
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.logout_listener'));
$listeners[] = new Reference($listenerId);
$arguments = $listener->getArguments();
@ -217,11 +227,11 @@ class SecurityExtension extends Extension
$arguments[2] = $firewall['logout']['target'];
}
$listener->setArguments($arguments);
if (!isset($firewall['stateless']) || !$firewall['stateless']) {
$listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
}
if (count($cookies = $this->fixConfig($firewall['logout'], 'cookie')) > 0) {
$cookieHandlerId = 'security.logout.handler.cookie_clearing.'.$id;
$cookieHandler = $container->setDefinition($cookieHandlerId, clone $container->getDefinition('security.logout.handler.cookie_clearing'));
@ -308,7 +318,7 @@ class SecurityExtension extends Extension
}
// Parses user providers and returns an array of their ids
protected function createAuthenticationProviders($config, ContainerBuilder $container)
protected function createUserProviders($config, ContainerBuilder $container)
{
$providers = $this->fixConfig($config, 'provider');
if (!$providers) {
@ -318,6 +328,11 @@ class SecurityExtension extends Extension
$providerIds = array();
foreach ($providers as $name => $provider) {
list($id, $encoder) = $this->createUserDaoProvider($name, $provider, $container);
if (isset($providerIds[$id])) {
throw new \RuntimeException(sprintf('Provider names must be unique. Duplicate entry for %s.', $id));
}
$providerIds[$id] = $encoder;
}
@ -340,16 +355,14 @@ class SecurityExtension extends Extension
}
if (!$name) {
$name = md5(serialize($provider));
throw new \RuntimeException('You must define a name for each user provider.');
}
$name = $this->getUserProviderId($name);
$name = $this->getUserProviderId(strtolower($name));
// Existing DAO service provider
if (isset($provider['id'])) {
$container->setAlias($name, $provider['id']);
return array($name, $encoder);
return array($provider['id'], $encoder);
}
// Chain provider
@ -364,6 +377,7 @@ class SecurityExtension extends Extension
->register($name, '%security.user.provider.entity.class%')
->setArguments(array(
new Reference('security.user.entity_manager'),
$name,
$provider['entity']['class'],
isset($provider['entity']['property']) ? $provider['entity']['property'] : null,
));
@ -377,6 +391,7 @@ class SecurityExtension extends Extension
->register($name, '%security.user.provider.document.class%')
->setArguments(array(
new Reference('security.user.document_manager'),
$name,
$provider['document']['class'],
isset($provider['document']['property']) ? $provider['document']['property'] : null,
));
@ -385,7 +400,10 @@ class SecurityExtension extends Extension
}
// In-memory DAO provider
$definition = $container->register($name, '%security.user.provider.in_memory.class%');
$definition = $container
->register($name, '%security.user.provider.in_memory.class%')
->setArguments(array($name))
;
foreach ($this->fixConfig($provider, 'user') as $username => $user) {
if (isset($user['name'])) {
$username = $user['name'];
@ -443,8 +461,6 @@ class SecurityExtension extends Extension
return $listenerId;
}
protected function createExceptionListener($container, $id, $defaultEntryPoint)
{
$exceptionListenerId = 'security.exception_listener.'.$id;

View File

@ -156,6 +156,7 @@
<service id="security.context_listener" class="%security.context_listener.class%">
<argument type="service" id="security.context" />
<argument type="collection"></argument>
<argument type="service" id="logger" on-invalid="null" />
</service>
</services>

View File

@ -6,7 +6,7 @@
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<config>
<provider>
<provider name="foo">
<user name="foo" password="foo" roles="ROLE_USER" />
</provider>

View File

@ -88,7 +88,9 @@ class Firewall
// save current listener instances
$this->currentListeners = $listeners;
$this->currentListeners[] = $exception;
if (null !== $exception) {
$this->currentListeners[] = $exception;
}
// initiate the listener chain
$e = $this->dispatcher->notifyUntil(new Event($request, 'core.security', array('request' => $request)));

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\HttpKernel\Security\Firewall;
use Symfony\Component\Security\User\AccountInterface;
use Symfony\Component\Security\Authentication\Token\TokenInterface;
use Symfony\Component\Security\SecurityContext;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
@ -23,15 +25,18 @@ use Symfony\Component\Security\Authentication\Token\AnonymousToken;
* ContextListener manages the SecurityContext persistence through a session.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ContextListener implements ListenerInterface
{
protected $context;
protected $logger;
protected $userProviders;
public function __construct(SecurityContext $context, LoggerInterface $logger = null)
public function __construct(SecurityContext $context, array $userProviders, LoggerInterface $logger = null)
{
$this->context = $context;
$this->userProviders = $userProviders;
$this->logger = $logger;
}
@ -47,7 +52,7 @@ class ContextListener implements ListenerInterface
$dispatcher->connect('core.security', array($this, 'read'), 0);
$dispatcher->connect('core.response', array($this, 'write'), 0);
}
/**
* {@inheritDoc}
*/
@ -76,11 +81,11 @@ class ContextListener implements ListenerInterface
$token = unserialize($token);
$this->context->setToken($token);
if (null !== $token && false === $token->isImmutable()) {
$token = $this->refreshUser($token);
}
// FIXME: If the user is not an object, it probably means that it is persisted with a DAO
// we need to load it now (that does not happen right now as the Token serialize the user
// even if it is an object -- see Token)
$this->context->setToken($token);
}
}
@ -111,4 +116,61 @@ class ContextListener implements ListenerInterface
return $response;
}
/**
* Refreshes the user by reloading it from the user provider
*
* @param TokenInterface $token
* @return TokenInterface|null
*/
protected function refreshUser(TokenInterface $token)
{
$user = $token->getUser();
if (!$user instanceof AccountInterface) {
return $token;
} else if (0 === strlen($username = (string) $token)) {
return $token;
} else if (null === $providerName = $token->getUserProviderName()) {
return $token;
}
if (null !== $this->logger) {
$this->logger->debug(sprintf('Reloading user from user provider "%s".', $providerName));
}
foreach ($this->userProviders as $provider) {
if (!$provider->isAggregate() && $provider->supports($providerName)) {
try {
$result = $provider->loadUserByUsername($username);
if (!is_array($result) || 2 !== count($result)) {
throw new \RuntimeException('Provider returned an invalid result.');
}
list($cUser, $cProviderName) = $result;
} catch (\Exception $ex) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('An exception occurred while reloading the user: '.$ex->getMessage()));
}
return null;
}
if ($providerName !== $cProviderName) {
throw new \RuntimeException(sprintf('User was loaded from different provider. Requested "%s", Used: "%s"', $providerName, $cProviderName));
}
$token->setRoles($user->getRoles());
$token->setUser($cUser);
if (false === $cUser->equals($user)) {
$token->setAuthenticated(false);
}
return $token;
}
}
throw new \RuntimeException(sprintf('There is no user provider named "%s".', $providerName));
}
}

View File

@ -55,12 +55,19 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
*/
protected function checkAuthentication(AccountInterface $account, UsernamePasswordToken $token)
{
if (!$presentedPassword = (string) $token->getCredentials()) {
throw new BadCredentialsException('Bad credentials');
}
$user = $token->getUser();
if ($user instanceof AccountInterface) {
if ($account->getPassword() !== $user->getPassword()) {
throw new BadCredentialsException('The credentials were changed from another session.');
}
} else {
if (!$presentedPassword = (string) $token->getCredentials()) {
throw new BadCredentialsException('Bad credentials');
}
if (!$this->passwordEncoder->isPasswordValid($account->getPassword(), $presentedPassword, $account->getSalt())) {
throw new BadCredentialsException('Bad credentials');
if (!$this->passwordEncoder->isPasswordValid($account->getPassword(), $presentedPassword, $account->getSalt())) {
throw new BadCredentialsException('Bad credentials');
}
}
}
@ -69,19 +76,30 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
*/
protected function retrieveUser($username, UsernamePasswordToken $token)
{
$user = null;
$user = $token->getUser();
if ($user instanceof AccountInterface) {
return array($user, $token->getUserProviderName());
}
$result = null;
try {
$user = $this->userProvider->loadUserByUsername($username);
$result = $this->userProvider->loadUserByUsername($username);
} catch (UsernameNotFoundException $notFound) {
throw $notFound;
} catch (\Exception $repositoryProblem) {
throw new AuthenticationServiceException($repositoryProblem->getMessage(), $token, 0, $repositoryProblem);
}
if (!$user instanceof AccountInterface) {
if (!is_array($result) || 2 !== count($result)) {
throw new AuthenticationServiceException('User provider did not return an array, or array had invalid format.');
}
if (!$result[0] instanceof AccountInterface) {
throw new AuthenticationServiceException('The user provider must return an AccountInterface object.');
}
if (empty($result[1])) {
throw new AuthenticationServiceException('The user provider must return a non-empty user provider name.');
}
return $user;
return $result;
}
}

View File

@ -54,7 +54,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
$username = null === $token->getUser() ? 'NONE_PROVIDED' : (string) $token;
try {
$user = $this->retrieveUser($username, $token);
$result = $this->retrieveUser($username, $token);
} catch (UsernameNotFoundException $notFound) {
if ($this->hideUserNotFoundExceptions) {
throw new BadCredentialsException('Bad credentials', 0, $notFound);
@ -63,15 +63,16 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
throw $notFound;
}
if (!$user instanceof AccountInterface) {
throw new AuthenticationServiceException('The retrieveUser() methods must return an AccountInterface object.');
if (!is_array($result) || 2 !== count($result)) {
throw new AuthenticationServiceException('retrieveUser() did not return an array, or array had invalid format.');
}
list($user, $userProviderName) = $result;
$this->accountChecker->checkPreAuth($user);
$this->checkAuthentication($user, $token);
$this->accountChecker->checkPostAuth($user);
return new UsernamePasswordToken($user, $token->getCredentials(), $user->getRoles());
return new UsernamePasswordToken($user, $token->getCredentials(), $userProviderName, $user->getRoles());
}
/**
@ -88,7 +89,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
* @param string $username The username to retrieve
* @param UsernamePasswordToken $token The Token
*
* @return AccountInterface The user
* @return array The user
*
* @throws AuthenticationException if the credentials could not be validated
*/

View File

@ -19,12 +19,14 @@ use Symfony\Component\Security\User\AccountInterface;
* Base class for Token instances.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
abstract class Token implements TokenInterface
{
protected $roles;
protected $authenticated;
protected $user;
protected $userProviderName;
protected $credentials;
protected $immutable;
@ -35,13 +37,7 @@ abstract class Token implements TokenInterface
*/
public function __construct(array $roles = array())
{
$this->roles = array();
foreach ($roles as $role) {
if (is_string($role)) {
$role = new Role((string) $role);
}
$this->addRole($role);
}
$this->setRoles($roles);
$this->authenticated = false;
$this->immutable = false;
}
@ -53,6 +49,10 @@ abstract class Token implements TokenInterface
*/
public function addRole(RoleInterface $role)
{
if ($this->immutable) {
throw new \LogicException('This token is considered immutable.');
}
$this->roles[] = $role;
}
@ -64,6 +64,22 @@ abstract class Token implements TokenInterface
return $this->roles;
}
/**
* {@inheritDoc}
*/
public function setRoles(array $roles)
{
$this->roles = array();
foreach ($roles as $role) {
if (is_string($role)) {
$role = new Role($role);
}
$this->addRole($role);
}
}
/**
* {@inheritdoc}
*/
@ -91,6 +107,10 @@ abstract class Token implements TokenInterface
*/
public function setAuthenticated($authenticated)
{
if ($this->immutable) {
throw new \LogicException('This token is considered immutable.');
}
$this->authenticated = (Boolean) $authenticated;
}
@ -110,11 +130,33 @@ abstract class Token implements TokenInterface
return $this->user;
}
/**
* {@inheritDoc}
*/
public function setUser($user)
{
if ($this->immutable) {
throw new \LogicException('This token is considered immutable.');
}
if (!is_string($user) && !is_object($user)) {
throw new \InvalidArgumentException('$user must be an object, or a primitive string.');
} else if (is_object($user) && !method_exists($user, '__toString')) {
throw new \InvalidArgumentException('If $user is an object, it must implement __toString().');
}
$this->user = $user;
}
/**
* {@inheritdoc}
*/
public function eraseCredentials()
{
if ($this->immutable) {
throw new \LogicException('This token is considered immutable.');
}
if ($this->getCredentials() instanceof AccountInterface) {
$this->getCredentials()->eraseCredentials();
}
@ -124,6 +166,14 @@ abstract class Token implements TokenInterface
}
}
/**
* {@inheritDoc}
*/
public function getUserProviderName()
{
return $this->userProviderName;
}
/**
* {@inheritdoc}
*/
@ -135,9 +185,9 @@ abstract class Token implements TokenInterface
/**
* {@inheritdoc}
*/
public function setImmutable($value)
public function setImmutable()
{
$this->immutable = (Boolean) $value;
$this->immutable = true;
}
/**
@ -145,9 +195,7 @@ abstract class Token implements TokenInterface
*/
public function serialize()
{
// FIXME: don't serialize the user object, just the username (see ContextListener)
//return serialize(array((string) $this, $this->credentials, $this->authenticated, $this->roles, $this->immutable));
return serialize(array($this->user, $this->credentials, $this->authenticated, $this->roles, $this->immutable));
return serialize(array($this->user, $this->userProviderName, $this->credentials, $this->authenticated, $this->roles, $this->immutable));
}
/**
@ -155,6 +203,6 @@ abstract class Token implements TokenInterface
*/
public function unserialize($serialized)
{
list($this->user, $this->credentials, $this->authenticated, $this->roles, $this->immutable) = unserialize($serialized);
list($this->user, $this->userProviderName, $this->credentials, $this->authenticated, $this->roles, $this->immutable) = unserialize($serialized);
}
}

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\Security\Authentication\Token;
use Symfony\Component\Security\User\AccountInterface;
/*
* This file is part of the Symfony package.
*
@ -32,6 +34,14 @@ interface TokenInterface extends \Serializable
*/
function getRoles();
/**
* Sets the user's roles
*
* @param array $roles
* @return void
*/
function setRoles(array $roles);
/**
* Returns the user credentials.
*
@ -40,19 +50,28 @@ interface TokenInterface extends \Serializable
function getCredentials();
/**
* Checks whether the token is immutable or not.
* Returns a user representation.
*
* @return Boolean true if the token is immutable, false otherwise
*/
function isImmutable();
/**
* Returns a user instance.
*
* @return object The User instance
* @return mixed either returns an object which implements __toString(), or
* a primitive string is returned.
*/
function getUser();
/**
* Sets the user.
*
* @param mixed $user can either be an object which implements __toString(), or
* only a primitive string
*/
function setUser($user);
/**
* Returns a unique id for the user provider that was used to retrieve the user
*
* @return string
*/
function getUserProviderName();
/**
* Checks if the user is authenticated or not.
*
@ -67,6 +86,22 @@ interface TokenInterface extends \Serializable
*/
function setAuthenticated($isAuthenticated);
/**
* Whether this token is considered immutable
*
* @return Boolean
*/
function isImmutable();
/**
* Marks this token as immutable. This change cannot be reversed.
*
* You'll need to create a new token if you want a mutable token again.
*
* @return void
*/
function setImmutable();
/**
* Removes sensitive information from the token.
*/

View File

@ -21,12 +21,13 @@ class UsernamePasswordToken extends Token
/**
* Constructor.
*/
public function __construct($user, $credentials, array $roles = array())
public function __construct($user, $credentials, $userProviderName = null, array $roles = array())
{
parent::__construct($roles);
$this->user = $user;
$this->setUser($user);
$this->credentials = $credentials;
$this->userProviderName = $userProviderName;
parent::setAuthenticated((Boolean) count($roles));
}

View File

@ -57,4 +57,16 @@ interface AccountInterface
* Removes sensitive data from the user.
*/
function eraseCredentials();
/**
* 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 AccountInterface $account
* @return Boolean
*/
function equals(AccountInterface $account);
}

View File

@ -24,6 +24,7 @@ use Symfony\Component\Security\Exception\UsernameNotFoundException;
class InMemoryUserProvider implements UserProviderInterface
{
protected $users;
protected $name;
/**
* Constructor.
@ -32,8 +33,9 @@ class InMemoryUserProvider implements UserProviderInterface
* an array of attributes: 'password', 'enabled', and 'roles'.
*
* @param array $users An array of users
* @param string $name
*/
public function __construct(array $users = array())
public function __construct($name, array $users = array())
{
foreach ($users as $username => $attributes) {
$password = isset($attributes['password']) ? $attributes['password'] : null;
@ -43,6 +45,8 @@ class InMemoryUserProvider implements UserProviderInterface
$this->createUser($user);
}
$this->name = $name;
}
/**
@ -59,6 +63,14 @@ class InMemoryUserProvider implements UserProviderInterface
$this->users[strtolower($user->getUsername())] = $user;
}
/**
* {@inheritDoc}
*/
public function isAggregate()
{
return false;
}
/**
* {@inheritdoc}
*/
@ -70,7 +82,15 @@ class InMemoryUserProvider implements UserProviderInterface
$user = $this->users[strtolower($username)];
return new User($user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled(), $user->isAccountNonExpired(),
$user->isCredentialsNonExpired(), $user->isAccountNonLocked());
return array(new User($user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled(), $user->isAccountNonExpired(),
$user->isCredentialsNonExpired(), $user->isAccountNonLocked()), $this->name);
}
/**
* {@inheritDoc}
*/
public function supports($providerName)
{
return $this->name === $providerName;
}
}

View File

@ -121,4 +121,44 @@ class User implements AdvancedAccountInterface
{
$this->password = null;
}
/**
* {@inheritDoc}
*/
public function equals(AccountInterface $account)
{
if (!$account instanceof User) {
return false;
}
if ($this->password !== $account->getPassword()) {
return false;
}
if ($this->getSalt() !== $account->getSalt()) {
return false;
}
if ($this->username !== $account->getUsername()) {
return false;
}
if ($this->accountNonExpired !== $account->isAccountNonExpired()) {
return false;
}
if ($this->accountNonLocked !== $account->isAccountNonLocked()) {
return false;
}
if ($this->credentialsNonExpired !== $account->isCredentialsNonExpired()) {
return false;
}
if ($this->enabled !== $account->isEnabled()) {
return false;
}
return true;
}
}

View File

@ -2,8 +2,6 @@
namespace Symfony\Component\Security\User;
use Symfony\Component\Security\Exception\UsernameNotFoundException;
/*
* This file is part of the Symfony package.
*
@ -21,6 +19,13 @@ use Symfony\Component\Security\Exception\UsernameNotFoundException;
*/
interface UserProviderInterface
{
/**
* Whether this provider is an aggregate of user providers
*
* @return Boolean
*/
function isAggregate();
/**
* Loads the user for the given username.
*
@ -29,9 +34,19 @@ interface UserProviderInterface
*
* @param string $username The username
*
* @return AccountInterface A user instance
* @return array of the form: array(AccountInterface, string) with the
* implementation of AccountInterface, and the name of the provider
* that was used to retrieve it
*
* @throws UsernameNotFoundException if the user is not found
*/
function loadUserByUsername($username);
/**
* Determines whether this provider supports the given provider name
*
* @param string $providerName
* @return Boolean
*/
function supports($providerName);
}

View File

@ -62,6 +62,32 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$method->invoke($provider, 'fabien', $this->getSupportedToken());
}
public function testRetrieveUserReturnsUserFromTokenOnReauthentication()
{
$userProvider = $this->getMock('Symfony\Component\Security\User\UserProviderInterface');
$userProvider->expects($this->never())
->method('loadUserByUsername')
;
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
$token = $this->getSupportedToken();
$token->expects($this->once())
->method('getUser')
->will($this->returnValue($user))
;
$token->expects($this->once())
->method('getUserProviderName')
->will($this->returnValue('foo'))
;
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'));
$reflection = new \ReflectionMethod($provider, 'retrieveUser');
$reflection->setAccessible(true);
$result = $reflection->invoke($provider, null, $token);
$this->assertSame(array($user, 'foo'), $result);
}
public function testRetrieveUser()
{
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
@ -69,14 +95,14 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$userProvider = $this->getMock('Symfony\Component\Security\User\UserProviderInterface');
$userProvider->expects($this->once())
->method('loadUserByUsername')
->will($this->returnValue($user))
->will($this->returnValue($result = array($user, 'foo')))
;
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'));
$method = new \ReflectionMethod($provider, 'retrieveUser');
$method->setAccessible(true);
$this->assertSame($user, $method->invoke($provider, 'fabien', $this->getSupportedToken()));
$this->assertSame($result, $method->invoke($provider, 'fabien', $this->getSupportedToken()));
}
/**
@ -121,6 +147,59 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$method->invoke($provider, $this->getMock('Symfony\Component\Security\User\AccountInterface'), $token);
}
/**
* @expectedException Symfony\Component\Security\Exception\BadCredentialsException
*/
public function testCheckAuthenticationDoesNotReauthenticateWhenPasswordHasChanged()
{
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
$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\User\AccountInterface');
$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\User\AccountInterface');
$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\User\AccountInterface');
$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\Encoder\PasswordEncoderInterface');
@ -144,7 +223,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
protected function getSupportedToken()
{
return $this->getMock('Symfony\Component\Security\Authentication\Token\UsernamePasswordToken', array('getCredentials'), array(), '', false);
return $this->getMock('Symfony\Component\Security\Authentication\Token\UsernamePasswordToken', array('getCredentials', 'getUser', 'getUserProviderName'), array(), '', false);
}
protected function getProvider($user = false, $userChecker = false, $passwordEncoder = null)

View File

@ -86,7 +86,7 @@ class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$provider = $this->getProvider($userChecker);
$provider->expects($this->once())
->method('retrieveUser')
->will($this->returnValue($this->getMock('Symfony\Component\Security\User\AccountInterface')))
->will($this->returnValue(array($this->getMock('Symfony\Component\Security\User\AccountInterface'), 'foo')))
;
$provider->authenticate($this->getSupportedToken());
@ -106,7 +106,7 @@ class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$provider = $this->getProvider($userChecker);
$provider->expects($this->once())
->method('retrieveUser')
->will($this->returnValue($this->getMock('Symfony\Component\Security\User\AccountInterface')))
->will($this->returnValue(array($this->getMock('Symfony\Component\Security\User\AccountInterface'), 'foo')))
;
$provider->authenticate($this->getSupportedToken());
@ -120,7 +120,7 @@ class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$provider = $this->getProvider();
$provider->expects($this->once())
->method('retrieveUser')
->will($this->returnValue($this->getMock('Symfony\Component\Security\User\AccountInterface')))
->will($this->returnValue(array($this->getMock('Symfony\Component\Security\User\AccountInterface'), 'foo')))
;
$provider->expects($this->once())
->method('checkAuthentication')
@ -141,7 +141,7 @@ class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$provider = $this->getProvider();
$provider->expects($this->once())
->method('retrieveUser')
->will($this->returnValue($user))
->will($this->returnValue(array($user, 'foo')))
;
$token = $this->getSupportedToken();
@ -154,6 +154,7 @@ class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Symfony\Component\Security\Authentication\Token\UsernamePasswordToken', $authToken);
$this->assertSame($user, $authToken->getUser());
$this->assertSame('foo', $authToken->getUserProviderName());
$this->assertEquals(array(new Role('ROLE_FOO')), $authToken->getRoles());
$this->assertEquals('foo', $authToken->getCredentials());
}

View File

@ -15,17 +15,27 @@ use Symfony\Component\Security\Role\Role;
class Token extends BaseToken
{
public function setUser($user)
{
$this->user = $user;
}
public function setCredentials($credentials)
{
$this->credentials = $credentials;
}
}
class TestUser
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
public function __toString()
{
return $this->name;
}
}
class TokenTest extends \PHPUnit_Framework_TestCase
{
public function testMagicToString()
@ -34,7 +44,7 @@ class TokenTest extends \PHPUnit_Framework_TestCase
$token->setUser('fabien');
$this->assertEquals('fabien', (string) $token);
$token->setUser(new \stdClass('fabien'));
$token->setUser(new TestUser('fabien'));
$this->assertEquals('n/a', (string) $token);
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
@ -120,10 +130,30 @@ class TokenTest extends \PHPUnit_Framework_TestCase
$token = new Token();
$this->assertFalse($token->isImmutable());
$token->setImmutable(true);
$token->setImmutable();
$this->assertTrue($token->isImmutable());
}
$token->setImmutable(false);
$this->assertFalse($token->isImmutable());
/**
* @expectedException \LogicException
* @dataProvider getImmutabilityTests
*/
public function testImmutabilityIsEnforced($setter, $value)
{
$token = new Token();
$token->setImmutable(true);
$token->$setter($value);
}
public function getImmutabilityTests()
{
return array(
array('setUser', 'foo'),
array('eraseCredentials', null),
array('setAuthenticated', true),
array('setAuthenticated', false),
array('addRole', new Role('foo')),
array('setRoles', array('foo', 'asdf')),
);
}
}

View File

@ -20,7 +20,7 @@ class UsernamePasswordTokenTest extends \PHPUnit_Framework_TestCase
$token = new UsernamePasswordToken('foo', 'bar');
$this->assertFalse($token->isAuthenticated());
$token = new UsernamePasswordToken('foo', 'bar', array('ROLE_FOO'));
$token = new UsernamePasswordToken('foo', 'bar', null, array('ROLE_FOO'));
$this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles());
$this->assertTrue($token->isAuthenticated());
}

View File

@ -17,7 +17,7 @@ class InMemoryUserProviderTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$provider = new InMemoryUserProvider(array(
$provider = new InMemoryUserProvider('foo', array(
'fabien' => array(
'password' => 'foo',
'enabled' => false,
@ -25,7 +25,8 @@ class InMemoryUserProviderTest extends \PHPUnit_Framework_TestCase
),
));
$user = $provider->loadUserByUsername('fabien');
list($user, $providerName) = $provider->loadUserByUsername('fabien');
$this->assertSame('foo', $providerName);
$this->assertEquals('foo', $user->getPassword());
$this->assertEquals(array('ROLE_USER'), $user->getRoles());
$this->assertFalse($user->isEnabled());
@ -33,10 +34,11 @@ class InMemoryUserProviderTest extends \PHPUnit_Framework_TestCase
public function testCreateUser()
{
$provider = new InMemoryUserProvider();
$provider = new InMemoryUserProvider('foo');
$provider->createUser(new User('fabien', 'foo'));
$this->assertEquals('foo', $provider->loadUserByUsername('fabien')->getPassword());
list($user, $providerName) = $provider->loadUserByUsername('fabien');
$this->assertEquals('foo', $user->getPassword());
}
/**
@ -44,7 +46,7 @@ class InMemoryUserProviderTest extends \PHPUnit_Framework_TestCase
*/
public function testCreateUserAlreadyExist()
{
$provider = new InMemoryUserProvider();
$provider = new InMemoryUserProvider('foo');
$provider->createUser(new User('fabien', 'foo'));
$provider->createUser(new User('fabien', 'foo'));
}
@ -54,7 +56,7 @@ class InMemoryUserProviderTest extends \PHPUnit_Framework_TestCase
*/
public function testLoadUserByUsernameDoesNotExist()
{
$provider = new InMemoryUserProvider();
$provider = new InMemoryUserProvider('foo');
$provider->loadUserByUsername('fabien');
}
}