Skip to content

Commit e05c25c

Browse files
committed
Merge branch '5.4' into 6.0
* 5.4: Be more precise about the required Composer version [Security] Implement ADM strategies as dedicated classes Bump Symfony version to 5.3.11 Update VERSION for 5.3.10 Update CHANGELOG for 5.3.10
2 parents b3d54fd + bfc8cf4 commit e05c25c

27 files changed

+791
-161
lines changed

UPGRADE-5.4.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,6 @@ Security
159159
}
160160
}
161161
```
162+
* Deprecate passing the strategy as string to `AccessDecisionManager`,
163+
pass an instance of `AccessDecisionStrategyInterface` instead
164+
* Flag `AccessDecisionManager` as `@final`

UPGRADE-6.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ Security
402402
}
403403
}
404404
```
405+
* `AccessDecisionManager` does not accept strings as strategy anymore,
406+
pass an instance of `AccessDecisionStrategyInterface` instead
405407

406408
SecurityBundle
407409
--------------

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class FrameworkExtension extends Extension
235235
public function load(array $configs, ContainerBuilder $container)
236236
{
237237
if (!class_exists(InstalledVersions::class)) {
238-
trigger_deprecation('symfony/framework-bundle', '5.4', 'Configuring Symfony without the Composer Runtime API is deprecated. Consider upgrading to Composer 2.');
238+
trigger_deprecation('symfony/framework-bundle', '5.4', 'Configuring Symfony without the Composer Runtime API is deprecated. Consider upgrading to Composer 2.1 or later.');
239239
}
240240

241241
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config'));

src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ CHANGELOG
2929
factories instead.
3030
* Deprecate the `always_authenticate_before_granting` option
3131
* Display the roles of the logged-in user in the Web Debug Toolbar
32+
* Add the `security.access_decision_manager.strategy_service` option
3233

3334

3435
5.3

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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1818
use Symfony\Component\Config\Definition\ConfigurationInterface;
1919
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
20-
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
2120
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
2221
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
2322

@@ -28,6 +27,15 @@
2827
*/
2928
class MainConfiguration implements ConfigurationInterface
3029
{
30+
/** @internal */
31+
public const STRATEGY_AFFIRMATIVE = 'affirmative';
32+
/** @internal */
33+
public const STRATEGY_CONSENSUS = 'consensus';
34+
/** @internal */
35+
public const STRATEGY_UNANIMOUS = 'unanimous';
36+
/** @internal */
37+
public const STRATEGY_PRIORITY = 'priority';
38+
3139
private $factories;
3240
private $userProviderFactories;
3341

@@ -55,14 +63,18 @@ public function getConfigTreeBuilder(): TreeBuilder
5563
return true;
5664
}
5765

58-
if (!isset($v['access_decision_manager']['strategy']) && !isset($v['access_decision_manager']['service'])) {
66+
if (!isset($v['access_decision_manager']['strategy'])
67+
&& !isset($v['access_decision_manager']['service'])
68+
&& !isset($v['access_decision_manager']['strategy_service'])
69+
&& !isset($v['access_decision_manager']['strategy-service'])
70+
) {
5971
return true;
6072
}
6173

6274
return false;
6375
})
6476
->then(function ($v) {
65-
$v['access_decision_manager']['strategy'] = AccessDecisionManager::STRATEGY_AFFIRMATIVE;
77+
$v['access_decision_manager']['strategy'] = self::STRATEGY_AFFIRMATIVE;
6678

6779
return $v;
6880
})
@@ -83,13 +95,22 @@ public function getConfigTreeBuilder(): TreeBuilder
8395
->values($this->getAccessDecisionStrategies())
8496
->end()
8597
->scalarNode('service')->end()
98+
->scalarNode('strategy_service')->end()
8699
->booleanNode('allow_if_all_abstain')->defaultFalse()->end()
87100
->booleanNode('allow_if_equal_granted_denied')->defaultTrue()->end()
88101
->end()
89102
->validate()
90-
->ifTrue(function ($v) { return isset($v['strategy']) && isset($v['service']); })
103+
->ifTrue(function ($v) { return isset($v['strategy'], $v['service']); })
91104
->thenInvalid('"strategy" and "service" cannot be used together.')
92105
->end()
106+
->validate()
107+
->ifTrue(function ($v) { return isset($v['strategy'], $v['strategy_service']); })
108+
->thenInvalid('"strategy" and "strategy_service" cannot be used together.')
109+
->end()
110+
->validate()
111+
->ifTrue(function ($v) { return isset($v['service'], $v['strategy_service']); })
112+
->thenInvalid('"service" and "strategy_service" cannot be used together.')
113+
->end()
93114
->end()
94115
->end()
95116
;
@@ -416,18 +437,13 @@ private function addPasswordHashersSection(ArrayNodeDefinition $rootNode)
416437
->end();
417438
}
418439

