8000 Fix json_login default success/failure handling · symfony/symfony@9749618 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9749618

Browse files
committed
Fix json_login default success/failure handling
1 parent f9bb4dc commit 9749618

File tree

8 files changed

+144
-11
lines changed

8 files changed

+144
-11
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public function __construct()
2828
{
2929
$this->addOption('username_path', 'username');
3030
$this->addOption('password_path', 'password');
31+
$this->defaultFailureHandlerOptions = array();
32+
$this->defaultSuccessHandlerOptions = array();
33+
$this->options['require_previous_session'] = false;
3134
}
3235

3336
/**
@@ -86,8 +89,8 @@ protected function createListener($container, $id, $config, $userProvider)
8689
$listenerId = $this->getListenerId();
8790
$listener = new ChildDefinition($listenerId);
8891
$listener->replaceArgument(3, $id);
89-
$listener->replaceArgument(4, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)));
90-
$listener->replaceArgument(5, new Reference($this->createAuthenticationFailureHandler($container, $id, $config)));
92+
$listener->replaceArgument(4, isset($config['success_handler']) ? new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)) : null);
93+
$listener->replaceArgument(5, isset($config['failure_handler']) ? new Reference($this->createAuthenticationFailureHandler($container, $id, $config)) : null);
9194
$listener->replaceArgument(6, array_intersect_key($config, $this->options));
9295

9396
$listenerId .= '.'.$id;

src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@
149149
<argument type="service" id="security.authentication.manager" />
150150
<argument type="service" id="security.http_utils" />
151151
<argument /> <!-- Provider-shared Key -->
152-
<argument type="service" id="security.authentication.success_handler" />
153-
<argument type="service" id="security.authentication.failure_handler" />
152+
<argument /> <!-- Failure handler -->
153+
<argument /> <!-- Success Handler -->
154154
<argument type="collection" /> <!-- Options -->
155155
<argument type="service" id="logger" on-invalid="null" />
156156
<argument type="service" id="event_dispatcher" on-invalid="null" />

src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Controller;
1313

14+
use Symfony\Component\HttpFoundation\JsonResponse;
15+
use Symfony\Component\Security\Core\User\UserInterface;
16+
1417
/**
1518
* @author Kévin Dunglas <dunglas@gmail.com>
1619
*/
1720
class TestController
1821
{
19-
public function loginCheckAction()
22+
public function loginCheckAction(UserInterface $user)
2023
{
21-
throw new \RuntimeException(sprintf('%s should never be called.', __FUNCTION__));
24+
return new JsonResponse(array('message' => sprintf('Welcome @%s!', $user->getUsername())));
2225
}
2326
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http;
13+
14+
use Symfony\Component\HttpFoundation\JsonResponse;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
17+
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
18+
19+
class JsonAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface
20+
{
21+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
22+
{
23+
return new JsonResponse(array('message' => 'Something went wrong'), 500);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http;
13+
14+
use Symfony\Component\HttpFoundation\JsonResponse;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17+
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
18+
19+
class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
20+
{
21+
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
22+
{
23+
return new JsonResponse(array('message' => sprintf('Good game @%s!', $token->getUsername())));
24+
}
25+
}

src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,54 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
1313

14+
use Symfony\Component\HttpFoundation\JsonResponse;
15+
1416
/**
1517
* @author Kévin Dunglas <dunglas@gmail.com>
1618
*/
1719
class JsonLoginTest extends WebTestCase
1820
{
19-
public function testJsonLoginSuccess()
21+
public function testDefaultJsonLoginSuccess()
2022
{
2123
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'config.yml'));
2224
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "foo"}}');
23-
$this->assertEquals('http://localhost/', $client->getResponse()->headers->get('location'));
25+
$response = $client->getResponse();
26+
27+
$this->assertInstanceOf(JsonResponse::class, $response);
28+
$this->assertSame(200, $response->getStatusCode());
29+
$this->assertSame(array('message' => 'Welcome @dunglas!'), json_decode($response->getContent(), true));
2430
}
2531

