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

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 <[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\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 <[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\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)