From c189178d5a36cd425becaab578fee9935f2b87e7 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 4 May 2015 20:59:12 -0400 Subject: [PATCH 1/2] Adding a new AccessDecisionManagerAwareInterface that voters can implement to be able to ask other voters --- .../Authorization/AccessDecisionManager.php | 7 +++++++ .../AccessDecisionManagerAwareInterface.php | 21 +++++++++++++++++++ .../AccessDecisionManagerTest.php | 18 ++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerAwareInterface.php diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php index b8b6a776e9b8e..967847e2101ce 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php @@ -56,6 +56,13 @@ public function __construct(array $voters, $strategy = self::STRATEGY_AFFIRMATIV $this->strategy = $strategyMethod; $this->allowIfAllAbstainDecisions = (bool) $allowIfAllAbstainDecisions; $this->allowIfEqualGrantedDeniedDecisions = (bool) $allowIfEqualGrantedDeniedDecisions; + + // inject this AccessDecisionManager into any voters that want it + foreach ($this->voters as $voter) { + if ($voter instanceof AccessDecisionManagerAwareInterface) { + $voter->setAccessDecisionManager($this); + } + } } /** diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerAwareInterface.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerAwareInterface.php new file mode 100644 index 0000000000000..e4c74f0ec2b0a --- /dev/null +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerAwareInterface.php @@ -0,0 +1,21 @@ +getVoter(VoterInterface::ACCESS_GRANTED)), 'fooBar'); } + public function testAccessDecisionManagerInterfaceSetting() + { + // mock our stub voter that implements the AccessDecisionManagerAwareInterface + $voter = $this->getMock('Symfony\Component\Security\Core\Tests\Authorization\VoterWithAccessDecisionManagerAwareInterfaceStub'); + + $voter->expects($this->once()) + ->method('setAccessDecisionManager') + ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authorization\AccessDecisionManager')); + + $manager = new AccessDecisionManager(array($voter)); + } + /** * @dataProvider getStrategyTests */ @@ -196,3 +209,8 @@ protected function getVoterSupportsAttribute($ret) return $voter; } } + +// stub class that implements AccessDecisionManagerAwareInterface +abstract class VoterWithAccessDecisionManagerAwareInterfaceStub implements VoterInterface, AccessDecisionManagerAwareInterface +{ +} From f24e1a694f5c1097c5ba15c8262c39fc8f3d098b Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 4 May 2015 21:14:00 -0400 Subject: [PATCH 2/2] Moving the setAccessDecisionManager() calls out of the __construct I just don't like doing any work in the __construct --- .../Authorization/AccessDecisionManager.php | 30 ++++++++++++++----- .../AccessDecisionManagerTest.php | 19 ++++++++---- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php index 967847e2101ce..abd6cb802787a 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php @@ -30,6 +30,7 @@ class AccessDecisionManager implements AccessDecisionManagerInterface private $strategy; private $allowIfAllAbstainDecisions; private $allowIfEqualGrantedDeniedDecisions; + private $areVotersPrepared = false; /** * Constructor. @@ -56,13 +57,6 @@ public function __construct(array $voters, $strategy = self::STRATEGY_AFFIRMATIV $this->strategy = $strategyMethod; $this->allowIfAllAbstainDecisions = (bool) $allowIfAllAbstainDecisions; $this->allowIfEqualGrantedDeniedDecisions = (bool) $allowIfEqualGrantedDeniedDecisions; - - // inject this AccessDecisionManager into any voters that want it - foreach ($this->voters as $voter) { - if ($voter instanceof AccessDecisionManagerAwareInterface) { - $voter->setAccessDecisionManager($this); - } - } } /** @@ -109,6 +103,7 @@ public function supportsClass($class) */ private function decideAffirmative(TokenInterface $token, array $attributes, $object = null) { + $this->prepareVoters(); $deny = 0; foreach ($this->voters as $voter) { $result = $voter->vote($token, $object, $attributes); @@ -149,6 +144,7 @@ private function decideAffirmative(TokenInterface $token, array $attributes, $ob */ private function decideConsensus(TokenInterface $token, array $attributes, $object = null) { + $this->prepareVoters(); $grant = 0; $deny = 0; $abstain = 0; @@ -196,6 +192,7 @@ private function decideConsensus(TokenInterface $token, array $attributes, $obje */ private function decideUnanimous(TokenInterface $token, array $attributes, $object = null) { + $this->prepareVoters(); $grant = 0; foreach ($attributes as $attribute) { foreach ($this->voters as $voter) { @@ -223,4 +220,23 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje return $this->allowIfAllAbstainDecisions; } + + /** + * Guarantees that AccessDecisionManagerAwareInterface voters have + * been prepared properly. + */ + private function prepareVoters() + { + if ($this->areVotersPrepared) { + return; + } + + // inject this AccessDecisionManager into any voters that want it + foreach ($this->voters as $voter) { + if ($voter instanceof AccessDecisionManagerAwareInterface) { + $voter->setAccessDecisionManager($this); + } + } + $this->areVotersPrepared = true; + } } diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php index 3246a9c718726..7e8f4e3f06a0d 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -65,14 +65,21 @@ public function testSetUnsupportedStrategy() public function testAccessDecisionManagerInterfaceSetting() { - // mock our stub voter that implements the AccessDecisionManagerAwareInterface - $voter = $this->getMock('Symfony\Component\Security\Core\Tests\Authorization\VoterWithAccessDecisionManagerAwareInterfaceStub'); + $strategies = array('affirmative', 'consensus', 'unanimous'); - $voter->expects($this->once()) - ->method('setAccessDecisionManager') - ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authorization\AccessDecisionManager')); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + foreach ($strategies as $strategy) { + // mock our stub voter that implements the AccessDecisionManagerAwareInterface + $voter = $this->getMock('Symfony\Component\Security\Core\Tests\Authorization\VoterWithAccessDecisionManagerAwareInterfaceStub'); + + $manager = new AccessDecisionManager(array($voter), $strategy); - $manager = new AccessDecisionManager(array($voter)); + $voter->expects($this->once()) + ->method('setAccessDecisionManager') + ->with($manager); + + $manager->decide($token, array('ROLE_DOES_NOT_MATTER')); + } } /**