Skip to content

Commit

Permalink
fixed user refreshing after unserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
schmittjoh authored and fabpot committed Dec 15, 2010
1 parent c9f08c0 commit 3c692bd
Show file tree
Hide file tree
Showing 21 changed files with 508 additions and 89 deletions.
22 changes: 20 additions & 2 deletions src/Symfony/Bundle/DoctrineBundle/Security/EntityUserProvider.php
Expand Up @@ -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;
}

/**
Expand All @@ -35,6 +45,14 @@ public function loadUserByUsername($username)
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;
}
}
Expand Up @@ -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;
}

/**
Expand All @@ -35,6 +45,14 @@ public function loadUserByUsername($username)
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;
}
}
Expand Up @@ -126,12 +126,22 @@ protected function createAuthorization($config, ContainerBuilder $container)

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'));
Expand Down Expand Up @@ -204,8 +214,8 @@ protected function createFirewall(ContainerBuilder $container, $firewall, $provi
// 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();
Expand All @@ -217,11 +227,11 @@ protected function createFirewall(ContainerBuilder $container, $firewall, $provi
$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'));
Expand Down Expand Up @@ -308,7 +318,7 @@ protected function createAuthenticationListeners($container, $id, $firewall, $de
}

// 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) {
Expand All @@ -318,6 +328,11 @@ protected function createAuthenticationProviders($config, ContainerBuilder $cont
$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;
}

Expand All @@ -340,16 +355,14 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con
}

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
Expand All @@ -364,6 +377,7 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con
->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,
));
Expand All @@ -377,6 +391,7 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con
->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,
));
Expand All @@ -385,7 +400,10 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con
}

// 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'];
Expand Down Expand Up @@ -443,8 +461,6 @@ protected function createAccessListener($container, $id, $providers)
return $listenerId;
}



protected function createExceptionListener($container, $id, $defaultEntryPoint)
{
$exceptionListenerId = 'security.exception_listener.'.$id;
Expand Down
Expand Up @@ -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>
Expand Down
Expand Up @@ -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>

Expand Down
4 changes: 3 additions & 1 deletion src/Symfony/Component/HttpKernel/Security/Firewall.php
Expand Up @@ -88,7 +88,9 @@ public function handle(Event $event)

// 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)));
Expand Down
Expand Up @@ -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;
Expand All @@ -23,15 +25,18 @@
* 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;
}

Expand All @@ -47,7 +52,7 @@ public function register(EventDispatcher $dispatcher)
$dispatcher->connect('core.security', array($this, 'read'), 0);
$dispatcher->connect('core.response', array($this, 'write'), 0);
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -76,11 +81,11 @@ public function read(Event $event)

$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);
}
}

Expand Down Expand Up @@ -111,4 +116,61 @@ public function write(Event $event, Response $response)

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));
}
}

0 comments on commit 3c692bd

Please sign in to comment.