26-
public function testJsonLoginFailure()
32+
public function testDefaultJsonLoginFailure()
2733
{
2834
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'config.yml'));
2935
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "bad"}}');
30-
$this->assertEquals('http://localhost/login', $client->getResponse()->headers->get('location'));
36+
$response = $client->getResponse();
37+
38+
$this->assertInstanceOf(JsonResponse::class, $response);
39+
$this->assertSame(401, $response->getStatusCode());
40+
$this->assertSame(array('error' => 'Invalid credentials.'), json_decode($response->getContent(), true));
41+
}
42+
43+
public function testCustomJsonLoginSuccess()
44+
{
45+
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml'));
46+
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "foo"}}');
47+
$response = $client->getResponse();
48+
49+
$this->assertInstanceOf(JsonResponse::class, $response);
50+
$this->assertSame(200, $response->getStatusCode());
51+
$this->assertSame(array('message' => 'Good game @dunglas!'), json_decode($response->getContent(), true));
52+
}
53+
54+
public function testCustomJsonLoginFailure()
55+
{
56+
$client = $this->createClient(array('test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml'));
57+
$client->request('POST', '/chk', array(), array(), array(), '{"user": {"login": "dunglas", "password": "bad"}}');
58+
$response = $client->getResponse();
59+
60+
$this->assertInstanceOf(JsonResponse::class, $response);
61+
$this->assertSame(500, $response->getStatusCode());
62+
$this->assertSame(array('message' => 'Something went wrong'), json_decode($response->getContent(), true));
3163
}
3264
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
imports:
2+
- { resource: ./../config/framework.yml }
3+
4+
security:
5+
encoders:
6+
Symfony\Component\Security\Core\User\User: plaintext
7+
8+
providers:
9+
in_memory:
10+
memory:
11+
users:
12+
dunglas: { password: foo, roles: [ROLE_USER] }
13+
14+
firewalls:
15+
main:
16+
pattern: ^/
17+
anonymous: true
18+
json_login:
19+
check_path: /chk
20+
username_path: user.login
21+
password_path: user.password
22+
success_handler: json_login.success_handler
23+
failure_handler: json_login.failure_handler
24+
25+
access_control:
26+
- { path: ^/foo, roles: ROLE_USER }
27+
28+
services:
29+
json_login.success_handler:
30+
class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http\JsonAuthenticationSuccessHandler
31+
json_login.failure_handler:
32+
class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\Security\Http\JsonAuthenticationFailureHandler

src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Psr\Log\LoggerInterface;
1515
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16+
use Symfony\Component\HttpFoundation\JsonResponse;
1617
use Symfony\Component\HttpFoundation\Request;
1718
use Symfony\Component\HttpFoundation\Response;
1819
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -53,7 +54,7 @@ class UsernamePasswordJsonAuthenticationListener implements ListenerInterface
5354
private $eventDispatcher;
5455
private $propertyAccessor;
5556

56-
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)
57+
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)
5758
{
5859
$this->tokenStorage = $tokenStorage;
5960
$this->authenticationManager = $authenticationManager;
@@ -117,6 +118,10 @@ public function handle(GetResponseEvent $event)
117118
$response = $this->onFailure($request, $e);
118119
}
119120

121+
if (null === $response) {
122+
return;
123+
}
124+
120125
$event->setResponse($response);
121126
}
122127

@@ -133,6 +138,10 @@ private function onSuccess(Request $request, TokenInterface $token)
133138
$this->eventDispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
134139
}
135140

141+
if (!$this->successHandler) {
142+
return; // let the original request succeeds
143+
}
144+
136145
$response = $this->successHandler->onAuthenticationSuccess($request, $token);
137146

138147
if (!$response instanceof Response) {
@@ -153,6 +162,10 @@ private function onFailure(Request $request, AuthenticationException $failed)
153162
$this->tokenStorage->setToken(null);
154163
}
155164

165+
if (!$this->failureHandler) {
166+
return new JsonResponse(array('error' => $failed->getMessageKey()), 401);
167+
}
168+
156169
$response = $this->failureHandler->onAuthenticationFailure($request, $failed);
157170

158171
if (!$response instanceof Response) {

0 commit comments

Comments
 (0)
0