Skip to content

Commit 70afc2c

Browse files
committed
feature #60085 [Security] improve VoteObject adding extraData for give more possibilities to AccessDecicsionStrategy (eltharin)
This PR was squashed before being merged into the 7.4 branch. Discussion ---------- [Security] improve VoteObject adding extraData for give more possibilities to AccessDecicsionStrategy | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | License | MIT In continuation of symfony/symfony#58107 and symfony/symfony#59771, add ExtraData in VoteObject and pass it to AccessDecicsionStrategy Object to allow the decision to be refined. ExtraData can be an array or an object, and AccessDecicsionStrategy can get it to get the final decision. In 7.2, voter can only respond abstain / allow or deny, but what if we want more choices, per example a ponderable vote ? With this PR, it allow to put somme data in the new VoteObject as : ```php /** ScoreData.php */ /** MyVoter.php */ public function vote(TokenInterface $token, mixed $subject, array $attributes, ?Vote $vote = null) : int { $vote->result = 1; $vote->reasons[] = 'is Admin'; $vote->extraData['score'] = 10; return $vote->result; } ``` we need also a custom strategy to take this score into account : ```php /** MyStrategy.php */ public function decide(\Traversable $results, $accessDecision = null): bool { $score = 0; foreach ($results as $key => $result) { $vote = $accessDecision->votes[$key]; // <== if(array_key_exists('score', $vote->extraData)) { $score += $vote->extraData['score']; } else { $score += $vote->result; } } $accessDecision->result = $score; if ($score > 0) { return true; } if ($score< 0) { return false; } return $this->allowIfAllAbstainDecisions; } ``` AccessDecision contains Vote objects and we can read our score from it. Commits ------- bd24f84ab90 [Security] improve VoteObject adding extraData for give more possibilities to AccessDecicsionStrategy
2 parents 6f0246d + 771c92e commit 70afc2c

File tree

9 files changed

+23
-7
lines changed

9 files changed

+23
-7
lines changed

Authorization/AccessDecisionManager.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public function decide(TokenInterface $token, array $attributes, mixed $object =
7171

7272
try {
7373
return $accessDecision->isGranted = $this->strategy->decide(
74-
$this->collectResults($token, $attributes, $object, $accessDecision)
74+
$this->collectResults($token, $attributes, $object, $accessDecision),
75+
$accessDecision,
7576
);
7677
} finally {
7778
array_pop($this->accessDecisionStack);

Authorization/Strategy/AccessDecisionStrategyInterface.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

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

14+
use Symfony\Component\Security\Core\Authorization\AccessDecision;
15+
1416
/**
1517
* A strategy for turning a stream of votes into a final decision.
1618
*
@@ -20,6 +22,7 @@ interface AccessDecisionStrategyInterface
2022
{
2123
/**
2224
* @param \Traversable<int> $results
25+
* @param ?AccessDecision $accessDecision
2326
*/
24-
public function decide(\Traversable $results): bool;
27+
public function decide(\Traversable $results/* , ?AccessDecision $accessDecision = null */): bool;
2528
}

Authorization/Strategy/AffirmativeStrategy.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

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

14+
use Symfony\Component\Security\Core\Authorization\AccessDecision;
1415
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
1516

1617
/**
@@ -29,7 +30,7 @@ public function __construct(
2930
) {
3031
}
3132

32-
public function decide(\Traversable $results): bool
33+
public function decide(\Traversable $results, ?AccessDecision $accessDecision = null): bool
3334
{
3435
$deny = 0;
3536
foreach ($results as $result) {

Authorization/Strategy/ConsensusStrategy.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

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

14+
use Symfony\Component\Security\Core\Authorization\AccessDecision;
1415
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
1516

1617
/**
@@ -38,7 +39,7 @@ public function __construct(
3839
) {
3940
}
4041

41-
public function decide(\Traversable $results): bool
42+
public function decide(\Traversable $results, ?AccessDecision $accessDecision = null): bool
4243
{
4344
$grant = 0;
4445
$deny = 0;

Authorization/Strategy/PriorityStrategy.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

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

14+
use Symfony\Component\Security\Core\Authorization\AccessDecision;
1415
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
1516

1617
/**
@@ -30,7 +31,7 @@ public function __construct(
3031
) {
3132
}
3233

33-
public function decide(\Traversable $results): bool
34+
public function decide(\Traversable $results, ?AccessDecision $accessDecision = null): bool
3435
{
3536
foreach ($results as $result) {
3637
if (VoterInterface::ACCESS_GRANTED === $result) {

Authorization/Strategy/UnanimousStrategy.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

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

14+
use Symfony\Component\Security\Core\Authorization\AccessDecision;
1415
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
1516

1617
/**
@@ -29,7 +30,7 @@ public function __construct(
2930
) {
3031
}
3132

32-
public function decide(\Traversable $results): bool
33+
public function decide(\Traversable $results, ?AccessDecision $accessDecision = null): bool
3334
{
3435
$grant = 0;
3536
foreach ($results as $result) {

Authorization/Voter/Vote.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ class Vote
2828
*/
2929
public array $reasons = [];
3030

31+
/**
32+
* @var array<string, mixed>
33+
*/
34+
public array $extraData = [];
35+
3136
public function addReason(string $reason): void
3237
{
3338
$this->reasons[] = $reason;

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ CHANGELOG
66

77
* Add `MermaidDumper` to dump Role Hierarchy graphs in the Mermaid.js flowchart format
88
* Deprecate `PersistentTokenInterface::getClass()`, the user class will be removed from the remember-me cookie in 8.0
9+
* Add `extraData` property to `Vote` objects
10+
* Add argument `$accessDecision` to `AccessDecisionStrategyInterface`
911

1012
7.3
1113
---

Tests/Authorization/AccessDecisionManagerTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\Assert;
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17+
use Symfony\Component\Security\Core\Authorization\AccessDecision;
1718
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
1819
use Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface;
1920
use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface;
@@ -40,7 +41,7 @@ public function testVoterCalls()
4041
];
4142

4243
$strategy = new class implements AccessDecisionStrategyInterface {
43-
public function decide(\Traversable $results): bool
44+
public function decide(\Traversable $results, ?AccessDecision $accessDecision = null): bool
4445
{
4546
$i = 0;
4647
foreach ($results as $result) {

0 commit comments

Comments
 (0)