Skip to content

[FrameworkBundle][HttpFoundation][Security] Deprecate service "session" #38616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions UPGRADE-5.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ Form
* Deprecated passing an array as the second argument of the `RadioListMapper::mapDataToForms()` method, pass `\Traversable` instead
* Deprecated passing an array as the first argument of the `RadioListMapper::mapFormsToData()` method, pass `\Traversable` instead

FrameworkBundle
---------------

* Deprecate the `session` service and the `SessionInterface` alias, use the `\Symfony\Component\HttpFoundation\Request::getSession()` or the new `\Symfony\Component\HttpFoundation\RequestStack::getSession()` methods instead

HttpFoundation
--------------

Expand Down
4 changes: 4 additions & 0 deletions UPGRADE-6.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Form
FrameworkBundle
---------------

* Remove the `session` service and the `SessionInterface` alias, use the `\Symfony\Component\HttpFoundation\Request::getSession()` or the new `\Symfony\Component\HttpFoundation\RequestStack::getSession()` methods instead
* `MicroKernelTrait::configureRoutes()` is now always called with a `RoutingConfigurator`
* The "framework.router.utf8" configuration option defaults to `true`
* Removed `session.attribute_bag` service and `session.flash_bag` service.
Expand Down Expand Up @@ -165,6 +166,9 @@ Routing
Security
--------

* Drop support for `SessionInterface $session` as constructor argument of `SessionTokenStorage`, inject a `\Symfony\Component\HttpFoundation\RequestStack $requestStack` instead
* Drop support for `session` provided by the ServiceLocator injected in `UsageTrackingTokenStorage`, provide a `request_stack` service instead
* Make `SessionTokenStorage` throw a `SessionNotFoundException` when called outside a request context
* Removed `ROLE_PREVIOUS_ADMIN` role in favor of `IS_IMPERSONATOR` attribute
* Removed `LogoutSuccessHandlerInterface` and `LogoutHandlerInterface`, register a listener on the `LogoutEvent` event instead.
* Removed `DefaultLogoutSuccessHandler` in favor of `DefaultLogoutListener`.
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
5.3
---

