Skip to content

Commit 39bd706

Browse files
committed
feature #34548 Added access decision strategy to respect voter priority (aschempp)
This PR was squashed before being merged into the 5.1-dev branch (closes #34548). Discussion ---------- Added access decision strategy to respect voter priority | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | _will happily do if this is of interest/to be merged 🙃_ The priority-based access decision strategy will decide based on the first voter that does not abstain from the decision. Security voters can be registered with priority (`PriorityTaggedServiceTrait`), so a voter with higher priority can overrule other voters. In [Contao CMS](https://github.com/contao/contao), the core system should provide security voters that provide the "default permissions", but extensions/bundles can override almost anything and therefore need to be able to override the core decision. None of the existing strategies allow for something like that. /ping @chalasr @Toflar @leofeyer @ausi #SymfonyHackday Commits ------- 0b8028a Added access decision strategy to respect voter priority
2 parents 76c98fd + 0b8028a commit 39bd706

File tree

5 files changed

+76
-1
lines changed

5 files changed

+76
-1
lines changed

src/Symfony/Bundle/SecurityBundle/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+
5.1.0
5+
-----
6+
7+
* Added security configuration for priority-based access decision strategy
8+
49
5.0.0
510
-----
611

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public function getConfigTreeBuilder()
7676
->addDefaultsIfNotSet()
7777
->children()
7878
->enumNode('strategy')
79-
->values([AccessDecisionManager::STRATEGY_AFFIRMATIVE, AccessDecisionManager::STRATEGY_CONSENSUS, AccessDecisionManager::STRATEGY_UNANIMOUS])
79+
->values($this->getAccessDecisionStrategies())
8080
->end()
8181
->scalarNode('service')->end()
8282
->booleanNode('allow_if_all_abstain')->defaultFalse()->end()
@@ -392,4 +392,19 @@ private function addEncodersSection(ArrayNodeDefinition $rootNode)
392392
->end()
393393
;
394394
}
395+
396+
private function getAccessDecisionStrategies()
397+
{
398+
$strategies = [
399+
AccessDecisionManager::STRATEGY_AFFIRMATIVE,
400+
AccessDecisionManager::STRATEGY_CONSENSUS,
401+
AccessDecisionManager::STRATEGY_UNANIMOUS,
402+
];
403+
404+
if (\defined(AccessDecisionManager::class.'::STRATEGY_PRIORITY')) {
405+
$strategies[] = AccessDecisionManager::STRATEGY_PRIORITY;
406+
}
407+
408+
return $strategies;
409+
}
395410
}

src/Symfony/Component/Security/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+
5.1.0
5+
-----
6+
7+
* Added access decision strategy to override access decisions by voter service priority
8+
49
5.0.0
510
-----
611

src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class AccessDecisionManager implements AccessDecisionManagerInterface
2626
const STRATEGY_AFFIRMATIVE = 'affirmative';
2727
const STRATEGY_CONSENSUS = 'consensus';
2828
const STRATEGY_UNANIMOUS = 'unanimous';
29+
const STRATEGY_PRIORITY = 'priority';
2930

3031
private $voters;
3132
private $strategy;
@@ -166,4 +167,28 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje
166167

167168
return $this->allowIfAllAbstainDecisions;
168169
}
170+
171+
/**
172+
* Grant or deny access depending on the first voter that does not abstain.
173+
* The priority of voters can be used to overrule a decision.
174+
*
175+
* If all voters abstained from voting, the decision will be based on the
176+
* allowIfAllAbstainDecisions property value (defaults to false).
177+
*/
178+
private function decidePriority(TokenInterface $token, array $attributes, $object = null)
179+
{
180+
foreach ($this->voters as $voter) {
181+
$result = $voter->vote($token, $object, $attributes);
182+
183+
if (VoterInterface::ACCESS_GRANTED === $result) {
184+
return true;
185+
}
186+
187+
if (VoterInterface::ACCESS_DENIED === $result) {
188+
return false;
189+
}
190+
}
191+
192+
return $this->allowIfAllAbstainDecisions;
193+
}
169194
}

src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,31 @@ public function getStrategyTests()
6666

6767
[AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(0, 0, 2), false, true, false],
6868
[AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(0, 0, 2), true, true, true],
69+
70+
// priority
71+
[AccessDecisionManager::STRATEGY_PRIORITY, [
72+
$this->getVoter(VoterInterface::ACCESS_ABSTAIN),
73+
$this->getVoter(VoterInterface::ACCESS_GRANTED),
74+
$this->getVoter(VoterInterface::ACCESS_DENIED),
75+
$this->getVoter(VoterInterface::ACCESS_DENIED),
76+
], true, true, true],
77+
78+
[AccessDecisionManager::STRATEGY_PRIORITY, [
79+
$this->getVoter(VoterInterface::ACCESS_ABSTAIN),
80+
$this->getVoter(VoterInterface::ACCESS_DENIED),
81+
$this->getVoter(VoterInterface::ACCESS_GRANTED),
82+
$this->getVoter(VoterInterface::ACCESS_GRANTED),
83+
], true, true, false],
84+
85+
[AccessDecisionManager::STRATEGY_PRIORITY, [
86+
$this->getVoter(VoterInterface::ACCESS_ABSTAIN),
87+
$this->getVoter(VoterInterface::ACCESS_ABSTAIN),
88+
], false, true, false],
89+
90+
[AccessDecisionManager::STRATEGY_PRIORITY, [
91+
$this->getVoter(VoterInterface::ACCESS_ABSTAIN),
92+
$this->getVoter(VoterInterface::ACCESS_ABSTAIN),
93+
], true, true, true],
6994
];
7095
}
7196

0 commit comments

Comments
 (0)