Skip to content

Commit b79f4f8

Browse files
committed
Add a ChainUserChecker to allow calling multiple user checkers for a firewall
1 parent 68f5b3c commit b79f4f8

File tree

4 files changed

+113
-0
lines changed

4 files changed

+113
-0
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\DependencyInjection\Alias;
2323
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
2424
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
25+
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
2526
use Symfony\Component\DependencyInjection\ChildDefinition;
2627
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
2728
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -44,6 +45,7 @@
4445
use Symfony\Component\Security\Core\Authorization\Strategy\PriorityStrategy;
4546
use Symfony\Component\Security\Core\Authorization\Strategy\UnanimousStrategy;
4647
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
48+
use Symfony\Component\Security\Core\User\ChainUserChecker;
4749
use Symfony\Component\Security\Core\User\ChainUserProvider;
4850
use Symfony\Component\Security\Core\User\UserCheckerInterface;
4951
use Symfony\Component\Security\Core\User\UserProviderInterface;
@@ -363,6 +365,12 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
363365
$container->register($firewallEventDispatcherId, EventDispatcher::class)
364366
->addTag('event_dispatcher.dispatcher', ['name' => $firewallEventDispatcherId]);
365367

368+
// Register Firewall-specific chained user checker
369+
if (class_exists(ChainUserChecker::class)) {
370+
$container->register('security.user_checker.chain.'.$id, ChainUserChecker::class)
371+
->addArgument(new TaggedIterator('security.user_checker.'.$id));
372+
}
373+
366374
// Register listeners
367375
$listeners = [];
368376
$listenerKeys = [];

src/Symfony/Component/Security/Core/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
6.1
5+
---
6+
7+
* Add a `ChainUserChecker` to allow calling multiple user checkers for a firewall
8+
49
6.0
510
---
611

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\Component\Security\Core\Tests\User;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Security\Core\User\ChainUserChecker;
16+
use Symfony\Component\Security\Core\User\UserCheckerInterface;
17+
use Symfony\Component\Security\Core\User\UserInterface;
18+
19+
final class ChainUserCheckerTest extends TestCase
20+
{
21+
public function testForwardsPreAuthToAllUserCheckers()
22+
{
23+
$user = $this->createMock(UserInterface::class);
24+
25+
$checker1 = $this->createMock(UserCheckerInterface::class);
26+
$checker1->expects($this->once())
27+
->method('checkPreAuth')
28+
->with($user);
29+
30+
$checker2 = $this->createMock(UserCheckerInterface::class);
31+
$checker2->expects($this->once())
32+
->method('checkPreAuth')
33+
->with($user);
34+
35+
$checker3 = $this->createMock(UserCheckerInterface::class);
36+
$checker3->expects($this->once())
37+
->method('checkPreAuth')
38+
->with($user);
39+
40+
(new ChainUserChecker([$checker1, $checker2, $checker3]))->checkPreAuth($user);
41+
}
42+
43+
public function testForwardsPostAuthToAllUserCheckers()
44+
{
45+
$user = $this->createMock(UserInterface::class);
46+
47+
$checker1 = $this->createMock(UserCheckerInterface::class);
48+
$checker1->expects($this->once())
49+
->method('checkPostAuth')
50+
->with($user);
51+
52+
$checker2 = $this->createMock(UserCheckerInterface::class);
53+
$checker2->expects($this->once())
54+
->method('checkPostAuth')
55+
->with($user);
56+
57+
$checker3 = $this->createMock(UserCheckerInterface::class);
58+
$checker3->expects($this->once())
59+
->method('checkPostAuth')
60+
->with($user);
61+
62+
(new ChainUserChecker([$checker1, $checker2, $checker3]))->checkPostAuth($user);
63+
}
64+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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\Component\Security\Core\User;
13+
14+
final class ChainUserChecker implements UserCheckerInterface
15+
{
16+
/**
17+
* @param iterable<UserCheckerInterface> $checkers
18+
*/
19+
public function __construct(private readonly iterable $checkers)
20+
{
21+
}
22+
23+
public function checkPreAuth(UserInterface $user): void
24+
{
25+
foreach ($this->checkers as $checker) {
26+
$checker->checkPreAuth($user);
27+
}
28+
}
29+
30+
public function checkPostAuth(UserInterface $user): void
31+
{
32+
foreach ($this->checkers as $checker) {
33+
$checker->checkPostAuth($user);
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)