Skip to content

Commit

Permalink
logout refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
schmittjoh authored and fabpot committed Dec 8, 2010
1 parent 915973f commit d94420f
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 18 deletions.
Expand Up @@ -20,9 +20,9 @@
*/
interface SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $providerIds, $defaultEntryPoint);
function create(ContainerBuilder $container, $id, $config, $userProvider, $providerIds, $defaultEntryPoint);

public function getPosition();
function getPosition();

public function getKey();
function getKey();
}
Expand Up @@ -203,14 +203,31 @@ protected function createFirewall(ContainerBuilder $container, $firewall, $provi

// Logout listener
if (array_key_exists('logout', $firewall)) {
$listeners[] = new Reference('security.logout_listener');
$listenerId = 'security.logout_listener.'.$id;
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.logout_listener'));

$listeners[] = new Reference($listenerId);

$arguments = $listener->getArguments();
if (isset($firewall['logout']['path'])) {
$container->setParameter('security.logout.path', $firewall['logout']['path']);
$arguments[1] = $firewall['logout']['path'];
}

if (isset($firewall['logout']['target'])) {
$container->setParameter('security.logout.target_path', $firewall['logout']['target']);
$arguments[2] = $firewall['logout']['target'];
}
$listener->setArguments($arguments);

if (!isset($firewall['stateless']) || !$firewall['stateless']) {
$listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
}

if (count($cookies = $this->fixConfig($firewall['logout'], 'cookie')) > 0) {
$cookieHandlerId = 'security.logout.handler.cookie_clearing.'.$id;
$cookieHandler = $container->setDefinition($cookieHandlerId, clone $container->getDefinition('security.logout.handler.cookie_clearing'));
$cookieHandler->setArguments(array($cookies));

$listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId)));
}
}

Expand Down Expand Up @@ -426,6 +443,8 @@ protected function createAccessListener($container, $id, $providers)
return $listenerId;
}



