vendor/symfony/security-core/Authorization/TraceableAccessDecisionManager.php line 63

  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <[email protected]>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Security\Core\Authorization;
  11. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  12. use Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface;
  13. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  14. /**
  15. * Decorates the original AccessDecisionManager class to log information
  16. * about the security voters and the decisions made by them.
  17. *
  18. * @author Javier Eguiluz <[email protected]>
  19. *
  20. * @internal
  21. */
  22. class TraceableAccessDecisionManager implements AccessDecisionManagerInterface
  23. {
  24. private AccessDecisionManagerInterface $manager;
  25. private ?AccessDecisionStrategyInterface $strategy = null;
  26. /** @var iterable<mixed, VoterInterface> */
  27. private iterable $voters = [];
  28. private array $decisionLog = []; // All decision logs
  29. private array $currentLog = []; // Logs being filled in
  30. public function __construct(AccessDecisionManagerInterface $manager)
  31. {
  32. $this->manager = $manager;
  33. // The strategy and voters are stored in a private properties of the decorated service
  34. if (property_exists($manager, 'strategy')) {
  35. $reflection = new \ReflectionProperty($manager::class, 'strategy');
  36. $this->strategy = $reflection->getValue($manager);
  37. }
  38. if (property_exists($manager, 'voters')) {
  39. $reflection = new \ReflectionProperty($manager::class, 'voters');
  40. $this->voters = $reflection->getValue($manager);
  41. }
  42. }
  43. public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false): bool
  44. {
  45. $currentDecisionLog = [
  46. 'attributes' => $attributes,
  47. 'object' => $object,
  48. 'voterDetails' => [],
  49. ];
  50. $this->currentLog[] = &$currentDecisionLog;
  51. $result = $this->manager->decide($token, $attributes, $object, $allowMultipleAttributes);
  52. $currentDecisionLog['result'] = $result;
  53. $this->decisionLog[] = array_pop($this->currentLog); // Using a stack since decide can be called by voters
  54. return $result;
  55. }
  56. /**
  57. * Adds voter vote and class to the voter details.
  58. *
  59. * @param array $attributes attributes used for the vote
  60. * @param int $vote vote of the voter
  61. */
  62. public function addVoterVote(VoterInterface $voter, array $attributes, int $vote): void
  63. {
  64. $currentLogIndex = \count($this->currentLog) - 1;
  65. $this->currentLog[$currentLogIndex]['voterDetails'][] = [
  66. 'voter' => $voter,
  67. 'attributes' => $attributes,
  68. 'vote' => $vote,
  69. ];
  70. }
  71. public function getStrategy(): string
  72. {
  73. if (null === $this->strategy) {
  74. return '-';
  75. }
  76. if (method_exists($this->strategy, '__toString')) {
  77. return (string) $this->strategy;
  78. }
  79. return get_debug_type($this->strategy);
  80. }
  81. /**
  82. * @return iterable<mixed, VoterInterface>
  83. */
  84. public function getVoters(): iterable
  85. {
  86. return $this->voters;
  87. }
  88. public function getDecisionLog(): array
  89. {
  90. return $this->decisionLog;
  91. }
  92. }