* Deprecate the `session` service and the `SessionInterface` alias, use the `Request::getSession()` or the new `RequestStack::getSession()` methods instead
* Added `AbstractController::renderForm()` to render a form and set the appropriate HTTP status code
* Added support for configuring PHP error level to log levels
* Added the `dispatcher` option to `debug:event-dispatcher`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -200,11 +201,11 @@ protected function file($file, string $fileName = null, string $disposition = Re
*/
protected function addFlash(string $type, $message): void
{
if (!$this->container->has('session')) {
throw new \LogicException('You can not use the addFlash method if sessions are disabled. Enable them in "config/packages/framework.yaml".');
try {
$this->container->get('request_stack')->getSession()->getFlashBag()->add($type, $message);
} catch (SessionNotFoundException $e) {
throw new \LogicException('You can not use the addFlash method if sessions are disabled. Enable them in "config/packages/framework.yaml".', 0, $e);
}

$this->container->get('session')->getFlashBag()->add($type, $message);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,41 @@ class SessionPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('session')) {
if (!$container->has('session.storage')) {
return;
}

// BC layer: Make "session" an alias of ".session.do-not-use" when not overriden by the user
if (!$container->has('session')) {
$alias = $container->setAlias('session', '.session.do-not-use');
$alias->setDeprecated('symfony/framework-bundle', '5.3', 'The "%alias_id%" service is deprecated, use "$requestStack->getSession()" instead.');

return;
}

if ($container->hasDefinition('session')) {
$definition = $container->getDefinition('session');
$definition->setDeprecated('symfony/framework-bundle', '5.3', 'The "%service_id%" service is deprecated, use "$requestStack->getSession()" instead.');
} else {
$alias = $container->getAlias('session');
$alias->setDeprecated('symfony/framework-bundle', '5.3', 'The "%alias_id%" alias is deprecated, use "$requestStack->getSession()" instead.');
$definition = $container->findDefinition('session');
}

// Convert internal service `.session.do-not-use` into alias of `session`.
$container->setAlias('.session.do-not-use', 'session');

$bags = [
'session.flash_bag' => $container->hasDefinition('session.flash_bag') ? $container->getDefinition('session.flash_bag') : null,
'session.attribute_bag' => $container->hasDefinition('session.attribute_bag') ? $container->getDefinition('session.attribute_bag') : null,
];

foreach ($container->getDefinition('session')->getArguments() as $v) {
foreach ($definition->getArguments() as $v) {
if (!$v instanceof Reference || !isset($bags[$bag = (string) $v]) || !\is_array($factory = $bags[$bag]->getFactory())) {
continue;
}

if ([0, 1] !== array_keys($factory) || !$factory[0] instanceof Reference || 'session' !== (string) $factory[0]) {
if ([0, 1] !== array_keys($factory) || !$factory[0] instanceof Reference || !\in_array((string) $factory[0], ['session', '.session.do-not-use'], true)) {
continue;
}

Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\HttpKernelBrowser;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile;
Expand Down Expand Up @@ -122,7 +123,7 @@ public function loginUser($user, string $firewallContext = 'main'): self

$token = new TestBrowserToken($user->getRoles(), $user);
$token->setAuthenticated(true);
$session = $this->getContainer()->get('session');
$session = new Session($this->getContainer()->get('test.service_container')->get('session.storage'));
$session->set('_security_'.$firewallContext, serialize($token));
$session->save();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
->alias(TokenGeneratorInterface::class, 'security.csrf.token_generator')

->set('security.csrf.token_storage', SessionTokenStorage::class)
->args([service('session')])
->args([service('request_stack')])

->alias(TokenStorageInterface::class, 'security.csrf.token_storage')

Expand Down
17 changes: 10 additions & 7 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/session.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Bundle\FrameworkBundle\Session\DeprecatedSessionFactory;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
Expand All @@ -33,15 +34,17 @@
$container->parameters()->set('session.metadata.storage_key', '_sf2_meta');

$container->services()
->set('session', Session::class)
->public()
->set('.session.do-not-use', Session::class) // to be removed in 6.0
->args([
service('session.storage'),
null, // AttributeBagInterface
null, // FlashBagInterface
[service('session_listener'), 'onSessionUsage'],
])
->alias(SessionInterface::class, 'session')
->set('.session.deprecated', SessionInterface::class) // to be removed in 6.0
->factory([inline_service(DeprecatedSessionFactory::class)->args([service('request_stack')]), 'getSession'])
->alias(SessionInterface::class, '.session.do-not-use')
->deprecate('symfony/framework-bundle', '5.3', 'The "%alias_id%" alias is deprecated, use "$requestStack->getSession()" instead.')
->alias(SessionStorageInterface::class, 'session.storage')
->alias(\SessionHandlerInterface::class, 'session.handler')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want to remove all session.* services?
This could be doable via a session-factory service instead.
Could be for a later PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can take care of this in another PR


Expand All @@ -65,12 +68,12 @@
])

->set('session.flash_bag', FlashBag::class)
->factory([service('session'), 'getFlashBag'])
->factory([service('.session.do-not-use'), 'getFlashBag'])
->deprecate('symfony/framework-bundle', '5.1', 'The "%service_id%" service is deprecated, use "$session->getFlashBag()" instead.')
->alias(FlashBagInterface::class, 'session.flash_bag')

->set('session.attribute_bag', AttributeBag::class)
->factory([service('session'), 'getBag'])
->factory([service('.session.do-not-use'), 'getBag'])
->args(['attributes'])
->deprecate('symfony/framework-bundle', '5.1', 'The "%service_id%" service is deprecated, use "$session->getAttributeBag()" instead.')

Expand All @@ -94,8 +97,8 @@
->set('session_listener', SessionListener::class)
->args([
service_locator([
'session' => service('session')->ignoreOnInvalid(),
'initialized_session' => service('session')->ignoreOnUninitialized(),
'session' => service('.session.do-not-use')->ignoreOnInvalid(),
'initialized_session' => service('.session.do-not-use')->ignoreOnUninitialized(),
'logger' => service('logger')->ignoreOnInvalid(),
'session_collector' => service('data_collector.request.session_collector')->ignoreOnInvalid(),
]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
->set('test.session.listener', TestSessionListener::class)
->args([
service_locator([
'session' => service('session')->ignoreOnInvalid(),
'session' => service('.session.do-not-use')->ignoreOnInvalid(),
]),
])
->tag('kernel.event_subscriber')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?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\FrameworkBundle\Session;

use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

/**
* Provides session and trigger deprecation.
*
* Used by service that should trigger deprecation when accessed by the user.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*
* @internal to be removed in 6.0
*/
class DeprecatedSessionFactory
{
private $requestStack;

public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}

public function getSession(): ?SessionInterface
{
trigger_deprecation('symfony/framework-bundle', '5.3', 'The "session" service is deprecated, use "$requestStack->getSession()" instead.');

try {
return $this->requestStack->getSession();
} catch (SessionNotFoundException $e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -497,8 +497,14 @@ public function testAddFlash()
$session = $this->createMock(Session::class);
$session->expects($this->once())->method('getFlashBag')->willReturn($flashBag);

$request = new Request();
$request->setSession($session);
$requestStack = new RequestStack();
$requestStack->push($request);

$container = new Container();
$container->set('session', $session);
$container->set('request_stack', $requestStack);

$controller = $this->createController();
$controller->setContainer($container);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,77 @@
class SessionPassTest extends TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container
->register('session.storage'); // marker service
$container
->register('.session.do-not-use');

(new SessionPass())->process($container);

$this->assertTrue($container->hasAlias('session'));
$this->assertSame($container->findDefinition('session'), $container->getDefinition('.session.do-not-use'));
$this->assertTrue($container->getAlias('session')->isDeprecated());
}

public function testProcessUserDefinedSession()
{
$arguments = [
new Reference('session.flash_bag'),
new Reference('session.attribute_bag'),
];
$container = new ContainerBuilder();
$container
->register('session.storage'); // marker service
$container
->register('session')
->setArguments($arguments);
$container
->register('session.flash_bag')
->setFactory([new Reference('session'), 'getFlashBag']);
->setFactory([new Reference('.session.do-not-use'), 'getFlashBag']);
$container
->register('session.attribute_bag')
->setFactory([new Reference('session'), 'getAttributeBag']);
->setFactory([new Reference('.session.do-not-use'), 'getAttributeBag']);

(new SessionPass())->process($container);

$this->assertSame($arguments, $container->getDefinition('session')->getArguments());
$this->assertNull($container->getDefinition('session.flash_bag')->getFactory());
$this->assertNull($container->getDefinition('session.attribute_bag')->getFactory());
$this->assertTrue($container->hasAlias('.session.do-not-use'));
$this->assertSame($container->getDefinition('session'), $container->findDefinition('.session.do-not-use'));
$this->assertTrue($container->getDefinition('session')->isDeprecated());
}

public function testProcessUserDefinedAlias()
{
$arguments = [
new Reference('session.flash_bag'),
new Reference('session.attribute_bag'),
];
$container = new ContainerBuilder();
$container
->register('session.storage'); // marker service
$container
->register('trueSession')
->setArguments($arguments);
$container
->setAlias('session', 'trueSession');
$container
->register('session.flash_bag')
->setFactory([new Reference('.session.do-not-use'), 'getFlashBag']);
$container
->register('session.attribute_bag')
->setFactory([new Reference('.session.do-not-use'), 'getAttributeBag']);

(new SessionPass())->process($container);

$this->assertSame($arguments, $container->findDefinition('session')->getArguments());
$this->assertNull($container->getDefinition('session.flash_bag')->getFactory());
$this->assertNull($container->getDefinition('session.attribute_bag')->getFactory());
$this->assertTrue($container->hasAlias('.session.do-not-use'));
$this->assertSame($container->findDefinition('session'), $container->findDefinition('.session.do-not-use'));
$this->assertTrue($container->getAlias('session')->isDeprecated());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\RetryableHttpClient;
use Symfony\Component\HttpClient\ScopingHttpClient;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass;
use Symfony\Component\Messenger\Transport\TransportFactory;
use Symfony\Component\PropertyAccess\PropertyAccessor;
Expand Down Expand Up @@ -541,7 +542,7 @@ public function testSession()
{
$container = $this->createContainerFromFile('full');

$this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml');
$this->assertTrue($container->hasAlias(SessionInterface::class), '->registerSessionConfiguration() loads session.xml');
$this->assertEquals('fr', $container->getParameter('kernel.default_locale'));
$this->assertEquals('session.storage.native', (string) $container->getAlias('session.storage'));
$this->assertEquals('session.handler.native_file', (string) $container->getAlias('session.handler'));
Expand All @@ -567,7 +568,7 @@ public function testNullSessionHandler()
{
$container = $this->createContainerFromFile('session');

$this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml');
$this->assertTrue($container->hasAlias(SessionInterface::class), '->registerSessionConfiguration() loads session.xml');
$this->assertNull($container->getDefinition('session.storage.native')->getArgument(1));
$this->assertNull($container->getDefinition('session.storage.php_bridge')->getArgument(0));
$this->assertSame('session.handler.native_file', (string) $container->getAlias('session.handler'));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class DeprecatedSessionController extends AbstractController
{
public function triggerAction()
{
$this->get('session');

return new Response('done');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ injected_flashbag_session_setflash:
path: injected_flashbag/session_setflash/{message}
defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\InjectedFlashbagSessionController::setFlashAction}

deprecated_session_setflash:
path: /deprecated_session/trigger
defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\DeprecatedSessionController::triggerAction}

session_showflash:
path: /session_showflash
defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SessionController::showFlashAction }
Expand Down
Loading