protected function createExceptionListener($container, $id, $defaultEntryPoint)
{
$exceptionListenerId = 'security.exception_listener.'.$id;
Expand Down
10 changes: 4 additions & 6 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml
Expand Up @@ -49,6 +49,8 @@
<parameter key="security.logout_listener.class">Symfony\Component\HttpKernel\Security\Firewall\LogoutListener</parameter>
<parameter key="security.logout.path">/logout</parameter>
<parameter key="security.logout.target_path">/</parameter>
<parameter key="security.logout.handler.session.class">Symfony\Component\HttpKernel\Security\Logout\SessionLogoutHandler</parameter>
<parameter key="security.logout.handler.cookie_clearing.class">Symfony\Component\HttpKernel\Security\Logout\CookieClearingLogoutHandler</parameter>

<parameter key="security.authentication.switchuser_listener.class">Symfony\Component\HttpKernel\Security\Firewall\SwitchUserListener</parameter>
<parameter key="security.authentication.switchuser.role">ROLE_ALLOWED_TO_SWITCH</parameter>
Expand Down Expand Up @@ -91,6 +93,8 @@

<service id="security.encoder.plain" class="%security.encoder.plain.class%" />

<service id="security.logout.handler.session" class="%security.logout.handler.session.class%"></service>

<service id="security.authentication.listener.anonymous" class="%security.authentication.listener.anonymous.class%">
<argument type="service" id="security.context" />
<argument>%security.anonymous.key%</argument>
Expand All @@ -113,12 +117,6 @@
<argument>%security.authentication.digest_entry_point.key%</argument>
</service>

<service id="security.logout_listener" class="%security.logout_listener.class%">
<argument type="service" id="security.context" />
<argument>%security.logout.path%</argument>
<argument>%security.logout.target_path%</argument>
</service>

<service id="security.channel_listener" class="%security.channel_listener.class%">
<argument type="service" id="security.access_map" />
<argument type="service" id="security.authentication.retry_entry_point" />
Expand Down
Expand Up @@ -20,6 +20,16 @@
<service id="security.authentication.factory.digest" class="Symfony\Bundle\FrameworkBundle\DependencyInjection\Security\Factory\HttpDigestFactory">
<tag name="security.listener.factory" />
</service>

<service id="security.logout_listener" class="%security.logout_listener.class%">
<argument type="service" id="security.context" />
<argument>%security.logout.path%</argument>
<argument>%security.logout.target_path%</argument>
</service>

<service id="security.logout.handler.cookie_clearing" class="%security.logout.handler.cookie_clearing.class%">
<argument type="collection"></argument>
</service>

<service id="security.authentication.listener.form" class="%security.authentication.listener.form.class%">
<argument type="service" id="security.context" />
Expand Down
Expand Up @@ -2,6 +2,7 @@

namespace Symfony\Component\HttpKernel\Security\Firewall;

use Symfony\Component\HttpKernel\Security\Logout\LogoutHandlerInterface;
use Symfony\Component\Security\SecurityContext;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
Expand All @@ -26,10 +27,12 @@ class LogoutListener implements ListenerInterface
protected $securityContext;
protected $logoutPath;
protected $targetUrl;
protected $handlers;

/**
* Constructor
*
* @param SecurityContext $securityContext
* @param string $logoutPath The path that starts the logout process
* @param string $targetUrl The URL to redirect to after logout
*/
Expand All @@ -38,7 +41,19 @@ public function __construct(SecurityContext $securityContext, $logoutPath, $targ
$this->securityContext = $securityContext;
$this->logoutPath = $logoutPath;
$this->targetUrl = $targetUrl;
$this->handlers = array();
}

/**
* Adds a logout handler
*
* @param LogoutHandlerInterface $handler
* @return void
*/
public function addHandler(LogoutHandlerInterface $handler)
{
$this->handlers[] = $handler;
}

/**
* Registers a core.security listener.
Expand All @@ -59,7 +74,7 @@ public function unregister(EventDispatcher $dispatcher)
}

/**
*
* Performs the logout if requested
*
* @param Event $event An Event instance
*/
Expand All @@ -70,13 +85,17 @@ public function handle(Event $event)
if ($this->logoutPath !== $request->getPathInfo()) {
return;
}

$this->securityContext->setToken(null);
$request->getSession()->invalidate();


$response = new Response();
$response->setRedirect(0 !== strpos($this->targetUrl, 'http') ? $request->getUriForPath($this->targetUrl) : $this->targetUrl, 302);


$token = $this->securityContext->getToken();

foreach ($this->handlers as $handler) {
$handler->logout($request, $response, $token);
}

$this->securityContext->setToken(null);
$event->setReturnValue($response);

return true;
Expand Down
@@ -0,0 +1,61 @@
<?php

namespace Symfony\Component\HttpKernel\Security\Logout;

use Symfony\Component\Security\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* This handler cleares the passed cookies when a user logs out.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class CookieClearingLogoutHandler implements LogoutHandlerInterface
{
protected $cookieNames;

/**
* Constructor
* @param array $cookieNames An array of cookie names to unset
*/
public function __construct(array $cookieNames)
{
$this->cookieNames = $cookieNames;
}

/**
* Returns the names of the cookies to unset
* @return array
*/
public function getCookieNames()
{
return $this->cookieNames;
}

/**
* Implementation for the LogoutHandlerInterface. Deletes all requested cookies.
*
* @param Request $request
* @param Response $response
* @param TokenInterface $token
* @return void
*/
public function logout(Request $request, Response $response, TokenInterface $token)
{
$expires = time() - 86400;

foreach ($this->cookieNames as $cookieName) {
$response->headers->setCookie($cookieName, '', null, $expires);
}
}
}
@@ -0,0 +1,36 @@
<?php

namespace Symfony\Component\HttpKernel\Security\Logout;

use Symfony\Component\Security\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* Interface that needs to be implemented by LogoutHandlers.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface LogoutHandlerInterface
{
/**
* This method is called by the LogoutListener when a user has requested
* to be logged out. Usually, you would unset session variables, or remove
* cookies, etc.
*
* @param Request $request
* @param Response $response
* @param TokenInterface $token
* @return void
*/
function logout(Request $request, Response $response, TokenInterface $token);
}
@@ -0,0 +1,37 @@
<?php

namespace Symfony\Component\HttpKernel\Security\Logout;

use Symfony\Component\Security\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* Handler for clearing invalidating the current session.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class SessionLogoutHandler implements LogoutHandlerInterface
{
/**
* Invalidate the current session
*
* @param Request $request
* @param Response $response
* @param TokenInterface $token
* @return void
*/
public function logout(Request $request, Response $response, TokenInterface $token)
{
$request->getSession()->invalidate();
}
}
@@ -0,0 +1,42 @@
<?php

namespace Symfony\Tests\Component\HttpKernel\Security\Logout;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Security\Logout\CookieClearingLogoutHandler;

class CookieClearingLogoutHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$cookieNames = array('foo', 'foo2', 'foo3');

$handler = new CookieClearingLogoutHandler($cookieNames);

$this->assertEquals($cookieNames, $handler->getCookieNames());
}

public function testLogout()
{
$request = new Request();
$response = new Response();
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');

$handler = new CookieClearingLogoutHandler(array('foo', 'foo2'));

$this->assertFalse($response->headers->has('Set-Cookie'));

$handler->logout($request, $response, $token);

$headers = $response->headers->all();
$cookies = $headers['set-cookie'];
$this->assertEquals(2, count($cookies));

$cookie = $cookies[0];
$this->assertStringStartsWith('foo=;', $cookie);

$cookie = $cookies[1];
$this->assertStringStartsWith('foo2=;', $cookie);
}
}
@@ -0,0 +1,31 @@
<?php

namespace Symfony\Tests\Component\HttpKernel\Security\Logout;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Security\Logout\SessionLogoutHandler;

class SessionLogoutHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testLogout()
{
$handler = new SessionLogoutHandler();

$request = $this->getMock('Symfony\Component\HttpFoundation\Request');
$response = new Response();
$session = $this->getMock('Symfony\Component\HttpFoundation\Session', array(), array(), '', false);

$request
->expects($this->once())
->method('getSession')
->will($this->returnValue($session))
;

$session
->expects($this->once())
->method('invalidate')
;

$handler->logout($request, $response, $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
}
}

0 comments on commit d94420f

Please sign in to comment.