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

Skip to content

Commit db59d0b

Browse files
committed
feature #46064 [Security] Add a ChainUserChecker to allow calling multiple user checkers for a firewall (mbabker)
This PR was merged into the 6.2 branch. Discussion ---------- [Security] Add a `ChainUserChecker` to allow calling multiple user checkers for a firewall | Q | A | ------------- | --- | Branch? | 6.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | N/A | License | MIT | Doc PR | TODO I had a case in one of my apps where I have some shared logic for user checkers across multiple firewalls (API and traditional web login) while also needing extra logic for the API. Instead of duplicating the checker for each firewall or using a decorator, I thought a chained user checker would be nice to have and that it might be useful to users of the framework. This will add a `ChainUserChecker` class to the `security-core` component, and the SecurityBundle will create a `security.user_checker.chain.<firewall>` service for each firewall. User checkers can then be tagged with a `security.user_checker.<firewall>` tag for each firewall the checker applies to, and users would set this chain service as the user checker for their firewall. ```php <?php namespace App\Security\User; use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; use Symfony\Component\Security\Core\User\UserCheckerInterface; #[Autoconfigure(tags: [['security.user_checker.main' => ['priority' => 10]]])] #[Autoconfigure(tags: [['security.user_checker.api' => ['priority' => 10]]])] final class DisabledAccountUserChecker implements UserCheckerInterface {} #[Autoconfigure(tags: [['security.user_checker.api' => ['priority' => 5]]])] final class ApiAccessAllowedUserChecker implements UserCheckerInterface {} ``` Commits ------- 0a1ad4b Add a ChainUserChecker to allow calling multiple user checkers for a firewall
2 parents 6f8c99c + 0a1ad4b commit db59d0b

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

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

Lines changed: 6 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\Argument\TaggedIteratorArgument;
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;
@@ -385,6 +387,10 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
385387
]))
386388
;
387389

390+
// Register Firewall-specific chained user checker
391+
$container->register('security.user_checker.chain.'.$id, ChainUserChecker::class)
392+
->addArgument(new TaggedIteratorArgument('security.user_checker.'.$id));
393+
388394
// Register listeners
389395
$listeners = [];
390396
$listenerKeys = [];

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead
88
* Change the signature of `TokenStorageInterface::setToken()` to `setToken(?TokenInterface $token)`
99
* Deprecate calling `TokenStorage::setToken()` without arguments
10+
* Add a `ChainUserChecker` to allow calling multiple user checkers for a firewall
1011

1112
6.0
1213
---
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 <[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\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 <[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\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)