8000 make it possible to configure the voting strategy · symfony/symfony@6ef3459 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6ef3459

Browse files
committed
make it possible to configure the voting strategy
1 parent e188cd7 commit 6ef3459

13 files changed

+682
-177
lines changed

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

Lines changed: 23 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace Symfony\Component\Security\Core\Authorization;
1313

14+
use Symfony\Component\Security\Core\Authorization\Strategy\AffirmativeVoteStrategy;
15+
use Symfony\Component\Security\Core\Authorization\Strategy\ConsensusVoteStrategy;
16+
use Symfony\Component\Security\Core\Authorization\Strategy\UnanimousVoteStrategy;
17+
use Symfony\Component\Security\Core\Authorization\Strategy\VoteStrategyInterface;
1418
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
1519
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
1620

@@ -28,30 +32,34 @@ class AccessDecisionManager implements AccessDecisionManagerInterface
2832

2933
private $voters;
3034
private $strategy;
31-
private $allowIfAllAbstainDecisions;
32-
private $allowIfEqualGrantedDeniedDecisions;
35+
private $voteStrategy;
3336

3437
/**
3538
* Constructor.
3639
*
37-
* @param VoterInterface[] $voters An array of VoterInterface instances
38-
* @param string $strategy The vote strategy
39-
* @param bool $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not
40-
* @param bool $allowIfEqualGrantedDeniedDecisions Whether to grant access if result are equals
40+
* @param VoterInterface[] $voters An array of VoterInterface instances
41+
* @param string|VoteStrategyInterface $strategy The vote strategy
42+
* @param bool $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not
43+
* @param bool $allowIfEqualGrantedDeniedDecisions Whether to grant access if result are equals
4144
*
4245
* @throws \InvalidArgumentException
4346
*/
4447
public function __construct(array $voters = array(), $strategy = self::STRATEGY_AFFIRMATIVE, $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true)
4548
{
46-
$strategyMethod = 'decide'.ucfirst($strategy);
47-
if (!is_callable(array($this, $strategyMethod))) {
48-
throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy));
49-
}
50-
5149
$this->voters = $voters;
52-
$this->strategy = $strategyMethod;
53-
$this->allowIfAllAbstainDecisions = (bool) $allowIfAllAbstainDecisions;
54-
$this->allowIfEqualGrantedDeniedDecisions = (bool) $allowIfEqualGrantedDeniedDecisions;
50+
$this->strategy = $strategy;
51+
52+
if ($strategy instanceof VoteStrategyInterface) {
53+
$this->voteStrategy = $strategy;
54+
} elseif (self::STRATEGY_AFFIRMATIVE === $strategy) {
55+
$this->voteStrategy = new AffirmativeVoteStrategy($allowIfAllAbstainDecisions);
56+
} elseif (self::STRATEGY_CONSENSUS === $strategy) {
57+
$this->voteStrategy = new ConsensusVoteStrategy($allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions);
58+
} elseif (self::STRATEGY_UNANIMOUS === $strategy) {
59+
$this->voteStrategy = new UnanimousVoteStrategy($allowIfAllAbstainDecisions);
60+
} else {
61+
throw new \InvalidArgumentException('The vote strategy must be one of "affirmative", "consensus", "unanimous", or an instance of Symfony\Component\Security\Core\Authorization\Strategy\VoteStrategyInterface.');
62+
}
5563
}
5664

