Fix json_login default success/failure handling

This commit is contained in:
Robin Chalas 2017-04-20 23:05:53 +02:00
parent f9bb4dccb8
commit 9749618ff5
8 changed files with 144 additions and 11 deletions

View File

@ -28,6 +28,9 @@ class JsonLoginFactory extends AbstractFactory
{
$this->addOption('username_path', 'username');
$this->addOption('password_path', 'password');
$this->defaultFailureHandlerOptions = array();
$this->defaultSuccessHandlerOptions = array();
$this->options['require_previous_session'] = false;
}
/**
@ -86,8 +89,8 @@ class JsonLoginFactory extends AbstractFactory
$listenerId = $this->getListenerId();
$listener = new ChildDefinition($listenerId);
$listener->replaceArgument(3, $id);
$listener->replaceArgument(4, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)));
$listener->replaceArgument(5, new Reference($this->createAuthenticationFailureHandler($container, $id, $config)));
$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));
$listenerId .= '.'.$id;

View File

@ -149,8 +149,8 @@
<argument type="service" id="security.authentication.manager" />
<argument type="service" id="security.http_utils" />
<argument /> <!-- Provider-shared Key -->
<argument type="service" id="security.authentication.success_handler" />
<argument type="service" id="security.authentication.failure_handler" />
<argument /> <!-- Failure handler -->
<argument /> <!-- Success Handler -->
<argument type="collection" /> <!-- Options -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null" />

View File

@ -11,13 +11,16 @@
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class TestController
{
public function loginCheckAction()
public function loginCheckAction(UserInterface $user)
{
throw new \RuntimeException(sprintf('%s should never be called.', __FUNCTION__));
return new JsonResponse(array('message' => sprintf('Welcome @%s!', $user->getUsername())));
}
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
class JsonAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface
{
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new JsonResponse(array('message' => 'Something went wrong'), 500);
}
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
{
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
return new JsonResponse(array('message' => sprintf('Good game @%s!', $token->getUsername())));
}
}

View File

@ -11,22 +11,54 @@
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class JsonLoginTest extends WebTestCase
{
public function testJsonLoginSuccess()
public function testDefaultJsonLoginSuccess()
{
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'config.yml'));
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "foo"}}');
$this->assertEquals('http://localhost/', $client->getResponse()->headers->get('location'));
$response = $client->getResponse();
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame(array('message' => 'Welcome @dunglas!'), json_decode($response->getContent(), true));
}
public function testJsonLoginFailure()
public function testDefaultJsonLoginFailure()
{
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'config.yml'));
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "bad"}}');
$this->assertEquals('http://localhost/login', $client->getResponse()->headers->get('location'));
$response = $client->getResponse();
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertSame(401, $response->getStatusCode());
$this->assertSame(array('error' => 'Invalid credentials.'), json_decode($response->getContent(), true));
}
public function testCustomJsonLoginSuccess()
{
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml'));
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "foo"}}');
$response = $client->getResponse();
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame(array('message' => 'Good game @dunglas!'), json_decode($response->getContent(), true));
}
public function testCustomJsonLoginFailure()
{
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml'));
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "bad"}}');
$response = $client->getResponse();
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertSame(500, $response->getStatusCode());
$this->assertSame(array('message' => 'Something went wrong'), json_decode($response->getContent(), true));
}
}

View File

@ -0,0 +1,32 @@
imports:
- { resource: ./../config/framework.yml }
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
providers:
in_memory:
memory:
users:
dunglas: { password: foo, roles: [ROLE_USER] }
firewalls:
main:
pattern: ^/
anonymous: true
json_login:
check_path: /chk
username_path: user.login
password_path: user.password
success_handler: json_login.success_handler
failure_handler: json_login.failure_handler
access_control:
- { path: ^/foo, roles: ROLE_USER }
services:
json_login.success_handler:
class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http\JsonAuthenticationSuccessHandler
json_login.failure_handler:
class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http\JsonAuthenticationFailureHandler

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@ -53,7 +54,7 @@ class UsernamePasswordJsonAuthenticationListener implements ListenerInterface
private $eventDispatcher;
private $propertyAccessor;
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null, PropertyAccessorInterface $propertyAccessor = null)
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)
{
$this->tokenStorage = $tokenStorage;
$this->authenticationManager = $authenticationManager;
@ -117,6 +118,10 @@ class UsernamePasswordJsonAuthenticationListener implements ListenerInterface
$response = $this->onFailure($request, $e);
}
if (null === $response) {
return;
}
$event->setResponse($response);
}
@ -133,6 +138,10 @@ class UsernamePasswordJsonAuthenticationListener implements ListenerInterface
$this->eventDispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
}
if (!$this->successHandler) {
return; // let the original request succeeds
}
$response = $this->successHandler->onAuthenticationSuccess($request, $token);
if (!$response instanceof Response) {
@ -153,6 +162,10 @@ class UsernamePasswordJsonAuthenticationListener implements ListenerInterface
$this->tokenStorage->setToken(null);
}
if (!$this->failureHandler) {
return new JsonResponse(array('error' => $failed->getMessageKey()), 401);
}
$response = $this->failureHandler->onAuthenticationFailure($request, $failed);
if (!$response instanceof Response) {