merged branch asm89/default-logout-success-handler (PR #4921)

Commits
-------

df2406f [Security] Add note to changelog about BC break
01b2e39 [Security] Extract default logout success handling logic

Discussion
----------

[Security] Extract default logout success handling logic

Bug fix: no
Feature addition: no
Backwards compatibility break: yes, small one for people using the component
Symfony2 tests pass: [![Build Status](https://secure.travis-ci.org/asm89/symfony.png?branch=default-logout-success-handler)](http://travis-ci.org/asm89/symfony)
License of the code: MIT

As discussed earlier with @fabpot and @schmittjoh. This PR extracts the default logout success handling logic to a separate class that users can extend.

Note: build status is red, but that is because of a failing performance test in the form component? ..
This commit is contained in:
Fabien Potencier 2012-07-15 10:12:14 +02:00
commit 80840fcd69
6 changed files with 75 additions and 20 deletions

View File

@ -279,18 +279,22 @@ class SecurityExtension extends Extension
if (isset($firewall['logout'])) { if (isset($firewall['logout'])) {
$listenerId = 'security.logout_listener.'.$id; $listenerId = 'security.logout_listener.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener')); $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener'));
$listener->replaceArgument(2, array( $listener->replaceArgument(3, array(
'csrf_parameter' => $firewall['logout']['csrf_parameter'], 'csrf_parameter' => $firewall['logout']['csrf_parameter'],
'intention' => $firewall['logout']['intention'], 'intention' => $firewall['logout']['intention'],
'logout_path' => $firewall['logout']['path'], 'logout_path' => $firewall['logout']['path'],
'target_url' => $firewall['logout']['target'],
)); ));
$listeners[] = new Reference($listenerId); $listeners[] = new Reference($listenerId);
// add logout success handler // add logout success handler
if (isset($firewall['logout']['success_handler'])) { if (isset($firewall['logout']['success_handler'])) {
$listener->replaceArgument(3, new Reference($firewall['logout']['success_handler'])); $logoutSuccessHandlerId = $firewall['logout']['success_handler'];
} else {
$logoutSuccessHandlerId = 'security.logout.success_handler.'.$id;
$logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new DefinitionDecorator('security.logout.success_handler'));
$logoutSuccessHandler->replaceArgument(1, $firewall['logout']['target']);
} }
$listener->replaceArgument(2, new Reference($logoutSuccessHandlerId));
// add CSRF provider // add CSRF provider
if (isset($firewall['logout']['csrf_provider'])) { if (isset($firewall['logout']['csrf_provider'])) {

View File

@ -27,6 +27,7 @@
<parameter key="security.logout_listener.class">Symfony\Component\Security\Http\Firewall\LogoutListener</parameter> <parameter key="security.logout_listener.class">Symfony\Component\Security\Http\Firewall\LogoutListener</parameter>
<parameter key="security.logout.handler.session.class">Symfony\Component\Security\Http\Logout\SessionLogoutHandler</parameter> <parameter key="security.logout.handler.session.class">Symfony\Component\Security\Http\Logout\SessionLogoutHandler</parameter>
<parameter key="security.logout.handler.cookie_clearing.class">Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler</parameter> <parameter key="security.logout.handler.cookie_clearing.class">Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler</parameter>
<parameter key="security.logout.success_handler.class">Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler</parameter>
<parameter key="security.access_listener.class">Symfony\Component\Security\Http\Firewall\AccessListener</parameter> <parameter key="security.access_listener.class">Symfony\Component\Security\Http\Firewall\AccessListener</parameter>
<parameter key="security.access_map.class">Symfony\Component\Security\Http\AccessMap</parameter> <parameter key="security.access_map.class">Symfony\Component\Security\Http\AccessMap</parameter>
@ -84,12 +85,17 @@
<service id="security.logout_listener" class="%security.logout_listener.class%" public="false" abstract="true"> <service id="security.logout_listener" class="%security.logout_listener.class%" public="false" abstract="true">
<argument type="service" id="security.context" /> <argument type="service" id="security.context" />
<argument type="service" id="security.http_utils" /> <argument type="service" id="security.http_utils" />
<argument type="service" id="security.logout.success_handler" />
<argument /> <!-- Options --> <argument /> <!-- Options -->
<argument type="service" id="security.logout.success_handler" on-invalid="null" />
</service> </service>
<service id="security.logout.handler.session" class="%security.logout.handler.session.class%" public="false" /> <service id="security.logout.handler.session" class="%security.logout.handler.session.class%" public="false" />
<service id="security.logout.handler.cookie_clearing" class="%security.logout.handler.cookie_clearing.class%" public="false" abstract="true" /> <service id="security.logout.handler.cookie_clearing" class="%security.logout.handler.cookie_clearing.class%" public="false" abstract="true" />
<service id="security.logout.success_handler" class="%security.logout.success_handler.class%" public="false" abstract="true">
<argument type="service" id="security.http_utils" />
<argument>/</argument>
</service>
<service id="security.authentication.form_entry_point" class="%security.authentication.form_entry_point.class%" public="false" abstract="true"> <service id="security.authentication.form_entry_point" class="%security.authentication.form_entry_point.class%" public="false" abstract="true">
<argument type="service" id="http_kernel" /> <argument type="service" id="http_kernel" />
</service> </service>

View File

@ -23,3 +23,5 @@ CHANGELOG
* [BC BREAK] moved the default authentication success and failure handling to * [BC BREAK] moved the default authentication success and failure handling to
seperate classes. The order of arguments in the constructor of the seperate classes. The order of arguments in the constructor of the
`AbstractAuthenticationListener` has changed. `AbstractAuthenticationListener` has changed.
* [BC BREAK] moved the default logout sucess handling to a seperate class. The
order of arguments in the constructor of `LogoutListener` has changed.

View File

@ -40,11 +40,11 @@ class LogoutListener implements ListenerInterface
* *
* @param SecurityContextInterface $securityContext * @param SecurityContextInterface $securityContext
* @param HttpUtils $httpUtils An HttpUtilsInterface instance * @param HttpUtils $httpUtils An HttpUtilsInterface instance
* @param array $options An array of options to process a logout attempt
* @param LogoutSuccessHandlerInterface $successHandler A LogoutSuccessHandlerInterface instance * @param LogoutSuccessHandlerInterface $successHandler A LogoutSuccessHandlerInterface instance
* @param array $options An array of options to process a logout attempt
* @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance * @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance
*/ */
public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, array $options = array(), LogoutSuccessHandlerInterface $successHandler = null, CsrfProviderInterface $csrfProvider = null) public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, LogoutSuccessHandlerInterface $successHandler, array $options = array(), CsrfProviderInterface $csrfProvider = null)
{ {
$this->securityContext = $securityContext; $this->securityContext = $securityContext;
$this->httpUtils = $httpUtils; $this->httpUtils = $httpUtils;
@ -52,7 +52,6 @@ class LogoutListener implements ListenerInterface
'csrf_parameter' => '_csrf_token', 'csrf_parameter' => '_csrf_token',
'intention' => 'logout', 'intention' => 'logout',
'logout_path' => '/logout', 'logout_path' => '/logout',
'target_url' => '/',
), $options); ), $options);
$this->successHandler = $successHandler; $this->successHandler = $successHandler;
$this->csrfProvider = $csrfProvider; $this->csrfProvider = $csrfProvider;
@ -95,14 +94,9 @@ class LogoutListener implements ListenerInterface
} }
} }
if (null !== $this->successHandler) { $response = $this->successHandler->onLogoutSuccess($request);
$response = $this->successHandler->onLogoutSuccess($request); if (!$response instanceof Response) {
throw new \RuntimeException('Logout Success Handler did not return a Response.');
if (!$response instanceof Response) {
throw new \RuntimeException('Logout Success Handler did not return a Response.');
}
} else {
$response = $this->httpUtils->createRedirectResponse($request, $this->options['target_url']);
} }
// handle multiple logout attempts gracefully // handle multiple logout attempts gracefully

