1212namespace Symfony \Component \Security \Core \Authorization ;
1313
1414use Symfony \Component \Security \Core \Authentication \Token \TokenInterface ;
15- use Symfony \Component \Security \Core \Authorization \Strategy \AccessDecisionStrategyInterface ;
1615use Symfony \Component \Security \Core \Authorization \Voter \VoterInterface ;
1716
1817/**
2524 */
2625class TraceableAccessDecisionManager implements AccessDecisionManagerInterface
2726{
28- private ?AccessDecisionStrategyInterface $ strategy = null ;
29- /** @var iterable<mixed, VoterInterface> */
30- private iterable $ voters = [];
27+ private ?string $ strategy = null ;
28+ /** @var array< VoterInterface> */
29+ private array $ voters = [];
3130 private array $ decisionLog = []; // All decision logs
3231 private array $ currentLog = []; // Logs being filled in
32+ private array $ accessDecisionStack = [];
3333
3434 public function __construct (
3535 private AccessDecisionManagerInterface $ manager ,
3636 ) {
37- // The strategy and voters are stored in a private properties of the decorated service
38- if (property_exists ($ manager , 'strategy ' )) {
39- $ reflection = new \ReflectionProperty ($ manager ::class, 'strategy ' );
40- $ this ->strategy = $ reflection ->getValue ($ manager );
41- }
42- if (property_exists ($ manager , 'voters ' )) {
43- $ reflection = new \ReflectionProperty ($ manager ::class, 'voters ' );
44- $ this ->voters = $ reflection ->getValue ($ manager );
45- }
4637 }
4738
48- public function decide (TokenInterface $ token , array $ attributes , mixed $ object = null , bool $ allowMultipleAttributes = false ): bool
39+ public function decide (TokenInterface $ token , array $ attributes , mixed $ object = null , bool | AccessDecision | null $ accessDecision = null , bool $ allowMultipleAttributes = false ): bool
4940 {
50- $ currentDecisionLog = [
41+ if (\is_bool ($ accessDecision )) {
42+ $ allowMultipleAttributes = $ accessDecision ;
43+ $ accessDecision = null ;
44+ }
45+
46+ // Using a stack since decide can be called by voters
47+ $ this ->currentLog [] = [
5148 'attributes ' => $ attributes ,
5249 'object ' => $ object ,
5350 'voterDetails ' => [],
5451 ];
5552
56- $ this ->currentLog [] = &$ currentDecisionLog ;
57-
58- $ result = $ this ->manager ->decide ($ token , $ attributes , $ object , $ allowMultipleAttributes );
59-
60- $ currentDecisionLog ['result ' ] = $ result ;
61-
62- $ this ->decisionLog [] = array_pop ($ this ->currentLog ); // Using a stack since decide can be called by voters
63-
64- return $ result ;
53+ $ accessDecision ??= end ($ this ->accessDecisionStack ) ?: new AccessDecision ();
54+ $ this ->accessDecisionStack [] = $ accessDecision ;
55+
56+ try {
57+ return $ accessDecision ->isGranted = $ this ->manager ->decide ($ token , $ attributes , $ object , $ accessDecision );
58+ } finally {
59+ $ this ->strategy = $ accessDecision ->strategy ;
60+ $ currentLog = array_pop ($ this ->currentLog );
61+ if (isset ($ accessDecision ->isGranted )) {
62+ $ currentLog ['result ' ] = $ accessDecision ->isGranted ;
63+ }
64+ $ this ->decisionLog [] = $ currentLog ;
65+ }
6566 }
6667
67- /**
68- * Adds voter vote and class to the voter details.
69- *
70- * @param array $attributes attributes used for the vote
71- * @param int $vote vote of the voter
72- */
73- public function addVoterVote (VoterInterface $ voter , array $ attributes , int $ vote ): void
68+ public function addVoterVote (VoterInterface $ voter , array $ attributes , int $ vote , array $ reasons = []): void
7469 {
7570 $ currentLogIndex = \count ($ this ->currentLog ) - 1 ;
7671 $ this ->currentLog [$ currentLogIndex ]['voterDetails ' ][] = [
7772 'voter ' => $ voter ,
7873 'attributes ' => $ attributes ,
7974 'vote ' => $ vote ,
75+ 'reasons ' => $ reasons ,
8076 ];
77+ $ this ->voters [$ voter ::class] = $ voter ;
8178 }
8279
8380 public function getStrategy (): string
8481 {
85- if (null === $ this ->strategy ) {
86- return '- ' ;
87- }
88- if ($ this ->strategy instanceof \Stringable) {
89- return (string ) $ this ->strategy ;
90- }
91-
92- return get_debug_type ($ this ->strategy );
82+ return $ this ->strategy ?? '- ' ;
9383 }
9484
9585 /**
96- * @return iterable<mixed, VoterInterface>
86+ * @return array< VoterInterface>
9787 */
98- public function getVoters (): iterable
88+ public function getVoters (): array
9989 {
10090 return $ this ->voters ;
10191 }
@@ -104,4 +94,11 @@ public function getDecisionLog(): array
10494 {
10595 return $ this ->decisionLog ;
10696 }
97+
98+ public function reset (): void
99+ {
100+ $ this ->strategy = null ;
101+ $ this ->voters = [];
102+ $ this ->decisionLog = [];
103+ }
107104}
0 commit comments