8000 [Security] Move default authentication success handling strategy to s… · symfony/symfony@c6aa392 · GitHub
[go: up one dir, main page]

Skip to content

Commit c6aa392

Browse files
committed
[Security] Move default authentication success handling strategy to seperate class
[Security] Update configuration for changes regarding default success handler [Security] Fix + add AbstractFactory test
1 parent b34bdd4 commit c6aa392

File tree

7 files changed

+162
-80
lines changed

7 files changed

+162
-80
lines changed

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,15 @@ abstract class AbstractFactory implements SecurityFactoryInterface
3030
'check_path' => '/login_check',
3131
'login_path' => '/login',
3232
'use_forward' => false,
33+
'failure_path' => null,
34+
'failure_forward' => false,
35+
);
36+
37+
protected $defaultSuccessHandlerOptions = array(
3338
'always_use_default_target_path' => false,
3439
'default_target_path' => '/',
3540
'target_path_parameter' => '_target_path',
3641
'use_referer' => false,
37-
'failure_path' => null,
38-
'failure_forward' => false,
3942
);
4043

4144
public function create(ContainerBuilder $container, $id, $config, $userProviderId, $defaultEntryPointId)
@@ -71,7 +74,7 @@ public function addConfiguration(NodeDefinition $node)
7174
->scalarNode('failure_handler')->end()
7275
;
7376

