Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #33663 [Security] Make stateful firewalls turn responses priv…
…ate only when needed (nicolas-grekas) This PR was merged into the 4.4 branch. Discussion ---------- [Security] Make stateful firewalls turn responses private only when needed | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #26769 *et al.* | License | MIT | Doc PR | - Replaces #28089 By taking over session usage tracking and replacing it with token usage tracking, we can prevent responses that don't actually use the token from turning responses private without changing anything to the lifecycle of security listeners. This makes the behavior much more seamless, allowing to still log the user with the monolog processor, and display it in the profiler toolbar. This works by using two separate token storage services: - `security.token_storage` now tracks access to the token and increments the session usage tracker when needed. This is the service that is injected in userland. - `security.untracked_token_storage` is a raw token storage that just stores the token and is disconnected from the session. This service is injected in places where reading the session doesn't impact the generated output in any way (as e.g. in Monolog processors, etc.) Commits ------- 20df3a1 [Security] Make stateful firewalls turn responses private only when needed
- Loading branch information
Showing
15 changed files
with
353 additions
and
41 deletions.
There are no files selected for viewing
51 changes: 51 additions & 0 deletions
51
...ony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler; | ||
|
||
use Symfony\Bridge\Monolog\Processor\ProcessorInterface; | ||
use Symfony\Component\DependencyInjection\Argument\BoundArgument; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | ||
|
||
/** | ||
* Injects the session tracker enabler in "security.context_listener" + binds "security.untracked_token_storage" to ProcessorInterface instances. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
* | ||
* @internal | ||
*/ | ||
class RegisterTokenUsageTrackingPass implements CompilerPassInterface | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function process(ContainerBuilder $container) | ||
{ | ||
if (!$container->has('security.untracked_token_storage')) { | ||
return; | ||
} | ||
|
||
$processorAutoconfiguration = $container->registerForAutoconfiguration(ProcessorInterface::class); | ||
$processorAutoconfiguration->setBindings($processorAutoconfiguration->getBindings() + [ | ||
TokenStorageInterface::class => new BoundArgument(new Reference('security.untracked_token_storage'), false), | ||
]); | ||
|
||
if (!$container->has('session')) { | ||
$container->setAlias('security.token_storage', 'security.untracked_token_storage')->setPublic(true); | ||
} elseif ($container->hasDefinition('security.context_listener')) { | ||
$container->getDefinition('security.context_listener') | ||
->setArgument(6, [new Reference('security.token_storage'), 'enableUsageTracking']); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
...ymfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Security\Core\Authentication\Token\Storage; | ||
|
||
use Psr\Container\ContainerInterface; | ||
use Symfony\Component\HttpFoundation\Session\SessionInterface; | ||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; | ||
use Symfony\Contracts\Service\ServiceSubscriberInterface; | ||
|
||
/** | ||
* A token storage that increments the session usage index when the token is accessed. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
*/ | ||
final class UsageTrackingTokenStorage implements TokenStorageInterface, ServiceSubscriberInterface | ||
{ | ||
private $storage; | ||
private $sessionLocator; | ||
private $enableUsageTracking = false; | ||
|
||
public function __construct(TokenStorageInterface $storage, ContainerInterface $sessionLocator) | ||
{ | ||
$this->storage = $storage; | ||
$this->sessionLocator = $sessionLocator; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getToken(): ?TokenInterface | ||
{ | ||
if ($this->enableUsageTracking) { | ||
// increments the internal session usage index | ||
$this->sessionLocator->get('session')->getMetadataBag(); | ||
} | ||
|
||
return $this->storage->getToken(); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function setToken(TokenInterface $token = null): void | ||
{ | ||
$this->storage->setToken($token); | ||
} | ||
|
||
public function enableUsageTracking(): void | ||
{ | ||
$this->enableUsageTracking = true; | ||
} | ||
|
||
public function disableUsageTracking(): void | ||
{ | ||
$this->enableUsageTracking = false; | ||
} | ||
|
||
public static function getSubscribedServices(): array | ||
{ | ||
return [ | ||
'session' => SessionInterface::class, | ||
]; | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
...ponent/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Security\Core\Tests\Authentication\Token\Storage; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Psr\Container\ContainerInterface; | ||
use Symfony\Component\HttpFoundation\Session\SessionInterface; | ||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; | ||
use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage; | ||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; | ||
use Symfony\Contracts\Service\ServiceLocatorTrait; | ||
|
||
class UsageTrackingTokenStorageTest extends TestCase | ||
{ | ||
public function testGetSetToken() | ||
{ | ||
$sessionAccess = 0; | ||
$sessionLocator = new class(['session' => function () use (&$sessionAccess) { | ||
++$sessionAccess; | ||
|
||
$session = $this->createMock(SessionInterface::class); | ||
$session->expects($this->once()) | ||
->method('getMetadataBag'); | ||
|
||
return $session; | ||
}]) implements ContainerInterface { | ||
use ServiceLocatorTrait; | ||
}; | ||
$tokenStorage = new TokenStorage(); | ||
$trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $sessionLocator); | ||
|
||
$this->assertNull($trackingStorage->getToken()); | ||
$token = $this->getMockBuilder(TokenInterface::class)->getMock(); | ||
|
||
$trackingStorage->setToken($token); | ||
$this->assertSame($token, $trackingStorage->getToken()); | ||
$this->assertSame($token, $tokenStorage->getToken()); | ||
$this->assertSame(0, $sessionAccess); | ||
|
||
$trackingStorage->enableUsageTracking(); | ||
$this->assertSame($token, $trackingStorage->getToken()); | ||
$this->assertSame(1, $sessionAccess); | ||
|
||
$trackingStorage->disableUsageTracking(); | ||
$this->assertSame($token, $trackingStorage->getToken()); | ||
$this->assertSame(1, $sessionAccess); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.