Skip to content

Commit ef1197d

Browse files
committed
feature #50946 [Routing][SecurityBundle] Add LogoutRouteLoader (MatTheCat)
This PR was merged into the 6.4 branch. Discussion ---------- [Routing][SecurityBundle] Add `LogoutRouteLoader` | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #50920 | License | MIT | Doc PR | symfony/symfony-docs#19000 #50920 is about avoiding for users to create logout routes. Given [we don’t want to allow bundles registering routes](#37786), I added a `LogoutRouteLoader` service bearing the `routing.route_loader` tag to be imported by the user. Such import could be added to the SecurityBundle recipe: ```yaml # config/routes/security.yaml logout: resource: security.route_loader.logout type: service ``` To invalidate routes when logout paths change, I stored them in a parameter so that the `ContainerParametersResourceChecker` can check the collection. Not sure if it’s okay or if a better way exists. Commits ------- 0a558d0 [SecurityBundle][Routing] Add `LogoutRouteLoader`
2 parents 7074da9 + 0a558d0 commit ef1197d

File tree

5 files changed

+127
-0
lines changed

5 files changed

+127
-0
lines changed

src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Allow an array of `pattern` in firewall configuration
99
* Add `$badges` argument to `Security::login`
1010
* Deprecate the `require_previous_session` config option. Setting it has no effect anymore
11+
* Add `LogoutRouteLoader`
1112

1213
6.3
1314
---

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
use Symfony\Component\PasswordHasher\Hasher\Pbkdf2PasswordHasher;
5050
use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher;
5151
use Symfony\Component\PasswordHasher\Hasher\SodiumPasswordHasher;
52+
use Symfony\Component\Routing\Loader\ContainerLoader;
5253
use Symfony\Component\Security\Core\Authorization\Strategy\AffirmativeStrategy;
5354
use Symfony\Component\Security\Core\Authorization\Strategy\ConsensusStrategy;
5455
use Symfony\Component\Security\Core\Authorization\Strategy\PriorityStrategy;
@@ -170,6 +171,13 @@ public function load(array $configs, ContainerBuilder $container)
170171
}
171172

172173
$this->createFirewalls($config, $container);
174+
175+
if ($container::willBeAvailable('symfony/routing', ContainerLoader::class, ['symfony/security-bundle'])) {
176+
$this->createLogoutUrisParameter($config['firewalls'] ?? [], $container);
177+
} else {
178+
$container->removeDefinition('security.route_loader.logout');
179+
}
180+
173181
$this->createAuthorization($config, $container);
174182
$this->createRoleHierarchy($config, $container);
175183

@@ -1095,4 +1103,20 @@ private function getSortedFactories(): array
10951103

10961104
return $this->sortedFactories;
10971105
}
1106+
1107+
private function createLogoutUrisParameter(array $firewallsConfig, ContainerBuilder $container): void
1108+
{
1109+
$logoutUris = [];
1110+
foreach ($firewallsConfig as $name => $config) {
1111+
if (!$logoutPath = $config['logout']['path'] ?? null) {
1112+
continue;
1113+
}
1114+
1115+
if ('/' === $logoutPath[0]) {
1116+
$logoutUris[$name] = $logoutPath;
1117+
}
1118+
}
1119+
1120+
$container->setParameter('security.logout_uris', $logoutUris);
1121+
}
10981122
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Bundle\SecurityBundle\CacheWarmer\ExpressionCacheWarmer;
1515
use Symfony\Bundle\SecurityBundle\EventListener\FirewallListener;
16+
use Symfony\Bundle\SecurityBundle\Routing\LogoutRouteLoader;
1617
use Symfony\Bundle\SecurityBundle\Security;
1718
use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
1819
use Symfony\Bundle\SecurityBundle\Security\FirewallContext;
@@ -229,6 +230,13 @@
229230
service('security.token_storage')->nullOnInvalid(),
230231
])
231232

233+
->set('security.route_loader.logout', LogoutRouteLoader::class)
234+
->args([
235+
'%security.logout_uris%',
236+
'security.logout_uris',
237+
])
238+
->tag('routing.route_loader')
239+
232240
// Provisioning
233241
->set('security.user.provider.missing', MissingUserProvider::class)
234242
->abstract()
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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 Symfony\Bundle\SecurityBundle\Routing;
13+
14+
use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
15+
use Symfony\Component\Routing\Route;
16+
use Symfony\Component\Routing\RouteCollection;
17+
18+
final class LogoutRouteLoader
19+
{
20+
/**
21+
* @param array<string, string> $logoutUris Logout URIs indexed by the corresponding firewall name
22+
* @param string $parameterName Name of the container parameter containing {@see $logoutUris}' value
23+
*/
24+
public function __construct(
25+
private readonly array $logoutUris,
26+
private readonly string $parameterName,
27+
) {
28+
}
29+
30+
public function __invoke(): RouteCollection
31+
{
32+
$collection = new RouteCollection();
33+
$collection->addResource(new ContainerParametersResource([$this->parameterName => $this->logoutUris]));
34+
35+
$routeNames = [];
36+
foreach ($this->logoutUris as $firewallName => $logoutPath) {
37+
$routeName = '_logout_'.$firewallName;
38+
39+
if (isset($routeNames[$logoutPath])) {
40+
$collection->addAlias($routeName, $routeNames[$logoutPath]);
41+
} else {
42+
$routeNames[$logoutPath] = $routeName;
43+
$collection->add($routeName, new Route($logoutPath));
44+
}
45+
}
46+
47+
return $collection;
48+
}
49+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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 Symfony\Bundle\SecurityBundle\Tests\Routing;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\SecurityBundle\Routing\LogoutRouteLoader;
16+
use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
17+
use Symfony\Component\Routing\Route;
18+
use Symfony\Component\Routing\RouteCollection;
19+
20+
class LogoutRouteLoaderTest extends TestCase
21+
{
22+
public function testLoad()
23+
{
24+
$logoutPaths = [
25+
'main' => '/logout',
26+
'admin' => '/logout',
27+
];
28+
29+
$loader = new LogoutRouteLoader($logoutPaths, 'parameterName');
30+
$collection = $loader();
31+
32+
self::assertInstanceOf(RouteCollection::class, $collection);
33+
self::assertCount(1, $collection);
34+
self::assertEquals(new Route('/logout'), $collection->get('_logout_main'));
35+
self::assertCount(1, $collection->getAliases());
36+
self::assertEquals('_logout_main', $collection->getAlias('_logout_admin')->getId());
37+
38+
$resources = $collection->getResources();
39+
self::assertCount(1, $resources);
40+
41+
$resource = reset($resources);
42+
self::assertInstanceOf(ContainerParametersResource::class, $resource);
43+
self::assertSame(['parameterName' => $logoutPaths], $resource->getParameters());
44+
}
45+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy