Skip to content

Commit

Permalink
Avoiding session migration for stateless firewall UsernamePasswordJso…
Browse files Browse the repository at this point in the history
…nAuthenticationListener
  • Loading branch information
weaverryan authored and fabpot committed Jun 10, 2018
1 parent 9586c43 commit c06f322
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 5 deletions.
Expand Up @@ -89,6 +89,7 @@ protected function createListener($container, $id, $config, $userProvider)
$listener->replaceArgument(4, isset($config['success_handler']) ? new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)) : null);
$listener->replaceArgument(5, isset($config['failure_handler']) ? new Reference($this->createAuthenticationFailureHandler($container, $id, $config)) : null);
$listener->replaceArgument(6, array_intersect_key($config, $this->options));
$listener->addMethodCall('setSessionAuthenticationStrategy', array(new Reference('security.authentication.session_strategy.'.$id)));

$listenerId .= '.'.$id;
$container->setDefinition($listenerId, $listener);
Expand Down
Expand Up @@ -377,7 +377,11 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a

$this->logoutOnUserChangeByContextKey[$contextKey] = array($id, $logoutOnUserChange);
$listeners[] = new Reference($this->createContextListener($container, $contextKey, $logoutOnUserChange));
$sessionStrategyId = 'security.authentication.session_strategy';
} else {
$sessionStrategyId = 'security.authentication.session_strategy_noop';
}
$container->setAlias(new Alias('security.authentication.session_strategy.'.$id, false), $sessionStrategyId);

$config->replaceArgument(6, $contextKey);

Expand Down
Expand Up @@ -62,6 +62,9 @@
<service id="security.authentication.session_strategy" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy">
<argument>%security.authentication.session_strategy.strategy%</argument>
</service>
<service id="security.authentication.session_strategy_noop" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy" public="false">
<argument>none</argument>
</service>
<service id="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface" alias="security.authentication.session_strategy" />

<service id="security.encoder_factory.generic" class="Symfony\Component\Security\Core\Encoder\EncoderFactory">
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/SecurityBundle/composer.json
Expand Up @@ -18,7 +18,7 @@
"require": {
"php": "^5.5.9|>=7.0.8",
"ext-xml": "*",
"symfony/security": "~3.4.11|^4.0.11",
"symfony/security": "~3.4.12|^4.0.12|^4.1.1",
"symfony/dependency-injection": "^3.4.3|^4.0.3",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/polyfill-php70": "~1.0"
Expand Down
Expand Up @@ -33,6 +33,7 @@
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;

/**
* UsernamePasswordJsonAuthenticationListener is a stateless implementation of
Expand All @@ -52,6 +53,7 @@ class UsernamePasswordJsonAuthenticationListener implements ListenerInterface
private $logger;
private $eventDispatcher;
private $propertyAccessor;
private $sessionStrategy;

public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null, PropertyAccessorInterface $propertyAccessor = null)
{
Expand Down Expand Up @@ -139,7 +141,7 @@ private function onSuccess(Request $request, TokenInterface $token)
$this->logger->info('User has been authenticated successfully.', array('username' => $token->getUsername()));
}

$this->migrateSession($request);
$this->migrateSession($request, $token);

$this->tokenStorage->setToken($token);

Expand Down Expand Up @@ -185,11 +187,21 @@ private function onFailure(Request $request, AuthenticationException $failed)
return $response;
}

private function migrateSession(Request $request)
/**
* Call this method if your authentication token is stored to a session.
*
* @final since version 3.4
*/
public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy)
{
$this->sessionStrategy = $sessionStrategy;
}

private function migrateSession(Request $request, TokenInterface $token)
{
if (!$request->hasSession() || !$request->hasPreviousSession()) {
if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
return;
}
$request->getSession()->migrate(true);
$this->sessionStrategy->onAuthentication($request, $token);
}
}
Expand Up @@ -212,4 +212,42 @@ public function testAttemptAuthenticationIfRequestPathMatchesCheckPath()
$this->listener->handle($event);
$this->assertSame('ok', $event->getResponse()->getContent());
}

public function testNoErrorOnMissingSessionStrategy()
{
$this->createListener();
$request = new Request(array(), array(), array(), array(), array(), array('HTTP_CONTENT_TYPE' => 'application/json'), '{"username": "dunglas", "password": "foo"}');
$this->configurePreviousSession($request);
$event = new GetResponseEvent($this->getMockBuilder(KernelInterface::class)->getMock(), $request, KernelInterface::MASTER_REQUEST);

$this->listener->handle($event);
$this->assertEquals('ok', $event->getResponse()->getContent());
}

public function testMigratesViaSessionStrategy()
{
$this->createListener();
$request = new Request(array(), array(), array(), array(), array(), array('HTTP_CONTENT_TYPE' => 'application/json'), '{"username": "dunglas", "password": "foo"}');
$this->configurePreviousSession($request);
$event = new GetResponseEvent($this->getMockBuilder(KernelInterface::class)->getMock(), $request, KernelInterface::MASTER_REQUEST);

$sessionStrategy = $this->getMockBuilder('Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface')->getMock();
$sessionStrategy->expects($this->once())
->method('onAuthentication')
->with($request, $this->isInstanceOf(TokenInterface::class));
$this->listener->setSessionAuthenticationStrategy($sessionStrategy);

$this->listener->handle($event);
$this->assertEquals('ok', $event->getResponse()->getContent());
}

private function configurePreviousSession(Request $request)
{
$session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock();
$session->expects($this->any())
->method('getName')
->willReturn('test_session_name');
$request->setSession($session);
$request->cookies->set('test_session_name', 'session_cookie_val');
}
}

0 comments on commit c06f322

Please sign in to comment.