Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c8a4fef

Browse files
[Security] Make stateful firewalls turn responses private only when needed
1 parent ba313d3 commit c8a4fef

File tree

14 files changed

+352
-38
lines changed

14 files changed

+352
-38
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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 Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Bridge\Monolog\Processor\ProcessorInterface;
15+
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
16+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Component\DependencyInjection\Reference;
19+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
20+
21+
/**
22+
* Injects the session tracker enabler in "security.context_listener" + binds "security.untracked_token_storage" to ProcessorInterface instances.
23+
*
24+
* @author Nicolas Grekas <[email protected]>
25+
*
26+
* @internal
27+
*/
28+
class RegisterSessionUsageIndexPass implements CompilerPassInterface
29+
{
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function process(ContainerBuilder $container)
34+
{
35+
if (!$container->has('security.untracked_token_storage')) {
36+
return;
37+
}
38+
39+
$processorAutoconfiguration = $container->registerForAutoconfiguration(ProcessorInterface::class);
40+
$processorAutoconfiguration->setBindings($processorAutoconfiguration->getBindings() + [
41+
TokenStorageInterface::class => new BoundArgument(new Reference('security.untracked_token_storage'), false),
42+
]);
43+
44+
if (!$container->has('session')) {
45+
$container->setAlias('security.token_storage', 'security.untracked_token_storage')->setPublic(true);
46+
} elseif ($container->hasDefinition('security.context_listener')) {
47+
$container->getDefinition('security.context_listener')
48+
->setArgument(6, [new Reference('security.token_storage'), 'enableUsageTracking']);
49+
}
50+
}
51+
}

src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<service id="data_collector.security" class="Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector">
1111
<tag name="data_collector" template="@Security/Collector/security.html.twig" id="security" priority="270" />
12-
<argument type="service" id="security.token_storage" on-invalid="ignore" />
12+
<argument type="service" id="security.untracked_token_storage" />
1313
<argument type="service" id="security.role_hierarchy" />
1414
<argument type="service" id="security.logout_url_generator" />
1515
<argument type="service" id="security.access.decision_manager" />

src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@
2121
</service>
2222
<service id="Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface" alias="security.authorization_checker" />
2323

24-
<service id="security.token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage" public="true">
25-
<tag name="kernel.reset" method="setToken" />
24+
<service id="security.token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage" public="true">
25+
<tag name="kernel.reset" method="disableUsageTracking" />
26+
<argument type="service" id="security.untracked_token_storage" />
27+
<argument type="service_locator">
28+
<argument key="session" type="service" id="session" />
29+
</argument>
2630
</service>
2731
<service id="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface" alias="security.token_storage" />
2832

33+
<service id="security.untracked_token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage" />
34+
2935
<service id="security.helper" class="Symfony\Component\Security\Core\Security">
3036
<argument type="service_locator">
3137
<argument key="security.token_storage" type="service" id="security.token_storage" />
@@ -162,7 +168,7 @@
162168
<service id="security.logout_url_generator" class="Symfony\Component\Security\Http\Logout\LogoutUrlGenerator">
163169
<argument type="service" id="request_stack" on-invalid="null" />
164170
<argument type="service" id="router" on-invalid="null" />
165-
<argument type="service" id="security.token_storage" on-invalid="null" />
171+
<argument type="service" id="security.token_storage" />
166172
</service>
167173

168174
<!-- Provisioning -->

src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<service id="security.authentication.listener.anonymous" class="Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener">
1111
<tag name="monolog.logger" channel="security" />
12-
<argument type="service" id="security.token_storage" />
12+
<argument type="service" id="security.untracked_token_storage" />
1313
<argument /> <!-- Key -->
1414
<argument type="service" id="logger" on-invalid="null" />
1515
<argument type="service" id="security.authentication.manager" />
@@ -37,7 +37,7 @@
3737

3838
<service id="security.context_listener" class="Symfony\Component\Security\Http\Firewall\ContextListener">
3939
<tag name="monolog.logger" channel="security" />
40-
<argument type="service" id="security.token_storage" />
40+
<argument type="service" id="security.untracked_token_storage" />
4141
<argument type="collection" />
4242
<argument /> <!-- Provider Key -->
4343
<argument type="service" id="logger" on-invalid="null" />
@@ -128,7 +128,7 @@
128128

129129
<service id="security.authentication.listener.simple_preauth" class="Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener" abstract="true">
130130
<tag name="monolog.logger" channel="security" />
131-
<argument type="service" id="security.token_storage" />
131+
<argument type="service" id="security.untracked_token_storage" />
132132
<argument type="service" id="security.authentication.manager" />
133133
<argument /> <!-- Provider-shared Key -->
134134
<argument /> <!-- Authenticator -->

src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<service id="security.authentication.listener.rememberme" class="Symfony\Component\Security\Http\Firewall\RememberMeListener" abstract="true">
1111
<tag name="monolog.logger" channel="security" />
12-
<argument type="service" id="security.token_storage" />
12+
<argument type="service" id="security.untracked_token_storage" />
1313
<argument type="service" id="security.authentication.rememberme" />
1414
<argument type="service" id="security.authentication.manager" />
1515
<argument type="service" id="logger" on-invalid="null" />