View File

@ -0,0 +1,47 @@
<?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\Component\Security\Http\Logout;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
/**
* Default logout success handler will redirect users to a configured path.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Alexander <iam.asm89@gmail.com>
*/
class DefaultLogoutSuccessHandler implements LogoutSuccessHandlerInterface
{
protected $httpUtils;
protected $targetUrl;
/**
* @param HttpUtils $httpUtils
* @param string $targetUrl
*/
public function __construct(HttpUtils $httpUtils, $targetUrl = '/')
{
$this->httpUtils = $httpUtils;
$this->targetUrl = $targetUrl;
}
/**
* {@inheritDoc}
*/
public function onLogoutSuccess(Request $request)
{
return $this->httpUtils->createRedirectResponse($request, $this->targetUrl);
}
}

View File

@ -103,7 +103,9 @@ class LogoutListenerTest extends \PHPUnit_Framework_TestCase
public function testHandleMatchedPathWithoutSuccessHandlerAndCsrfValidation() public function testHandleMatchedPathWithoutSuccessHandlerAndCsrfValidation()
{ {
list($listener, $context, $httpUtils, $options) = $this->getListener(); $successHandler = $this->getSuccessHandler();
list($listener, $context, $httpUtils, $options) = $this->getListener($successHandler);
list($event, $request) = $this->getGetResponseEvent(); list($event, $request) = $this->getGetResponseEvent();
@ -112,9 +114,9 @@ class LogoutListenerTest extends \PHPUnit_Framework_TestCase
->with($request, $options['logout_path']) ->with($request, $options['logout_path'])
->will($this->returnValue(true)); ->will($this->returnValue(true));
$httpUtils->expects($this->once()) $successHandler->expects($this->once())
->method('createRedirectResponse') ->method('onLogoutSuccess')
->with($request, $options['target_url']) ->with($request)
->will($this->returnValue($response = new Response())); ->will($this->returnValue($response = new Response()));
$context->expects($this->once()) $context->expects($this->once())
@ -231,13 +233,13 @@ class LogoutListenerTest extends \PHPUnit_Framework_TestCase
$listener = new LogoutListener( $listener = new LogoutListener(
$context = $this->getContext(), $context = $this->getContext(),
$httpUtils = $this->getHttpUtils(), $httpUtils = $this->getHttpUtils(),
$successHandler ?: $this->getSuccessHandler(),
$options = array( $options = array(
'csrf_parameter' => '_csrf_token', 'csrf_parameter' => '_csrf_token',
'intention' => 'logout', 'intention' => 'logout',
'logout_path' => '/logout', 'logout_path' => '/logout',
'target_url' => '/', 'target_url' => '/',
), ),
$successHandler,
$csrfProvider $csrfProvider
); );