diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php
index 0d539f146cf6..ab5fb7e77813 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php
@@ -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();
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/SecurityExtension.php
index f13c2a817fe2..9cb97e867826 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/SecurityExtension.php
@@ -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)));
}
}
@@ -426,6 +443,8 @@ protected function createAccessListener($container, $id, $providers)
return $listenerId;
}
+
+
protected function createExceptionListener($container, $id, $defaultEntryPoint)
{
$exceptionListenerId = 'security.exception_listener.'.$id;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml
index 0efcc6396c03..18d63a375080 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml
@@ -49,6 +49,8 @@
Symfony\Component\HttpKernel\Security\Firewall\LogoutListener
/logout
/
+ Symfony\Component\HttpKernel\Security\Logout\SessionLogoutHandler
+ Symfony\Component\HttpKernel\Security\Logout\CookieClearingLogoutHandler
Symfony\Component\HttpKernel\Security\Firewall\SwitchUserListener
ROLE_ALLOWED_TO_SWITCH
@@ -91,6 +93,8 @@
+
+
%security.anonymous.key%
@@ -113,12 +117,6 @@
%security.authentication.digest_entry_point.key%
-
-
- %security.logout.path%
- %security.logout.target_path%
-
-
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_templates.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_templates.xml
index af6084e382ba..602dc0523f86 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_templates.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_templates.xml
@@ -20,6 +20,16 @@
+
+
+
+ %security.logout.path%
+ %security.logout.target_path%
+
+
+
+
+
diff --git a/src/Symfony/Component/HttpKernel/Security/Firewall/LogoutListener.php b/src/Symfony/Component/HttpKernel/Security/Firewall/LogoutListener.php
index 5b5794ca8d49..5b6a2402fb20 100644
--- a/src/Symfony/Component/HttpKernel/Security/Firewall/LogoutListener.php
+++ b/src/Symfony/Component/HttpKernel/Security/Firewall/LogoutListener.php
@@ -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;
@@ -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
*/
@@ -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.
@@ -59,7 +74,7 @@ public function unregister(EventDispatcher $dispatcher)
}
/**
- *
+ * Performs the logout if requested
*
* @param Event $event An Event instance
*/
@@ -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;
diff --git a/src/Symfony/Component/HttpKernel/Security/Logout/CookieClearingLogoutHandler.php b/src/Symfony/Component/HttpKernel/Security/Logout/CookieClearingLogoutHandler.php
new file mode 100644
index 000000000000..f95045ad478f
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/Security/Logout/CookieClearingLogoutHandler.php
@@ -0,0 +1,61 @@
+
+ *
+ * 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
+ */
+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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/HttpKernel/Security/Logout/LogoutHandlerInterface.php b/src/Symfony/Component/HttpKernel/Security/Logout/LogoutHandlerInterface.php
new file mode 100644
index 000000000000..67f6e3494714
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/Security/Logout/LogoutHandlerInterface.php
@@ -0,0 +1,36 @@
+
+ *
+ * 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
+ */
+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);
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/HttpKernel/Security/Logout/SessionLogoutHandler.php b/src/Symfony/Component/HttpKernel/Security/Logout/SessionLogoutHandler.php
new file mode 100644
index 000000000000..c92e2d6ec1b2
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/Security/Logout/SessionLogoutHandler.php
@@ -0,0 +1,37 @@
+
+ *
+ * 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
+ */
+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();
+ }
+}
\ No newline at end of file
diff --git a/tests/Symfony/Tests/Component/HttpKernel/Security/Logout/CookieClearingLogoutHandlerTest.php b/tests/Symfony/Tests/Component/HttpKernel/Security/Logout/CookieClearingLogoutHandlerTest.php
new file mode 100644
index 000000000000..3d16b4850601
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpKernel/Security/Logout/CookieClearingLogoutHandlerTest.php
@@ -0,0 +1,42 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/tests/Symfony/Tests/Component/HttpKernel/Security/Logout/SessionLogoutHandlerTest.php b/tests/Symfony/Tests/Component/HttpKernel/Security/Logout/SessionLogoutHandlerTest.php
new file mode 100644
index 000000000000..57b7d3aa7d9a
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpKernel/Security/Logout/SessionLogoutHandlerTest.php
@@ -0,0 +1,31 @@
+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'));
+ }
+}
\ No newline at end of file