src/Symfony/Bundle/SecurityBundle/SecurityBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass;
1616
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSessionDomainConstraintPass;
1717
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterCsrfTokenClearingLogoutHandlerPass;
18+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterSessionUsageIndexPass;
1819
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AnonymousFactory;
1920
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory;
2021
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
@@ -66,5 +67,6 @@ public function build(ContainerBuilder $container)
6667
$container->addCompilerPass(new AddSecurityVotersPass());
6768
$container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_BEFORE_REMOVING);
6869
$container->addCompilerPass(new RegisterCsrfTokenClearingLogoutHandlerPass());
70+
$container->addCompilerPass(new RegisterSessionUsageIndexPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 200);
6971
}
7072
}

src/Symfony/Component/HttpFoundation/Session/Session.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public function count()
139139
/**
140140
* @internal
141141
*/
142-
public function getUsageIndex(): int
142+
public function &getUsageIndex(): int
143143
{
144144
return $this->usageIndex;
145145
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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 Symfony\Component\Security\Core\Authentication\Token\Storage;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
16+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17+
use Symfony\Contracts\Service\ServiceSubscriberInterface;
18+
19+
/**
20+
* A token storage that increments the session usage index when the token is accessed.
21+
*
22+
* @author Nicolas Grekas <[email protected]>
23+
*/
24+
final class UsageTrackingTokenStorage implements TokenStorageInterface, ServiceSubscriberInterface
25+
{
26+
private $storage;
27+
private $sessionLocator;
28+
private $enableUsageTracking = false;
29+
30+
public function __construct(TokenStorageInterface $storage, ContainerInterface $sessionLocator)
31+
{
32+
$this->storage = $storage;
33+
$this->sessionLocator = $sessionLocator;
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function getToken(): ?TokenInterface
40+
{
41+
if ($this->enableUsageTracking) {
42+
// increments the internal session usage index
43+
$this->sessionLocator->get('session')->getMetadataBag();
44+
}
45+
46+
return $this->storage->getToken();
47+
}
48+
49+
/**
50+
* {@inheritdoc}
51+
*/
52+
public function setToken(TokenInterface $token = null): void
53+
{
54+
$this->storage->setToken($token);
55+
}
56+
57+
public function enableUsageTracking(): void
58+
{
59+
$this->enableUsageTracking = true;
60+
}
61+
62+
public function disableUsageTracking(): void
63+
{
64+
$this->enableUsageTracking = false;
65+
}
66+
67+
public static function getSubscribedServices(): array
68+
{
69+
return [
70+
'session' => SessionInterface::class,
71+
];
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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 Symfony\Component\Security\Core\Tests\Authentication\Token\Storage;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Psr\Container\ContainerInterface;
16+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
17+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
18+
use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage;
19+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
20+
use Symfony\Contracts\Service\ServiceLocatorTrait;
21+
22+
class UsageTrackingTokenStorageTest extends TestCase
23+
{
24+
public function testGetSetToken()
25+
{
26+
$sessionAccess = 0;
27+
$sessionLocator = new class(['session' => function () use (&$sessionAccess) {
28+
++$sessionAccess;
29+
30+
$session = $this->createMock(SessionInterface::class);
31+
$session->expects($this->once())
32+
->method('getMetadataBag');
33+
34+
return $session;
35+
}]) implements ContainerInterface {
36+
use ServiceLocatorTrait;
37+
};
38+
$tokenStorage = new TokenStorage();
39+
$trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $sessionLocator);
40+
41+
$this->assertNull($trackingStorage->getToken());
42+
$token = $this->getMockBuilder(TokenInterface::class)->getMock();
43+
44+
$trackingStorage->setToken($token);
45+
$this->assertSame($token, $trackingStorage->getToken());
46+
$this->assertSame($token, $tokenStorage->getToken());
47+
$this->assertSame(0, $sessionAccess);
48+
49+
$trackingStorage->enableUsageTracking();
50+
$this->assertSame($token, $trackingStorage->getToken());
51+
$this->assertSame(1, $sessionAccess);
52+
53+
$trackingStorage->disableUsageTracking();
54+
$this->assertSame($token, $trackingStorage->getToken());
55+
$this->assertSame(1, $sessionAccess);
56+
}
57+
}

src/Symfony/Component/Security/Http/Firewall/AccessListener.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,18 @@ public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionM
5151
*/
5252
public function __invoke(RequestEvent $event)
5353
{
54-
if (null === $token = $this->tokenStorage->getToken()) {
55-
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
56-
}
57-
5854
$request = $event->getRequest();
5955

6056
list($attributes) = $this->map->getPatterns($request);
6157

62-
if (null === $attributes) {
58+
if (!$attributes) {
6359
return;
6460
}
6561

62+
if (null === $token = $this->tokenStorage->getToken()) {
63+
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
64+
}
65+
6666
if (!$token->isAuthenticated()) {
6767
$token = $this->authManager->authenticate($token);
6868
$this->tokenStorage->setToken($token);

0 commit comments

Comments
 (0)