Fixing a bug where having an authentication failure would log you out.

This solution is a copy of what AbstractAuthenticationListener does. Scenario:

1) Login
2) Go back to the log in page
3) Put in a bad user/pass

You *should* still be logged in after a failed attempt. This commit gives that behavior.
This commit is contained in:
Ryan Weaver 2015-09-20 20:11:34 -04:00
parent 396a1622dc
commit 302235e6e5
4 changed files with 56 additions and 9 deletions

View File

@ -117,7 +117,7 @@ class GuardAuthenticationListener implements ListenerInterface
$this->logger->info('Guard authentication failed.', array('exception' => $e, 'authenticator' => get_class($guardAuthenticator)));
}
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator);
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey);
if ($response instanceof Response) {
$event->setResponse($response);

View File

@ -18,6 +18,7 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
@ -112,12 +113,16 @@ class GuardAuthenticatorHandler
* @param AuthenticationException $authenticationException
* @param Request $request
* @param GuardAuthenticatorInterface $guardAuthenticator
* @param string $providerKey The key of the firewall
*
* @return null|Response
*/
public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator)
public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
{
$this->tokenStorage->setToken(null);
$token = $this->tokenStorage->getToken();
if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) {
$this->tokenStorage->setToken(null);
}
$response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException);
if ($response instanceof Response || null === $response) {

View File

@ -141,7 +141,7 @@ class GuardAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
$this->guardAuthenticatorHandler
->expects($this->once())
->method('handleAuthenticationFailure')
->with($authException, $this->request, $authenticator);
->with($authException, $this->request, $authenticator, $providerKey);
$listener = new GuardAuthenticationListener(
$this->guardAuthenticatorHandler,

View File

@ -18,9 +18,6 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
/**
* @author Ryan Weaver <weaverryan@gmail.com>
*/
class GuardAuthenticatorHandlerTest extends \PHPUnit_Framework_TestCase
{
private $tokenStorage;
@ -63,7 +60,8 @@ class GuardAuthenticatorHandlerTest extends \PHPUnit_Framework_TestCase
public function testHandleAuthenticationFailure()
{
$this->tokenStorage->expects($this->once())
// setToken() not called - getToken() will return null, so there's nothing to clear
$this->tokenStorage->expects($this->never())
->method('setToken')
->with(null);
$authException = new AuthenticationException('Bad password!');
@ -75,10 +73,54 @@ class GuardAuthenticatorHandlerTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue($response));
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator);
$actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, 'firewall_provider_key');
$this->assertSame($response, $actualResponse);
}
/**
* @dataProvider getTokenClearingTests
*/
public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey, $shouldTokenBeCleared)
{
$token = $this->getMockBuilder($tokenClass)
->disableOriginalConstructor()
->getMock();
$token->expects($this->any())
->method('getProviderKey')
->will($this->returnValue($tokenProviderKey));
// make the $token be the current token
$this->tokenStorage->expects($this->once())
->method('getToken')
->will($this->returnValue($token));
$this->tokenStorage->expects($shouldTokenBeCleared ? $this->once() : $this->never())
->method('setToken')
->with(null);
$authException = new AuthenticationException('Bad password!');
$response = new Response('Try again, but with the right password!');
$this->guardAuthenticator->expects($this->once())
->method('onAuthenticationFailure')
->with($this->request, $authException)
->will($this->returnValue($response));
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, $actualProviderKey);
$this->assertSame($response, $actualResponse);
}
public function getTokenClearingTests()
{
$tests = array();
// correct token class and matching firewall => clear the token
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key', true);
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key', false);
$tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key', false);
return $tests;
}
protected function setUp()
{
$this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');