Skip to content

Commit 3ec143a

Browse files
wesnickdbu
authored andcommitted
Fix ContextInvalidationLogoutHandler (#394)
Replace SessionLogoutHandler service instead of a logout listener The logout listener is only called after the session has been invalidated and thus invalidates the wrong session
1 parent e4df22c commit 3ec143a

File tree

9 files changed

+135
-61
lines changed

9 files changed

+135
-61
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ Changelog
2424

2525
* User context is more reliable not cache when the hash mismatches. (E.g. after
2626
login/logout.)
27+
28+
* The `ContextInvalidationLogoutHandler` has been deprecated in favor of the
29+
`ContextInvalidationSessionLogoutHandler`. The original handler was called
30+
after the invalidation of the session, and thus did not invalidate the session
31+
it should have but a newly created one. You should remove the deprecated service
32+
`fos_http_cache.user_context.logout_handler` from the logout.handlers section
33+
of your firewall configuration.
2734

2835
2.1.0
2936
-----

Resources/doc/reference/configuration/user-context.rst

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,11 @@ For the handler to work:
147147
* Symfony’s default behavior of regenerating the session id when users log in
148148
and out must be enabled (``invalidate_session``).
149149

150-
Add the handler to your firewall configuration:
151-
152-
.. code-block:: yaml
153-
154-
# app/config/security.yml
155-
security:
156-
firewalls:
157-
secured_area:
158-
logout:
159-
invalidate_session: true
160-
handlers:
161-
- fos_http_cache.user_context.logout_handler
150+
.. note::
151+
The logout handler is active on all firewalls. If your application has multiple firewalls
152+
with different user context, you need to create your own custom invalidation handler. Be
153+
aware that Symfony's `LogoutSuccessHandler` places the `SessionLogoutHandler` before any
154+
configured logout handlers.
162155

163156
enabled
164157
"""""""

src/DependencyInjection/FOSHttpCacheExtension.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,15 @@ private function loadUserContext(ContainerBuilder $container, XmlFileLoader $loa
292292
->replaceArgument(0, $config['user_identifier_headers']);
293293

294294
if ($config['logout_handler']['enabled']) {
295-
$container->getDefinition($this->getAlias().'.user_context.logout_handler')
295+
$container->getDefinition($this->getAlias().'.user_context_invalidator')
296296
->replaceArgument(1, $config['user_identifier_headers'])
297297
->replaceArgument(2, $config['match']['accept']);
298+
299+
$container->setAlias('security.logout.handler.session', $this->getAlias().'.user_context.session_logout_handler');
298300
} else {
299301
$container->removeDefinition($this->getAlias().'.user_context.logout_handler');
302+
$container->removeDefinition($this->getAlias().'.user_context.session_logout_handler');
303+
$container->removeDefinition($this->getAlias().'.user_context_invalidator');
300304
}
301305

302306
if ($config['role_provider']) {

src/Resources/config/user_context.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,21 @@
2525
<argument type="service" id="security.token_storage" on-invalid="ignore" />
2626
</service>
2727

28-
<service id="fos_http_cache.user_context.logout_handler" class="FOS\HttpCacheBundle\Security\Http\Logout\ContextInvalidationLogoutHandler">
28+
<service id="fos_http_cache.user_context_invalidator" class="FOS\HttpCacheBundle\UserContextInvalidator">
2929
<argument type="service" id="fos_http_cache.default_proxy_client" />
3030
<argument />
3131
<argument />
3232
</service>
3333

34+
<service id="fos_http_cache.user_context.logout_handler" class="FOS\HttpCacheBundle\Security\Http\Logout\ContextInvalidationLogoutHandler" public="false">
35+
<argument type="service" id="fos_http_cache.user_context_invalidator" />
36+
<deprecated>The "%service_id%" service is deprecated since 2.2 and will be removed in 3.0.</deprecated>
37+
</service>
38+
39+
<service id="fos_http_cache.user_context.session_logout_handler" class="FOS\HttpCacheBundle\Security\Http\Logout\ContextInvalidationSessionLogoutHandler" public="false">
40+
<argument type="service" id="fos_http_cache.user_context_invalidator" />
41+
</service>
42+
3443
<service id="fos_http_cache.user_context.anonymous_request_matcher" class="FOS\HttpCacheBundle\UserContext\AnonymousRequestMatcher">
3544
<argument type="collection" />
3645
</service>

src/Security/Http/Logout/ContextInvalidationLogoutHandler.php

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,58 +11,33 @@
1111

1212
namespace FOS\HttpCacheBundle\Security\Http\Logout;
1313

14-
use FOS\HttpCache\ProxyClient\Invalidation\BanCapable;
14+
use FOS\HttpCacheBundle\UserContextInvalidator;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpFoundation\Response;
1717
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
1818
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
1919

20-
class ContextInvalidationLogoutHandler implements LogoutHandlerInterface
20+
/**
21+
* @deprecated use ContextInvalidationSessionLogoutHandler in this same namespace as a replacement
22+
*
23+
* This handler is deprecated because it never did what it was supposed to do. The session is already invalidated by the SessionLogoutHandler
24+
* which is always the first logout handler executed
25+
*/
26+
final class ContextInvalidationLogoutHandler implements LogoutHandlerInterface
2127
{
22-
/**
23-
* Service used to ban hash request.
24-
*
25-
* @var BanCapable
26-
*/
27-
private $banner;
28+
private $invalidator;
2829

29-
/**
30-
* Accept header.
31-
*
32-
* @var string
33-
*/
34-
private $acceptHeader;
35-
36-
/**
37-
* User identifier headers.
38-
*
39-
* @var string[]
40-
*/
41-
private $userIdentifierHeaders;
42-
43-
public function __construct(BanCapable $banner, $userIdentifierHeaders, $acceptHeader)
30+
public function __construct(UserContextInvalidator $invalidator)
4431
{
45-
$this->banner = $banner;
46-
$this->acceptHeader = $acceptHeader;
47-
$this->userIdentifierHeaders = $userIdentifierHeaders;
32+
$this->invalidator = $invalidator;
4833
}
4934

5035
/**
51-
* Invalidate the user context hash.
52-
*
53-
* @param Request $request
54-
* @param Response $response
55-
* @param TokenInterface $token
36+
* {@inheritdoc}
5637
*/
5738
public function logout(Request $request, Response $response, TokenInterface $token)
5839
{
59-
$sessionId = $request->getSession()->getId();
60-
61-
foreach ($this->userIdentifierHeaders as $header) {
62-
$this->banner->ban([
63-
'accept' => $this->acceptHeader,
64-
$header => sprintf('.*%s.*', $sessionId),
65-
]);
66-
}
40+
@trigger_error('Using the ContextInvalidationLogoutHandler is deprecated', E_USER_DEPRECATED);
41+
$this->invalidator->invalidateContext($request->getSession()->getId());
6742
}
6843
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCacheBundle package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.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 FOS\HttpCacheBundle\Security\Http\Logout;
13+
14+
use FOS\HttpCacheBundle\UserContextInvalidator;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
18+
use Symfony\Component\Security\Http\Logout\SessionLogoutHandler;
19+
20+
final class ContextInvalidationSessionLogoutHandler extends SessionLogoutHandler
21+
{
22+
private $invalidator;
23+
24+
public function __construct(UserContextInvalidator $invalidator)
25+
{
26+
$this->invalidator = $invalidator;
27+
}
28+
29+
public function logout(Request $request, Response $response, TokenInterface $token)
30+
{
31+
$this->invalidator->invalidateContext($request->getSession()->getId());
32+
parent::logout($request, $response, $token);
33+
}
34+
}

src/UserContextInvalidator.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCacheBundle package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.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 FOS\HttpCacheBundle;
13+
14+
use FOS\HttpCache\ProxyClient\Invalidation\BanCapable;
15+
16+
class UserContextInvalidator
17+
{
18+
/**
19+
* Service used to ban hash request.
20+
*
21+
* @var BanCapable
22+
*/
23+
private $banner;
24+
25+
/**
26+
* Accept header.
27+
*
28+
* @var string
29+
*/
30+
private $acceptHeader;
31+
32+
/**
33+
* User identifier headers.
34+
*
35+
* @var string[]
36+
*/
37+
private $userIdentifierHeaders;
38+
39+
public function __construct(BanCapable $banner, $userIdentifierHeaders, $acceptHeader)
40+
{
41+
$this->banner = $banner;
42+
$this->acceptHeader = $acceptHeader;
43+
$this->userIdentifierHeaders = $userIdentifierHeaders;
44+
}
45+
46+
/**
47+
* Invalidate the user context hash.
48+
*
49+
* @param string $sessionId
50+
*/
51+
public function invalidateContext($sessionId)
52+
{
53+
foreach ($this->userIdentifierHeaders as $header) {
54+
$this->banner->ban([
55+
'accept' => $this->acceptHeader,
56+
$header => sprintf('.*%s.*', $sessionId),
57+
]);
58+
}
59+
}
60+
}

tests/Functional/Fixtures/app/config/config.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,3 @@ security:
7474
anonymous:
7575
logout:
7676
path: /secured_area/logout
77-
handlers:
78-
- fos_http_cache.user_context.logout_handler

tests/Functional/Security/Http/Logout/ContextInvalidationLogoutHandlerTest.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,6 @@ class ContextInvalidationLogoutHandlerTest extends WebTestCase
2020
{
2121
public function testLogout()
2222
{
23-
$this->markTestSkipped(<<<'EOF'
24-
Session is invalidated in LogoutListener before Proxy Client can invalidate cache.
25-
@see https://github.com/FriendsOfSymfony/FOSHttpCacheBundle/pull/390#issuecomment-333545374
26-
EOF
27-
);
28-
2923
$client = static::createClient();
3024
$session = $client->getContainer()->get('session');
3125

0 commit comments

Comments
 (0)