Avoid migration on stateless firewalls

This commit is contained in:
Ryan Weaver 2018-05-30 10:06:54 -04:00 committed by Fabien Potencier
parent b1f60216a4
commit cca73bb564
15 changed files with 138 additions and 38 deletions

View File

@ -77,6 +77,7 @@ class GuardAuthenticationFactory implements SecurityFactoryInterface
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.guard')); $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.guard'));
$listener->replaceArgument(2, $id); $listener->replaceArgument(2, $id);
$listener->replaceArgument(3, $authenticatorReferences); $listener->replaceArgument(3, $authenticatorReferences);
$listener->addMethodCall('setSessionAuthenticationStrategy', array(new Reference('security.authentication.session_strategy.'.$id)));
// determine the entryPointId to use // determine the entryPointId to use
$entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config); $entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config);

View File

@ -41,6 +41,7 @@ class HttpBasicFactory implements SecurityFactoryInterface
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic')); $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic'));
$listener->replaceArgument(2, $id); $listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($entryPointId)); $listener->replaceArgument(3, new Reference($entryPointId));
$listener->addMethodCall('setSessionAuthenticationStrategy', array(new Reference('security.authentication.session_strategy.'.$id)));
return array($provider, $listenerId, $entryPointId); return array($provider, $listenerId, $entryPointId);
} }

View File

@ -42,6 +42,7 @@ class HttpDigestFactory implements SecurityFactoryInterface
$listener->replaceArgument(1, new Reference($userProvider)); $listener->replaceArgument(1, new Reference($userProvider));
$listener->replaceArgument(2, $id); $listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($entryPointId)); $listener->replaceArgument(3, new Reference($entryPointId));
$listener->addMethodCall('setSessionAuthenticationStrategy', array(new Reference('security.authentication.session_strategy.'.$id)));
return array($provider, $listenerId, $entryPointId); return array($provider, $listenerId, $entryPointId);
} }

View File

@ -38,6 +38,7 @@ class RemoteUserFactory implements SecurityFactoryInterface
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.remote_user')); $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.remote_user'));
$listener->replaceArgument(2, $id); $listener->replaceArgument(2, $id);
$listener->replaceArgument(3, $config['user']); $listener->replaceArgument(3, $config['user']);
$listener->addMethodCall('setSessionAuthenticationStrategy', array(new Reference('security.authentication.session_strategy.'.$id)));
return array($providerId, $listenerId, $defaultEntryPoint); return array($providerId, $listenerId, $defaultEntryPoint);
} }

View File

@ -57,6 +57,7 @@ class SimplePreAuthenticationFactory implements SecurityFactoryInterface
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.simple_preauth')); $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.simple_preauth'));
$listener->replaceArgument(2, $id); $listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($config['authenticator'])); $listener->replaceArgument(3, new Reference($config['authenticator']));
$listener->addMethodCall('setSessionAuthenticationStrategy', array(new Reference('security.authentication.session_strategy.'.$id)));
return array($provider, $listenerId, null); return array($provider, $listenerId, null);
} }

View File

@ -39,6 +39,7 @@ class X509Factory implements SecurityFactoryInterface
$listener->replaceArgument(2, $id); $listener->replaceArgument(2, $id);
$listener->replaceArgument(3, $config['user']); $listener->replaceArgument(3, $config['user']);
$listener->replaceArgument(4, $config['credentials']); $listener->replaceArgument(4, $config['credentials']);
$listener->addMethodCall('setSessionAuthenticationStrategy', array(new Reference('security.authentication.session_strategy.'.$id)));
return array($providerId, $listenerId, $defaultEntryPoint); return array($providerId, $listenerId, $defaultEntryPoint);
} }

View File

@ -285,7 +285,11 @@ class SecurityExtension extends Extension
} }
$listeners[] = new Reference($this->createContextListener($container, $contextKey)); $listeners[] = new Reference($this->createContextListener($container, $contextKey));
$sessionStrategyId = 'security.authentication.session_strategy';
} else {
$sessionStrategyId = 'security.authentication.session_strategy_noop';
} }
$container->setAlias(new Alias('security.authentication.session_strategy.'.$id, false), $sessionStrategyId);
// Logout listener // Logout listener
$logoutListenerId = null; $logoutListenerId = null;