74-
foreach ($this->options as $name => $default) {
77+
foreach (array_merge($this->options, $this->defaultSuccessHandlerOptions) as $name => $default) {
7578
if (is_bool($default)) {
7679
$builder->booleanNode($name)->defaultValue($default);
7780
} else {
@@ -149,12 +152,8 @@ protected function createListener($container, $id, $config, $userProvider)
149152
$listenerId = $this->getListenerId();
150153
$listener = new DefinitionDecorator($listenerId);
151154
$listener->replaceArgument(4, $id);
152-
$listener->replaceArgument(5, array_intersect_key($config, $this->options));
153-
154-
// success handler
155-
if (isset($config['success_handler'])) {
156-
$listener->replaceArgument(6, new Reference($config['success_handler']));
157-
}
155+
$listener->replaceArgument(5, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)));
156+
$listener->replaceArgument(6, array_intersect_key($config, $this->options));
158157

159158
// failure handler
160159
if (isset($config['failure_handler'])) {
@@ -166,4 +165,20 @@ protected function createListener($container, $id, $config, $userProvider)
166165

167166
return $listenerId;
168167
}
168+
169+
protected function createAuthenticationSuccessHandler($container, $id, $config)
170+
{
171+
// success handler
172+
if (isset($config['success_handler'])) {
173+
174+
return $config['success_handler'];
175+
}
176+
177+
$id = 'security.authentication.success_handler.'.$id;
178+
179+
$successHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.success_handler'));
180+
$successHandler->replaceArgument(1, array_intersect_key($config, $this->defaultSuccessHandlerOptions));
181+
182+
return $id;
183+
}
169184
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
<parameter key="security.authentication.provider.pre_authenticated.class">Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider</parameter>
3838

3939
<parameter key="security.authentication.provider.anonymous.class">Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider</parameter>
40+
41+
<parameter key="security.authentication.success_handler.class">Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler</parameter>
4042
</parameters>
4143

4244
<services>
@@ -98,13 +100,18 @@
98100
<argument type="service" id="security.authentication.session_strategy" />
99101
<argument type="service" id="security.http_utils" />
100102
<argument />
103+
<argument type="service" id="security.authentication.success_handler" />
101104
<argument type="collection"></argument>
102-
<argument type="service" id="security.authentication.success_handler" on-invalid="null" />
103105
<argument type="service" id="security.authentication.failure_handler" on-invalid="null" />
104106
<argument type="service" id="logger" on-invalid="null" />
105107
<argument type="service" id="event_dispatcher" on-invalid="null" />
106108
</service>
107109

110+
<service id="security.authentication.success_handler" class="%security.authentication.success_handler.class%" abstract="true" public="false">
111+
<argument type="service" id="security.http_utils" />
112+
<argument />
113+
</service>
114+
108115
<service id="security.authentication.listener.form"
109116
class="%security.authentication.listener.form.class%"
110117
parent="security.authentication.listener.abstract"

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,10 @@ class AbstractFactoryTest extends \PHPUnit_Framework_TestCase
1818
{
1919
public function testCreate()
2020
{
21-
$factory = $this->getFactory();
22-
23-
$factory
24-
->expects($this->once())
25-
->method('createAuthProvider')
26-
->will($this->returnValue('auth_provider'))
27-
;
28-
$factory
29-
->expects($this->atLeastOnce())
30-
->method('getListenerId')
31-
->will($this->returnValue('abstract_listener'))
32-
;
33-
34-
$container = new ContainerBuilder();
35-
$container->register('auth_provider');
36-
37-
list($authProviderId,
21+
list($container,
22+
$authProviderId,
3823
$listenerId,
39-
$entryPointId
40-
) = $factory->create($container, 'foo', array('use_forward' => true, 'failure_path' => '/foo', 'success_handler' => 'foo', 'remember_me' => true), 'user_provider', 'entry_point');
24+
$entryPointId) = $this->callFactory('foo', array('use_forward' => true, 'failure_path' => '/foo', 'success_handler' => 'foo', 'remember_me' => true), 'user_provider', 'entry_point');
4125

4226
// auth provider
4327
$this->assertEquals('auth_provider', $authProviderId);
@@ -48,19 +32,52 @@ public function testCreate()
4832
$definition = $container->getDefinition('abstract_listener.foo');
4933
$this->assertEquals(array(
5034
'index_4' => 'foo',
51-
'index_5' => array(
35+
'index_5' => new Reference('foo'),
36+
'index_6' => array(
5237
'use_forward' => true,
5338
'failure_path' => '/foo',
5439
),
55-
'index_6' => new Reference('foo'),
5640
), $definition->getArguments());
5741

5842
// entry point
5943
$this->assertEquals('entry_point', $entryPointId, '->create() does not change the default entry point.');
6044
}
6145

62-
protected function getFactory()
46+
public function testDefaultSuccessHandler()
47+
{
48+
list($container,
49+
$authProviderId,
50+
$listenerId,
51+
$entryPointId) = $this->callFactory('foo', array('remember_me' => true), 'user_provider', 'entry_point');
52+
53+
$definition = $container->getDefinition('abstract_listener.foo');
54+
$arguments = $definition->getArguments();
55+
$this->assertEquals(new Reference('security.authentication.success_handler.foo'), $arguments['index_5']);
56+
}
57+
58+
protected function callFactory($id, $config, $userProviderId, $defaultEntryPointId)
6359
{
64-
return $this->getMockForAbstractClass('Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory', array());
60+
$factory = $this->getMockForAbstractClass('Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory', array());
61+
62+
$factory
63+
->expects($this->once())
64+
->method('createAuthProvider')
65+
->will($this->returnValue('auth_provider'))
66+
;
67+
$factory
68+
->expects($this->atLeastOnce())
69+
->method('getListenerId')
70+
->will($this->returnValue('abstract_listener'))
71+
;
72+
73+
$container = new ContainerBuilder();
74+
$container->register('auth_provider');
75+
76+
list($authProviderId,
77+
$listenerId,
78+
$entryPointId
79+
) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId);
80+
81+
return array($container, $authProviderId, $listenerId, $entryPointId);
6582
}
6683
}

src/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandlerInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ interface AuthenticationSuccessHandlerInterface
3333
* @param Request $request
3434
* @param TokenInterface $token
3535
*
36-
* @return Response|null the response to return
36+
* @return Response never null
3737
*/
3838
function onAuthenticationSuccess(Request $request, TokenInterface $token);
3939
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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\Component\Security\Http\Authentication;
13+
14+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\Security\Http\HttpUtils;
17+
18+
/**
19+
* Class with the default authentication success handling logic.
20+
*
21+
* Can be optionally be extended from by the developer to alter the behaviour
22+
* while keeping the default behaviour.
23+
*
24+
* @author Alexander <iam.asm89@gmail.com>
25+
*/
26+
class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
27+
{
28+
/**
29+
* Constructor.
30+
*
31+
* @param HttpUtils $httpUtils HttpUtils
32+
* @param array $options Options for processing a successful authentication attempt.
33+
*/
34+
public function __construct(HttpUtils $httpUtils, array $options)
35+
{
36+
$this->httpUtils = $httpUtils;
37+
38+
$this->options = array_merge(array(
39+
'always_use_default_target_path' => false,
40+
'default_target_path' => '/',
41+
'target_path_parameter' => '_target_path',
42+
'use_referer' => false,
43+
), $options);
44+
}
45+
46+
/**
47+
* {@inheritDoc}
48+
*/
49+
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
50+
{
51+
return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
52+
}
53+
54+
/**
55+
* Builds the target URL according to the defined options.
56+
*
57+
* @param Request $request
58+
*
59+
* @return string
60+
*/
61+
protected function determineTargetUrl(Request $request)
62+
{
63+
if ($this->options['always_use_default_target_path']) {
64+
return $this->options['default_target_path'];
65+
}
66+
67+
if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) {
68+
return $targetUrl;
69+
}
70+
71+
$session = $request->getSession();
72+
if ($targetUrl = $session->get('_security.target_path')) {
73+
$session->remove('_security.target_path');
74+
75+
return $targetUrl;
76+
}
77+
78+
if ($this->options['use_referer'] && ($targetUrl = $request->headers->get('Referer')) && $targetUrl !== $request->getUriForPath($this->options['login_path'])) {
79+
return $targetUrl;
80+
}
81+
82+
return $this->options['default_target_path'];
83+
}
84+
}

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

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
7777
* @param LoggerInterface $logger A LoggerInterface instance
7878
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
7979
*/
80-
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
80+
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, array $options = array(), AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
8181
{
8282
if (empty($providerKey)) {
8383
throw new \InvalidArgumentException('$providerKey must not be empty.');
@@ -92,10 +92,6 @@ public function __construct(SecurityContextInterface $securityContext, Authentic
9292
$this->options = array_merge(array(
9393
'check_path' => '/login_check',
9494
'login_path' => '/login',
95-
'always_use_default_target_path' => false,
96-
'default_target_path' => '/',
97-
'target_path_parameter' => '_target_path',
98-
'use_referer' => false,
9995
'failure_path' => null,
10096
'failure_forward' => false,
10197
), $options);
@@ -238,49 +234,12 @@ private function onSuccess(GetResponseEvent $event, Request $request, TokenInter
238234
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
239235
}
240236

241-
$response = null;
242-
if (null !== $this->successHandler) {
243-
$response = $this->successHandler->onAuthenticationSuccess($request, $token);
244-
}
245-
if (null === $response) {
246-
$response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
247-
}
237+
$response = $this->successHandler->onAuthenticationSuccess($request, $token);
248238

249239
if (null !== $this->rememberMeServices) {
250240
$this->rememberMeServices->loginSuccess($request, $response, $token);
251241
}
252242

253243
return $response;
254244
}
255-
256-
/**
257-
* Builds the target URL according to the defined options.
258-
*
259-
* @param Request $request
260-
*
261-
* @return string
262-
*/
263-
private function determineTargetUrl(Request $request)
264-
{
265-
if ($this->options['always_use_default_target_path']) {
266-
return $this->options['default_target_path'];
267-
}
268-
269-
if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) {
270-
return $targetUrl;
271-
}
272-
273-
$session = $request->getSession();
274-
if ($targetUrl = $session->get('_security.' . $this->providerKey . '.target_path')) {
275-
$session->remove('_security.' . $this->providerKey . '.target_path');
276-
277-
return $targetUrl;
278-
}
279-
280-
if ($this->options['use_referer'] && ($targetUrl = $request->headers->get('Referer')) && $targetUrl !== $request->getUriForPath($this->options['login_path'])) {
281-
return $targetUrl;
282-
}
283-
284-
return $this->options['default_target_path'];
285-
}
286245
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
3737
/**
3838
* {@inheritdoc}
3939
*/
40-
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
40+
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler = null, array $options = array(), AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
4141
{
42-
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, array_merge(array(
42+
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, array_merge(array(
4343
'username_parameter' => '_username',
4444
'password_parameter' => '_password',
4545
'csrf_parameter' => '_csrf_token',
4646
'intention' => 'authenticate',
4747
'post_only' => true,
48-
), $options), $successHandler, $failureHandler, $logger, $dispatcher);
48+
), $options), $failureHandler, $logger, $dispatcher);
4949

5050
$this->csrfProvider = $csrfProvider;
5151
}

0 commit comments

Comments
 (0)
0