5765
/**
@@ -69,123 +77,6 @@ public function setVoters(array $voters)
6977
*/
7078
public function decide(TokenInterface $token, array $attributes, $object = null)
7179
{
72-
return $this->{$this->strategy}($token, $attributes, $object);
73-
}
74-
75-
/**
76-
* Grants access if any voter returns an affirmative response.
77-
*
78-
* If all voters abstained from voting, the decision will be based on the
79-
* allowIfAllAbstainDecisions property value (defaults to false).
80-
*/
81-
private function decideAffirmative(TokenInterface $token, array $attributes, $object = null)
82-
{
83-
$deny = 0;
84-
foreach ($this->voters as $voter) {
85-
$result = $voter->vote($token, $object, $attributes);
86-
switch ($result) {
87-
case VoterInterface::ACCESS_GRANTED:
88-
return true;
89-
90-
case VoterInterface::ACCESS_DENIED:
91-
++$deny;
92-
93-
break;
94-
95-
default:
96-
break;
97-
}
98-
}
99-
100-
if ($deny > 0) {
101-
return false;
102-
}
103-
104-
return $this->allowIfAllAbstainDecisions;
105-
}
106-
107-
/**
108-
* Grants access if there is consensus of granted against denied responses.
109-
*
110-
* Consensus means majority-rule (ignoring abstains) rather than unanimous
111-
* agreement (ignoring abstains). If you require unanimity, see
112-
* UnanimousBased.
113-
*
114-
* If there were an equal number of grant and deny votes, the decision will
115-
* be based on the allowIfEqualGrantedDeniedDecisions property value
116-
* (defaults to true).
117-
*
118-
* If all voters abstained from voting, the decision will be based on the
119-
* allowIfAllAbstainDecisions property value (defaults to false).
120-
*/
121-
private function decideConsensus(TokenInterface $token, array $attributes, $object = null)
122-
{
123-
$grant = 0;
124-
$deny = 0;
125-
foreach ($this->voters as $voter) {
126-
$result = $voter->vote($token, $object, $attributes);
127-
128-
switch ($result) {
129-
case VoterInterface::ACCESS_GRANTED:
130-
++$grant;
131-
132-
break;
133-
134-
case VoterInterface::ACCESS_DENIED:
135-
++$deny;
136-
137-
break;
138-
}
139-
}
140-
141-
if ($grant > $deny) {
142-
return true;
143-
}
144-
145-
if ($deny > $grant) {
146-
return false;
147-
}
148-
149-
if ($grant > 0) {
150-
return $this->allowIfEqualGrantedDeniedDecisions;
151-
}
152-
153-
return $this->allowIfAllAbstainDecisions;
154-
}
155-
156-
/**
157-
* Grants access if only grant (or abstain) votes were received.
158-
*
159-
* If all voters abstained from voting, the decision will be based on the
160-
* allowIfAllAbstainDecisions property value (defaults to false).
161-
*/
162-
private function decideUnanimous(TokenInterface $token, array $attributes, $object = null)
163-
{
164-
$grant = 0;
165-
foreach ($attributes as $attribute) {
166-
foreach ($this->voters as $voter) {
167-
$result = $voter->vote($token, $object, array($attribute));
168-
169-
switch ($result) {
170-
case VoterInterface::ACCESS_GRANTED:
171-
++$grant;
172-
173-
break;
174-
175-
case VoterInterface::ACCESS_DENIED:
176-
return false;
177-
178-
default:
179-
break;
180-
}
181-
}
182-
}
183-
184-
// no deny votes
185-
if ($grant > 0) {
186-
return true;
187-
}
188-
189-
return $this->allowIfAllAbstainDecisions;
80+
return $this->voteStrategy->decide($this->voters, $token, $attributes, $object);
19081
}
19182
}
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 <fabien@symfony.com>
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\Authorization\Strategy;
13+
14+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
16+
17+
/**
18+
* Grants access if any voter returns an affirmative response.
19+
*
20+
* If all voters abstained from voting, the decision will be based on the
21+
* allowIfAllAbstainDecisions argument value.
22+
*/
23+
class AffirmativeVoteStrategy implements VoteStrategyInterface
24+
{
25+
private $allowIfAllAbstainDecisions;
26+
27+
/**
28+
* @param bool $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not
29+
*/
30+
public function __construct($allowIfAllAbstainDecisions)
31+
{
32+
$this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions;
33+
}
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function decide(array $voters, TokenInterface $token, array $attributes, $subject = null)
39+
{
40+
$deny = 0;
41+
42+
foreach ($voters as $voter) {
43+
$result = $voter->vote($token, $subject, $attributes);
44+
switch ($result) {
45+
case VoterInterface::ACCESS_GRANTED:
46+
return true;
47+
48+
case VoterInterface::ACCESS_DENIED:
49+
++$deny;
50+
51+
break;
52+
53+
default:
54+
break;
55+
}
56+
}
57+
58+
if ($deny > 0) {
59+
return false;
60+
}
61+
62+
return $this->allowIfAllAbstainDecisions;
63+
}
64+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Authorization\Strategy;
13+
14+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
16+
17+
/**
18+
* Grants access if there is consensus of granted against denied responses.
19+
*
20+
* Consensus means majority-rule (ignoring abstains) rather than unanimous
21+
* agreement (ignoring abstains). If you require unanimity, see
22+
* UnanimousBased.
23+
*
24+
* If there were an equal number of grant and deny votes, the decision will
25+
* be based on the allowIfEqualGrantedDeniedDecisions property value
26+
* (defaults to true).
27+
*
28+
* If all voters abstained from voting, the decision will be based on the
29+
* allowIfAllAbstainDecisions argument value.
30+
*/
31+
class ConsensusVoteStrategy implements VoteStrategyInterface
32+
{
33+
private $allowIfAllAbstainDecisions;
34+
private $allowIfEqualGrantedDeniedDecisions;
35+
36+
/**
37+
* @param bool $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not
38+
* @param bool $allowIfEqualGrantedDeniedDecisions Whether to grant access if result are equals
39+
*/
40+
public function __construct($allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions)
41+
{
42+
$this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions;
43+
$this->allowIfEqualGrantedDeniedDecisions = $allowIfEqualGrantedDeniedDecisions;
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
public function decide(array $voters, TokenInterface $token, array $attributes, $subject = null)
50+
{
51+
$grant = 0;
52+
$deny = 0;
53+
54+
foreach ($voters as $voter) {
55+
$result = $voter->vote($token, $subject, $attributes);
56+
57+
switch ($result) {
58+
case VoterInterface::ACCESS_GRANTED:
59+
++$grant;
60+
61+
break;
62+
63+
case VoterInterface::ACCESS_DENIED:
64+
++$deny;
65+
66+
break;
67+
}
68+
}
69+
70+
if ($grant > $deny) {
71+
return true;
72+
}
73+
74+
if ($deny > $grant) {
75+
return false;
76+
}
77+
78+
if ($grant > 0) {
79+
return $this->allowIfEqualGrantedDeniedDecisions;
80+
}
81+
82+
return $this->allowIfAllAbstainDecisions;
83+
}
84+
}

0 commit comments

Comments
 (0)
0