View File

@ -84,6 +84,10 @@
<argument>%security.authentication.session_strategy.strategy%</argument> <argument>%security.authentication.session_strategy.strategy%</argument>
</service> </service>
<service id="security.authentication.session_strategy_noop" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy" public="false">
<argument>none</argument>
</service>
<service id="security.encoder_factory.generic" class="%security.encoder_factory.generic.class%" public="false"> <service id="security.encoder_factory.generic" class="%security.encoder_factory.generic.class%" public="false">
<argument type="collection" /> <argument type="collection" />
</service> </service>

View File

@ -18,7 +18,7 @@
"require": { "require": {
"php": ">=5.3.9", "php": ">=5.3.9",
"ext-xml": "*", "ext-xml": "*",
"symfony/security": "^2.8.41|^3.4.11", "symfony/security": "^2.8.42|^3.4.12",
"symfony/security-acl": "~2.7|~3.0.0", "symfony/security-acl": "~2.7|~3.0.0",
"symfony/http-kernel": "~2.7|~3.0.0", "symfony/http-kernel": "~2.7|~3.0.0",
"symfony/polyfill-php70": "~1.0" "symfony/polyfill-php70": "~1.0"

View File

@ -20,6 +20,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
/** /**
* A utility class that does much of the *work* during the guard authentication process. * A utility class that does much of the *work* during the guard authentication process.
@ -32,8 +33,8 @@ use Symfony\Component\Security\Http\SecurityEvents;
class GuardAuthenticatorHandler class GuardAuthenticatorHandler
{ {
private $tokenStorage; private $tokenStorage;
private $dispatcher; private $dispatcher;
private $sessionStrategy;
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null) public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null)
{ {
@ -46,7 +47,7 @@ class GuardAuthenticatorHandler
*/ */
public function authenticateWithToken(TokenInterface $token, Request $request) public function authenticateWithToken(TokenInterface $token, Request $request)
{ {
$this->migrateSession($request); $this->migrateSession($request, $token);
$this->tokenStorage->setToken($token); $this->tokenStorage->setToken($token);
if (null !== $this->dispatcher) { if (null !== $this->dispatcher) {
@ -129,15 +130,22 @@ class GuardAuthenticatorHandler
)); ));
} }
private function migrateSession(Request $request) /**
* Call this method if your authentication token is stored to a session.
*
* @final since version 2.8
*/
public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy)
{ {
if (!$request->hasSession() || !$request->hasPreviousSession()) { $this->sessionStrategy = $sessionStrategy;
}
private function migrateSession(Request $request, TokenInterface $token)
{
if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
return; return;
} }
// Destroying the old session is broken in php 5.4.0 - 5.4.10 $this->sessionStrategy->onAuthentication($request, $token);
// See https://bugs.php.net/63379
$destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
$request->getSession()->migrate($destroy);
} }
} }

View File

