diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 7f0fe56f17665..e4ae2e2614080 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.1.0 +----- + + * Added security configuration for priority-based access decision strategy + 5.0.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index ba55fe195c76b..96f667f9c210b 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -76,7 +76,7 @@ public function getConfigTreeBuilder() ->addDefaultsIfNotSet() ->children() ->enumNode('strategy') - ->values([AccessDecisionManager::STRATEGY_AFFIRMATIVE, AccessDecisionManager::STRATEGY_CONSENSUS, AccessDecisionManager::STRATEGY_UNANIMOUS]) + ->values($this->getAccessDecisionStrategies()) ->end() ->scalarNode('service')->end() ->booleanNode('allow_if_all_abstain')->defaultFalse()->end() @@ -386,4 +386,19 @@ private function addEncodersSection(ArrayNodeDefinition $rootNode) ->end() ; } + + private function getAccessDecisionStrategies() + { + $strategies = [ + AccessDecisionManager::STRATEGY_AFFIRMATIVE, + AccessDecisionManager::STRATEGY_CONSENSUS, + AccessDecisionManager::STRATEGY_UNANIMOUS, + ]; + + if (\defined(AccessDecisionManager::class.'::STRATEGY_PRIORITY')) { + $strategies[] = AccessDecisionManager::STRATEGY_PRIORITY; + } + + return $strategies; + } } diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index d5c28dc67ab88..69459712479db 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.1.0 +----- + + * Added access decision strategy to override access decisions by voter service priority + 5.0.0 ----- diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php index f4c567432cafa..cfbd463a99e5b 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php @@ -26,6 +26,7 @@ class AccessDecisionManager implements AccessDecisionManagerInterface const STRATEGY_AFFIRMATIVE = 'affirmative'; const STRATEGY_CONSENSUS = 'consensus'; const STRATEGY_UNANIMOUS = 'unanimous'; + const STRATEGY_PRIORITY = 'priority'; private $voters; private $strategy; @@ -181,4 +182,28 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje return $this->allowIfAllAbstainDecisions; } + + /** + * Grant or deny access depending on the first voter that does not abstain. + * The priority of voters can be used to overrule a decision. + * + * If all voters abstained from voting, the decision will be based on the + * allowIfAllAbstainDecisions property value (defaults to false). + */ + private function decidePriority(TokenInterface $token, array $attributes, $object = null) + { + foreach ($this->voters as $voter) { + $result = $voter->vote($token, $object, $attributes); + + if (VoterInterface::ACCESS_GRANTED === $result) { + return true; + } + + if (VoterInterface::ACCESS_DENIED === $result) { + return false; + } + } + + return $this->allowIfAllAbstainDecisions; + } } diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php index 04875dde14fe3..0e3c62c5bd861 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -66,6 +66,31 @@ public function getStrategyTests() [AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(0, 0, 2), false, true, false], [AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(0, 0, 2), true, true, true], + + // priority + [AccessDecisionManager::STRATEGY_PRIORITY, [ + $this->getVoter(VoterInterface::ACCESS_ABSTAIN), + $this->getVoter(VoterInterface::ACCESS_GRANTED), + $this->getVoter(VoterInterface::ACCESS_DENIED), + $this->getVoter(VoterInterface::ACCESS_DENIED), + ], true, true, true], + + [AccessDecisionManager::STRATEGY_PRIORITY, [ + $this->getVoter(VoterInterface::ACCESS_ABSTAIN), + $this->getVoter(VoterInterface::ACCESS_DENIED), + $this->getVoter(VoterInterface::ACCESS_GRANTED), + $this->getVoter(VoterInterface::ACCESS_GRANTED), + ], true, true, false], + + [AccessDecisionManager::STRATEGY_PRIORITY, [ + $this->getVoter(VoterInterface::ACCESS_ABSTAIN), + $this->getVoter(VoterInterface::ACCESS_ABSTAIN), + ], false, true, false], + + [AccessDecisionManager::STRATEGY_PRIORITY, [ + $this->getVoter(VoterInterface::ACCESS_ABSTAIN), + $this->getVoter(VoterInterface::ACCESS_ABSTAIN), + ], true, true, true], ]; }