Skip to content

Commit f8de402

Browse files
authored
Merge pull request #516 from FriendsOfSymfony/without-session
do not leak the Symfony-Session-NoAutoCacheControl header when the Symfony session system is not enabled.
2 parents b2a3d20 + 929f9ca commit f8de402

File tree

6 files changed

+67
-13
lines changed

6 files changed

+67
-13
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Changelog
22
=========
33

4+
2.6.1
5+
-----
6+
7+
### Fixed
8+
9+
* Do not leak the `Symfony-Session-NoAutoCacheControl` header when the Symfony session system is not enabled.
10+
411
2.6.0
512
-----
613

src/DependencyInjection/Compiler/SessionListenerRemovePass.php renamed to src/DependencyInjection/Compiler/SessionListenerPass.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
1616

1717
/**
18-
* Remove the session listener decorator again if the application has no session listener.
18+
* Undo our workarounds for the Symfony session listener when the session
19+
* system of Symfony has not been activated.
1920
*
20-
* This will happen on some APIs when the session system is not activated.
21+
* - Set the hasSessionListener option of the UserContextListener to false to
22+
* avoid the AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER being
23+
* leaked to clients.
24+
* - Remove the session listener decorator we configured.
2125
*/
22-
class SessionListenerRemovePass implements CompilerPassInterface
26+
class SessionListenerPass implements CompilerPassInterface
2327
{
2428
/**
2529
* {@inheritdoc}
@@ -30,6 +34,10 @@ public function process(ContainerBuilder $container)
3034
return;
3135
}
3236

37+
if ($container->hasDefinition('fos_http_cache.event_listener.user_context')) {
38+
$contextListener = $container->getDefinition('fos_http_cache.event_listener.user_context');
39+
$contextListener->setArgument(5, false);
40+
}
3341
$container->removeDefinition('fos_http_cache.user_context.session_listener');
3442
}
3543
}

src/EventListener/UserContextListener.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ class UserContextListener implements EventSubscriberInterface
5959
*/
6060
private $options;
6161

62+
/**
63+
* Whether the application has a session listener and therefore could
64+
* require the AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER.
65+
*
66+
* @var bool
67+
*/
68+
private $hasSessionListener;
69+
6270
/**
6371
* @var string
6472
*/
@@ -78,12 +86,14 @@ public function __construct(
7886
HashGenerator $hashGenerator,
7987
RequestMatcherInterface $anonymousRequestMatcher = null,
8088
ResponseTagger $responseTagger = null,
81-
array $options = []
89+
array $options = [],
90+
bool $hasSessionListener = true
8291
) {
8392
$this->requestMatcher = $requestMatcher;
8493
$this->hashGenerator = $hashGenerator;
85-
$this->responseTagger = $responseTagger;
8694
$this->anonymousRequestMatcher = $anonymousRequestMatcher;
95+
$this->responseTagger = $responseTagger;
96+
$this->hasSessionListener = $hasSessionListener;
8797

8898
$resolver = new OptionsResolver();
8999
$resolver->setDefaults([
@@ -146,7 +156,7 @@ public function onKernelRequest(GetResponseEvent $event)
146156
$response->setClientTtl($this->options['ttl']);
147157
$response->setVary($this->options['user_identifier_headers']);
148158
$response->setPublic();
149-
if (4 <= Kernel::MAJOR_VERSION && 1 <= Kernel::MINOR_VERSION) {
159+
if ($this->hasSessionListener && version_compare('4.1', Kernel::VERSION, '<=')) {
150160
// header to avoid Symfony SessionListener overwriting the response to private
151161
$response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, 1);
152162
}
@@ -210,7 +220,7 @@ public function onKernelResponse(FilterResponseEvent $event)
210220
}
211221

212222
// For Symfony 4.1+ if user hash header was in vary or just added here by "add_vary_on_hash"
213-
if (4 <= Kernel::MAJOR_VERSION && 1 <= Kernel::MINOR_VERSION && in_array($this->options['user_hash_header'], $vary)) {
223+
if ($this->hasSessionListener && \version_compare('4.1', Kernel::VERSION, '<=') && in_array($this->options['user_hash_header'], $vary)) {
214224
// header to avoid Symfony SessionListener overwriting the response to private
215225
$response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, 1);
216226
}

src/FOSHttpCacheBundle.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use FOS\HttpCache\UserContext\ContextProvider;
1515
use FOS\HttpCacheBundle\DependencyInjection\Compiler\HashGeneratorPass;
1616
use FOS\HttpCacheBundle\DependencyInjection\Compiler\LoggerPass;
17-
use FOS\HttpCacheBundle\DependencyInjection\Compiler\SessionListenerRemovePass;
17+
use FOS\HttpCacheBundle\DependencyInjection\Compiler\SessionListenerPass;
1818
use FOS\HttpCacheBundle\DependencyInjection\Compiler\TagListenerPass;
1919
use Symfony\Component\DependencyInjection\ContainerBuilder;
2020
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -33,7 +33,7 @@ public function build(ContainerBuilder $container)
3333
if (version_compare(Kernel::VERSION, '3.4', '>=')
3434
&& version_compare(Kernel::VERSION, '4.1', '<')
3535
) {
36-
$container->addCompilerPass(new SessionListenerRemovePass());
36+
$container->addCompilerPass(new SessionListenerPass());
3737
}
3838

3939
// Symfony 3.3 and higher

src/Resources/config/user_context.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<argument type="service" id="fos_http_cache.user_context.anonymous_request_matcher" />
2020
<argument type="service" id="fos_http_cache.http.symfony_response_tagger" on-invalid="ignore" />
2121
<argument>%fos_http_cache.event_listener.user_context.options%</argument>
22+
<argument>true</argument>
2223
<tag name="kernel.event_subscriber" />
2324
</service>
2425

tests/Unit/EventListener/UserContextListenerTest.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public function testOnKernelResponse()
203203

204204
public function testOnKernelResponseSetsNoAutoCacheHeader()
205205
{
206-
if (4 > Kernel::MAJOR_VERSION || 1 > Kernel::MINOR_VERSION) {
206+
if (\version_compare('4.1', Kernel::VERSION, '>')) {
207207
$this->markTestSkipped('Test only relevant for Symfony 4.1 and up');
208208
}
209209

@@ -225,9 +225,37 @@ public function testOnKernelResponseSetsNoAutoCacheHeader()
225225
$this->assertEquals(1, $event->getResponse()->headers->get(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER));
226226
}
227227

228+
public function testOnKernelResponseDoesNotSetNoAutoCacheHeaderWhenNoSessionListener()
229+
{
230+
if (\version_compare('4.1', Kernel::VERSION, '>')) {
231+
$this->markTestSkipped('Test only relevant for Symfony 4.1 and up');
232+
}
233+
234+
$request = new Request();
235+
$request->setMethod('HEAD');
236+
$request->headers->set('X-User-Context-Hash', 'hash');
237+
238+
$hashGenerator = \Mockery::mock(HashGenerator::class);
239+
240+
$userContextListener = new UserContextListener(
241+
$this->getRequestMatcher($request, false),
242+
$hashGenerator,
243+
null,
244+
null,
245+
[],
246+
false
247+
);
248+
$event = $this->getKernelResponseEvent($request);
249+
250+
$userContextListener->onKernelResponse($event);
251+
252+
$this->assertContains('X-User-Context-Hash', $event->getResponse()->headers->get('Vary'));
253+
$this->assertFalse($event->getResponse()->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER));
254+
}
255+
228256
public function testOnKernelResponseSetsNoAutoCacheHeaderWhenCustomHeader()
229257
{
230-
if (4 > Kernel::MAJOR_VERSION || 1 > Kernel::MINOR_VERSION) {
258+
if (\version_compare('4.1', Kernel::VERSION, '>')) {
231259
$this->markTestSkipped('Test only relevant for Symfony 4.1 and up');
232260
}
233261

@@ -250,7 +278,7 @@ public function testOnKernelResponseSetsNoAutoCacheHeaderWhenCustomHeader()
250278

251279
public function testOnKernelResponseSetsNoAutoCacheHeaderWhenCustomHeaderAndNoAddVaryOnHash()
252280
{
253-
if (4 > Kernel::MAJOR_VERSION || 1 > Kernel::MINOR_VERSION) {
281+
if (\version_compare('4.1', Kernel::VERSION, '>')) {
254282
$this->markTestSkipped('Test only relevant for Symfony 4.1 and up');
255283
}
256284

@@ -278,7 +306,7 @@ public function testOnKernelResponseSetsNoAutoCacheHeaderWhenCustomHeaderAndNoAd
278306

279307
public function testOnKernelResponseDoesNotSetNoAutoCacheHeaderWhenNoCustomHeaderAndNoAddVaryOnHash()
280308
{
281-
if (4 > Kernel::MAJOR_VERSION || 1 > Kernel::MINOR_VERSION) {
309+
if (\version_compare('4.1', Kernel::VERSION, '>')) {
282310
$this->markTestSkipped('Test only relevant for Symfony 4.1 and up');
283311
}
284312

0 commit comments

Comments
 (0)