@ -25,6 +25,7 @@ class GuardAuthenticatorHandlerTest extends TestCase
private $dispatcher; private $dispatcher;
private $token; private $token;
private $request; private $request;
private $sessionStrategy;
private $guardAuthenticator; private $guardAuthenticator;
public function testAuthenticateWithToken() public function testAuthenticateWithToken()
@ -117,12 +118,38 @@ class GuardAuthenticatorHandlerTest extends TestCase
return $tests; return $tests;
} }
public function testNoFailureIfSessionStrategyNotPassed()
{
$this->configurePreviousSession();
$this->tokenStorage->expects($this->once())
->method('setToken')
->with($this->token);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$handler->authenticateWithToken($this->token, $this->request);
}
public function testSessionStrategyIsCalled()
{
$this->configurePreviousSession();
$this->sessionStrategy->expects($this->once())
->method('onAuthentication')
->with($this->request, $this->token);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$handler->setSessionAuthenticationStrategy($this->sessionStrategy);
$handler->authenticateWithToken($this->token, $this->request);
}
protected function setUp() protected function setUp()
{ {
$this->tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); $this->tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock();
$this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
$this->token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); $this->token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
$this->request = new Request(array(), array(), array(), array(), array(), array()); $this->request = new Request(array(), array(), array(), array(), array(), array());
$this->sessionStrategy = $this->getMockBuilder('Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface')->getMock();
$this->guardAuthenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); $this->guardAuthenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
} }
@ -134,4 +161,14 @@ class GuardAuthenticatorHandlerTest extends TestCase
$this->request = null; $this->request = null;
$this->guardAuthenticator = null; $this->guardAuthenticator = null;
} }
private function configurePreviousSession()
{
$session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock();
$session->expects($this->any())
->method('getName')
->willReturn('test_session_name');
$this->request->setSession($session);
$this->request->cookies->set('test_session_name', 'session_cookie_val');
}
} }

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\SecurityEvents;
@ -22,6 +23,7 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
/** /**
* AbstractPreAuthenticatedListener is the base class for all listener that * AbstractPreAuthenticatedListener is the base class for all listener that
@ -37,6 +39,7 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
private $authenticationManager; private $authenticationManager;
private $providerKey; private $providerKey;
private $dispatcher; private $dispatcher;
private $sessionStrategy;
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{ {
@ -83,7 +86,7 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
$this->logger->info('Pre-authentication successful.', array('token' => (string) $token)); $this->logger->info('Pre-authentication successful.', array('token' => (string) $token));
} }
$this->migrateSession($request); $this->migrateSession($request, $token);
$this->tokenStorage->setToken($token); $this->tokenStorage->setToken($token);
@ -96,6 +99,16 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
} }
} }
/**
* Call this method if your authentication token is stored to a session.
*
* @final since version 2.8
*/
public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy)
{
$this->sessionStrategy = $sessionStrategy;
}
/** /**
* Clears a PreAuthenticatedToken for this provider (if present). * Clears a PreAuthenticatedToken for this provider (if present).
*/ */
@ -118,15 +131,12 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
*/ */
abstract protected function getPreAuthenticatedData(Request $request); abstract protected function getPreAuthenticatedData(Request $request);
private function migrateSession(Request $request) private function migrateSession(Request $request, TokenInterface $token)
{ {
if (!$request->hasSession() || !$request->hasPreviousSession()) { if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
return; return;
} }
// Destroying the old session is broken in php 5.4.0 - 5.4.10 $this->sessionStrategy->onAuthentication($request, $token);
// See https://bugs.php.net/63379
$destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
$request->getSession()->migrate($destroy);
} }
} }

View File