419-
private function getAccessDecisionStrategies()
440+
private function getAccessDecisionStrategies(): array
420441
{
421-
$strategies = [
422-
AccessDecisionManager::STRATEGY_AFFIRMATIVE,
423-
AccessDecisionManager::STRATEGY_CONSENSUS,
424-
AccessDecisionManager::STRATEGY_UNANIMOUS,
442+
return [
443+
self::STRATEGY_AFFIRMATIVE,
444+
self::STRATEGY_CONSENSUS,
445+
self::STRATEGY_UNANIMOUS,
446+
self::STRATEGY_PRIORITY,
425447
];
426-
427-
if (\defined(AccessDecisionManager::class.'::STRATEGY_PRIORITY')) {
428-
$strategies[] = AccessDecisionManager::STRATEGY_PRIORITY;
429-
}
430-
431-
return $strategies;
432448
}
433449
}

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
use Symfony\Component\PasswordHasher\Hasher\Pbkdf2PasswordHasher;
3939
use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher;
4040
use Symfony\Component\PasswordHasher\Hasher\SodiumPasswordHasher;
41+
use Symfony\Component\Security\Core\Authorization\Strategy\AffirmativeStrategy;
42+
use Symfony\Component\Security\Core\Authorization\Strategy\ConsensusStrategy;
43+
use Symfony\Component\Security\Core\Authorization\Strategy\PriorityStrategy;
44+
use Symfony\Component\Security\Core\Authorization\Strategy\UnanimousStrategy;
4145
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
4246
use Symfony\Component\Security\Core\User\ChainUserProvider;
4347
use Symfony\Component\Security\Core\User\UserProviderInterface;
@@ -73,7 +77,7 @@ public function prepend(ContainerBuilder $container)
7377
public function load(array $configs, ContainerBuilder $container)
7478
{
7579
if (!class_exists(InstalledVersions::class)) {
76-
trigger_deprecation('symfony/security-bundle', '5.4', 'Configuring Symfony without the Composer Runtime API is deprecated. Consider upgrading to Composer 2.');
80+
trigger_deprecation('symfony/security-bundle', '5.4', 'Configuring Symfony without the Composer Runtime API is deprecated. Consider upgrading to Composer 2.1 or later.');
7781
}
7882

7983
if (!array_filter($configs)) {
@@ -119,12 +123,18 @@ public function load(array $configs, ContainerBuilder $container)
119123

120124
if (isset($config['access_decision_manager']['service'])) {
121125
$container->setAlias('security.access.decision_manager', $config['access_decision_manager']['service']);
126+
} elseif (isset($config['access_decision_manager']['strategy_service'])) {
127+
$container
128+
->getDefinition('security.access.decision_manager')
129+
->addArgument(new Reference($config['access_decision_manager']['strategy_service']));
122130
} else {
123131
$container
124132
->getDefinition('security.access.decision_manager')
125-
->addArgument($config['access_decision_manager']['strategy'])
126-
->addArgument($config['access_decision_manager']['allow_if_all_abstain'])
127-
->addArgument($config['access_decision_manager']['allow_if_equal_granted_denied']);
133+
->addArgument($this->createStrategyDefinition(
134+
$config['access_decision_manager']['strategy'],
135+
$config['access_decision_manager']['allow_if_all_abstain'],
136+
$config['access_decision_manager']['allow_if_equal_granted_denied']
137+
));
128138
}
129139

130140
$container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']);
@@ -156,6 +166,25 @@ public function load(array $configs, ContainerBuilder $container)
156166
$container->getDefinition('security.authorization_checker')->setArgument(3, false);
157167
}
158168

169+
/**
170+
* @throws \InvalidArgumentException if the $strategy is invalid
171+
*/
172+
private function createStrategyDefinition(string $strategy, bool $allowIfAllAbstainDecisions, bool $allowIfEqualGrantedDeniedDecisions): Definition
173+
{
174+
switch ($strategy) {
175+
case MainConfiguration::STRATEGY_AFFIRMATIVE:
176+
return new Definition(AffirmativeStrategy::class, [$allowIfAllAbstainDecisions]);
177+
case MainConfiguration::STRATEGY_CONSENSUS:
178+
return new Definition(ConsensusStrategy::class, [$allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions]);
179+
case MainConfiguration::STRATEGY_UNANIMOUS:
180+
return new Definition(UnanimousStrategy::class, [$allowIfAllAbstainDecisions]);
181+
case MainConfiguration::STRATEGY_PRIORITY:
182+
return new Definition(PriorityStrategy::class, [$allowIfAllAbstainDecisions]);
183+
}
184+
185+
throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy));
186+
}
187+
159188
private function createRoleHierarchy(array $config, ContainerBuilder $container)
160189
{
161190
if (!isset($config['role_hierarchy']) || 0 === \count($config['role_hierarchy'])) {

src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<xsd:complexType name="access_decision_manager">
5656
<xsd:attribute name="strategy" type="access_decision_manager_strategy" />
5757
<xsd:attribute name="service" type="xsd:string" />
58+
<xsd:attribute name="strategy-service" type="xsd:string" />
5859
<xsd:attribute name="allow-if-all-abstain" type="xsd:boolean" />
5960
<xsd:attribute name="allow-if-equal-granted-denied" type="xsd:boolean" />
6061
</xsd:complexType>

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1818
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1919
use Symfony\Component\DependencyInjection\ContainerBuilder;
20+
use Symfony\Component\DependencyInjection\Definition;
2021
use Symfony\Component\DependencyInjection\Reference;
2122
use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
2223
use Symfony\Component\PasswordHasher\Hasher\Pbkdf2PasswordHasher;
2324
use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher;
2425
use Symfony\Component\PasswordHasher\Hasher\SodiumPasswordHasher;
2526
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
27+
use Symfony\Component\Security\Core\Authorization\Strategy\AffirmativeStrategy;
2628
use Symfony\Component\Security\Http\Authentication\AuthenticatorManager;
2729
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
2830
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
@@ -605,7 +607,7 @@ public function testDefaultAccessDecisionManagerStrategyIsAffirmative()
605607
{
606608
$container = $this->getContainer('access_decision_manager_default_strategy');
607609

608-
$this->assertSame(AccessDecisionManager::STRATEGY_AFFIRMATIVE, $container->getDefinition('security.access.decision_manager')->getArgument(1), 'Default vote strategy is affirmative');
610+
$this->assertEquals((new Definition(AffirmativeStrategy::class, [false])), $container->getDefinition('security.access.decision_manager')->getArgument(1), 'Default vote strategy is affirmative');
609611
}
610612

611613
public function testCustomAccessDecisionManagerService()
@@ -628,9 +630,17 @@ public function testAccessDecisionManagerOptionsAreNotOverriddenByImplicitStrate
628630

629631
$accessDecisionManagerDefinition = $container->getDefinition('security.access.decision_manager');
630632

631-
$this->assertSame(AccessDecisionManager::STRATEGY_AFFIRMATIVE, $accessDecisionManagerDefinition->getArgument(1));
632-
$this->assertTrue($accessDecisionManagerDefinition->getArgument(2));
633-
$this->assertFalse($accessDecisionManagerDefinition->getArgument(3));
633+
$this->assertEquals((new Definition(AffirmativeStrategy::class, [true])), $accessDecisionManagerDefinition->getArgument(1));
634+
}
635+
636+
public function testAccessDecisionManagerWithStrategyService()
637+
{
638+
$container = $this->getContainer('access_decision_manager_strategy_service');
639+
640+
$accessDecisionManagerDefinition = $container->getDefinition('security.access.decision_manager');
641+
642+
$this->assertEquals(AccessDecisionManager::class, $accessDecisionManagerDefinition->getClass());
643+
$this->assertEquals(new Reference('app.custom_access_decision_strategy'), $accessDecisionManagerDefinition->getArgument(1));
634644
}
635645

636646
public function testFirewallUndefinedUserProvider()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
$container->loadFromExtension('security', [
4+
'enable_authenticator_manager' => true,
5+
'access_decision_manager' => [
6+
'strategy_service' => 'app.custom_access_decision_strategy',
7+
],
8+
'providers' => [
9+
'default' => [
10+
'memory' => [
11+
'users' => [
12+
'foo' => ['password' => 'foo', 'roles' => 'ROLE_USER'],
13+
],
14+
],
15+
],
16+
],
17+
'firewalls' => [
18+
'simple' => ['pattern' => '/login', 'security' => false],
19+
],
20+
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<srv:container xmlns="http://symfony.com/schema/dic/security"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:srv="http://symfony.com/schema/dic/services"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services
6+
https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/security
8+
https://symfony.com/schema/dic/security/security-1.0.xsd">
9+
10+
<config enable-authenticator-manager="true">
11+
<access-decision-manager strategy-service="app.custom_access_decision_strategy" />
12+
13+
<provider name="default">
14+
<memory>
15+
<user identifier="foo" password="foo" roles="ROLE_USER" />
16+
</memory>
17+
</provider>
18+
19+
<firewall name="simple" pattern="/login" security="false" />
20+
</config>
21+
</srv:container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
security:
2+
enable_authenticator_manager: true
3+
access_decision_manager:
4+
strategy_service: app.custom_access_decision_strategy
5+
providers:
6+
default:
7+
memory:
8+
users:
9+
foo: { password: foo, roles: ROLE_USER }
10+
firewalls:
11+
simple: { pattern: /login, security: false }

src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class TwigExtension extends Extension
3737
public function load(array $configs, ContainerBuilder $container)
3838
{
3939
if (!class_exists(InstalledVersions::class)) {
40-
trigger_deprecation('symfony/twig-bundle', '5.4', 'Configuring Symfony without the Composer Runtime API is deprecated. Consider upgrading to Composer 2.');
40+
trigger_deprecation('symfony/twig-bundle', '5.4', 'Configuring Symfony without the Composer Runtime API is deprecated. Consider upgrading to Composer 2.1 or later.');
4141
}
4242

4343
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));

0 commit comments

Comments
 (0)