@ -14,11 +14,13 @@ namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
/** /**
* BasicAuthenticationListener implements Basic HTTP authentication. * BasicAuthenticationListener implements Basic HTTP authentication.
@ -33,6 +35,7 @@ class BasicAuthenticationListener implements ListenerInterface
private $authenticationEntryPoint; private $authenticationEntryPoint;
private $logger; private $logger;
private $ignoreFailure; private $ignoreFailure;
private $sessionStrategy;
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null) public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
{ {
@ -72,7 +75,7 @@ class BasicAuthenticationListener implements ListenerInterface
try { try {
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey)); $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
$this->migrateSession($request); $this->migrateSession($request, $token);
$this->tokenStorage->setToken($token); $this->tokenStorage->setToken($token);
} catch (AuthenticationException $e) { } catch (AuthenticationException $e) {
@ -93,15 +96,22 @@ class BasicAuthenticationListener implements ListenerInterface
} }
} }
private function migrateSession(Request $request) /**
* Call this method if your authentication token is stored to a session.
*
* @final since version 2.8
*/
public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy)
{ {
if (!$request->hasSession() || !$request->hasPreviousSession()) { $this->sessionStrategy = $sessionStrategy;
}
private function migrateSession(Request $request, TokenInterface $token)
{
if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
return; return;
} }
// Destroying the old session is broken in php 5.4.0 - 5.4.10 $this->sessionStrategy->onAuthentication($request, $token);
// See https://bugs.php.net/63379
$destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
$request->getSession()->migrate($destroy);
} }
} }

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall; namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -23,6 +24,7 @@ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException; use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
/** /**
* DigestAuthenticationListener implements Digest HTTP authentication. * DigestAuthenticationListener implements Digest HTTP authentication.
@ -36,6 +38,7 @@ class DigestAuthenticationListener implements ListenerInterface
private $providerKey; private $providerKey;
private $authenticationEntryPoint; private $authenticationEntryPoint;
private $logger; private $logger;
private $sessionStrategy;
public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null) public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null)
{ {
@ -117,9 +120,20 @@ class DigestAuthenticationListener implements ListenerInterface
$this->logger->info('Digest authentication successful.', array('username' => $digestAuth->getUsername(), 'received' => $digestAuth->getResponse())); $this->logger->info('Digest authentication successful.', array('username' => $digestAuth->getUsername(), 'received' => $digestAuth->getResponse()));
} }
$this->migrateSession($request); $token = new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey);
$this->migrateSession($request, $token);
$this->tokenStorage->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey)); $this->tokenStorage->setToken($token);
}
/**
* Call this method if your authentication token is stored to a session.
*
* @final since version 2.8
*/
public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy)
{
$this->sessionStrategy = $sessionStrategy;
} }
private function fail(GetResponseEvent $event, Request $request, AuthenticationException $authException) private function fail(GetResponseEvent $event, Request $request, AuthenticationException $authException)
@ -136,16 +150,13 @@ class DigestAuthenticationListener implements ListenerInterface
$event->setResponse($this->authenticationEntryPoint->start($request, $authException)); $event->setResponse($this->authenticationEntryPoint->start($request, $authException));
} }
private function migrateSession(Request $request) private function migrateSession(Request $request, TokenInterface $token)
{ {
if (!$request->hasSession() || !$request->hasPreviousSession()) { if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
return; return;
} }
// Destroying the old session is broken in php 5.4.0 - 5.4.10 $this->sessionStrategy->onAuthentication($request, $token);
// See https://bugs.php.net/63379
$destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
$request->getSession()->migrate($destroy);
} }
} }

View File

@ -19,12 +19,14 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface; use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
/** /**
* SimplePreAuthenticationListener implements simple proxying to an authenticator. * SimplePreAuthenticationListener implements simple proxying to an authenticator.
@ -39,6 +41,7 @@ class SimplePreAuthenticationListener implements ListenerInterface
private $simpleAuthenticator; private $simpleAuthenticator;
private $logger; private $logger;
private $dispatcher; private $dispatcher;
private $sessionStrategy;
/** /**
* @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance * @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance
@ -62,6 +65,16 @@ class SimplePreAuthenticationListener implements ListenerInterface
$this->dispatcher = $dispatcher; $this->dispatcher = $dispatcher;
} }
/**
* Call this method if your authentication token is stored to a session.
*
* @final since version 2.8
*/
public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy)
{
$this->sessionStrategy = $sessionStrategy;
}
/** /**
* Handles basic authentication. * Handles basic authentication.
*/ */
@ -87,7 +100,7 @@ class SimplePreAuthenticationListener implements ListenerInterface
$token = $this->authenticationManager->authenticate($token); $token = $this->authenticationManager->authenticate($token);
$this->migrateSession($request); $this->migrateSession($request, $token);
$this->tokenStorage->setToken($token); $this->tokenStorage->setToken($token);
@ -124,15 +137,12 @@ class SimplePreAuthenticationListener implements ListenerInterface
} }
} }
private function migrateSession(Request $request) private function migrateSession(Request $request, TokenInterface $token)
{ {
if (!$request->hasSession() || !$request->hasPreviousSession()) { if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
return; return;
} }
// Destroying the old session is broken in php 5.4.0 - 5.4.10 $this->sessionStrategy->onAuthentication($request, $token);
// See https://bugs.php.net/63379
$destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
$request->getSession()->migrate($destroy);
} }
} }