diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
index 4818d379312a3..29ffc35757ca5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
@@ -232,6 +232,7 @@ protected function denyAccessUnlessGranted($attributes, $subject = null, string
$exception = $this->createAccessDeniedException($message);
$exception->setAttributes($attributes);
$exception->setSubject($subject);
+ $exception->setAccessDecision($this->container->get('security.authorization_checker')->getLastAccessDecision());
throw $exception;
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
index 6b0819513fa04..f11be0e1bf3bd 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
@@ -346,16 +346,17 @@
attribute {{ voter_detail['attributes'][0] }} |
{% endif %}
- {% if voter_detail['vote'] == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_GRANTED') %}
+ {% if voter_detail['vote'].access == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_GRANTED') %}
ACCESS GRANTED
- {% elseif voter_detail['vote'] == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_ABSTAIN') %}
+ {% elseif voter_detail['vote'].access == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_ABSTAIN') %}
ACCESS ABSTAIN
- {% elseif voter_detail['vote'] == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_DENIED') %}
+ {% elseif voter_detail['vote'].access == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_DENIED') %}
ACCESS DENIED
{% else %}
- unknown ({{ voter_detail['vote'] }})
+ unknown ({{ voter_detail['vote'].access }})
{% endif %}
|
+ {{ voter_detail['vote'].reason|default('~') }} |
{% endfor %}
diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecision.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecision.php
new file mode 100644
index 0000000000000..87ed37217a6db
--- /dev/null
+++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecision.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Core\Authorization;
+
+use Symfony\Component\Security\Core\Authorization\Voter\AccessTrait;
+use Symfony\Component\Security\Core\Authorization\Voter\Vote;
+use Symfony\Component\Security\Core\Authorization\Voter\Voter;
+use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
+
+/**
+ * An AccessDecision is returned by an AccessDecisionManager and contains the access verdict and all the related votes.
+ *
+ * @author Dany Maillard
+ */
+final class AccessDecision
+{
+ use AccessTrait;
+
+ /** @var Vote[] */
+ private $votes = [];
+
+ /**
+ * @param int $access One of the VoterInterface::ACCESS_* constants
+ * @param Vote[] $votes
+ */
+ private function __construct(int $access, array $votes = [])
+ {
+ $this->access = $access;
+ $this->votes = $votes;
+ }
+
+ /**
+ * @param Vote[] $votes
+ */
+ public static function createGranted(array $votes = []): self
+ {
+ return new self(VoterInterface::ACCESS_GRANTED, $votes);
+ }
+
+ /**
+ * @param Vote[] $votes
+ */
+ public static function createDenied(array $votes = []): self
+ {
+ return new self(VoterInterface::ACCESS_DENIED, $votes);
+ }
+
+ /**
+ * @return Vote[]
+ */
+ public function getVotes(): array
+ {
+ return $this->votes;
+ }
+
+ /**
+ * @return Vote[]
+ */
+ public function getGrantedVotes(): array
+ {
+ return $this->getVotesByAccess(Voter::ACCESS_GRANTED);
+ }
+
+ /**
+ * @return Vote[]
+ */
+ public function getAbstainedVotes(): array
+ {
+ return $this->getVotesByAccess(Voter::ACCESS_ABSTAIN);
+ }
+
+ /**
+ * @return Vote[]
+ */
+ public function getDeniedVotes(): array
+ {
+ return $this->getVotesByAccess(Voter::ACCESS_DENIED);
+ }
+
+ private function getVotesByAccess(int $access): array
+ {
+ return array_filter($this->votes, function (Vote $vote) use ($access) { return $vote->getAccess() === $access; });
+ }
+}
diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
index 8da5b06818ada..1c9ef362d1535 100644
--- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Core\Authorization;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
@@ -72,26 +73,27 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
* If all voters abstained from voting, the decision will be based on the
* allowIfAllAbstainDecisions property value (defaults to false).
*/
- private function decideAffirmative(TokenInterface $token, array $attributes, $object = null): bool
+ private function decideAffirmative(TokenInterface $token, array $attributes, $object = null): AccessDecision
{
+ $votes = [];
$deny = 0;
foreach ($this->voters as $voter) {
- $result = $voter->vote($token, $object, $attributes);
+ $votes[] = $vote = $this->vote($voter, $token, $object, $attributes);
- if (VoterInterface::ACCESS_GRANTED === $result) {
- return true;
+ if ($vote->isGranted()) {
+ return AccessDecision::createGranted($votes);
}
- if (VoterInterface::ACCESS_DENIED === $result) {
+ if ($vote->isDenied()) {
++$deny;
}
}
if ($deny > 0) {
- return false;
+ return AccessDecision::createDenied($votes);
}
- return $this->allowIfAllAbstainDecisions;
+ return $this->decideIfAllAbstainDecisions();
}
/**
@@ -108,33 +110,37 @@ private function decideAffirmative(TokenInterface $token, array $attributes, $ob
* If all voters abstained from voting, the decision will be based on the
* allowIfAllAbstainDecisions property value (defaults to false).
*/
- private function decideConsensus(TokenInterface $token, array $attributes, $object = null): bool
+ private function decideConsensus(TokenInterface $token, array $attributes, $object = null): AccessDecision
{
+ $votes = [];
$grant = 0;
$deny = 0;
foreach ($this->voters as $voter) {
- $result = $voter->vote($token, $object, $attributes);
+ $votes[] = $vote = $this->vote($voter, $token, $object, $attributes);
- if (VoterInterface::ACCESS_GRANTED === $result) {
+ if ($vote->isGranted()) {
++$grant;
- } elseif (VoterInterface::ACCESS_DENIED === $result) {
+ } elseif ($vote->isDenied()) {
++$deny;
}
}
if ($grant > $deny) {
- return true;
+ return AccessDecision::createGranted($votes);
}
if ($deny > $grant) {
- return false;
+ return AccessDecision::createDenied($votes);
}
if ($grant > 0) {
- return $this->allowIfEqualGrantedDeniedDecisions;
+ return $this->allowIfEqualGrantedDeniedDecisions
+ ? AccessDecision::createGranted()
+ : AccessDecision::createDenied()
+ ;
}
- return $this->allowIfAllAbstainDecisions;
+ return $this->decideIfAllAbstainDecisions();
}
/**
@@ -143,18 +149,19 @@ private function decideConsensus(TokenInterface $token, array $attributes, $obje
* If all voters abstained from voting, the decision will be based on the
* allowIfAllAbstainDecisions property value (defaults to false).
*/
- private function decideUnanimous(TokenInterface $token, array $attributes, $object = null): bool
+ private function decideUnanimous(TokenInterface $token, array $attributes, $object = null): AccessDecision
{
+ $votes = [];
$grant = 0;
foreach ($this->voters as $voter) {
foreach ($attributes as $attribute) {
- $result = $voter->vote($token, $object, [$attribute]);
+ $votes[] = $vote = $this->vote($voter, $token, $object, [$attribute]);
- if (VoterInterface::ACCESS_DENIED === $result) {
- return false;
+ if ($vote->isDenied()) {
+ return AccessDecision::createDenied($votes);
}
- if (VoterInterface::ACCESS_GRANTED === $result) {
+ if ($vote->isGranted()) {
++$grant;
}
}
@@ -162,10 +169,10 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje
// no deny votes
if ($grant > 0) {
- return true;
+ return AccessDecision::createGranted($votes);
}
- return $this->allowIfAllAbstainDecisions;
+ return $this->decideIfAllAbstainDecisions();
}
/**
@@ -191,4 +198,22 @@ private function decidePriority(TokenInterface $token, array $attributes, $objec
return $this->allowIfAllAbstainDecisions;
}
+
+ private function decideIfAllAbstainDecisions(): AccessDecision
+ {
+ return $this->allowIfAllAbstainDecisions
+ ? AccessDecision::createGranted()
+ : AccessDecision::createDenied()
+ ;
+ }
+
+ private function vote(VoterInterface $voter, TokenInterface $token, $subject, array $attributes): Vote
+ {
+ if (\is_int($vote = $voter->vote($token, $subject, $attributes))) {
+ trigger_deprecation('symfony/security', 5.1, 'Returning an int from the "%s::vote()" method is deprecated. Return a "%s" object instead.', \get_class($this->voter), Vote::class);
+ $vote = Vote::create($vote);
+ }
+
+ return $vote;
+ }
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php
index 97eb4f9d41a55..11d01979c8b5e 100644
--- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php
+++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php
@@ -26,7 +26,7 @@ interface AccessDecisionManagerInterface
* @param array $attributes An array of attributes associated with the method being invoked
* @param object $object The object to secure
*
- * @return bool true if the access is granted, false otherwise
+ * @return bool|AccessDecision Returning a boolean is deprecated since Symfony 5.1. Return an AccessDecision object instead.
*/
public function decide(TokenInterface $token, array $attributes, $object = null);
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php b/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php
index 9036bba029d2e..790f76b728316 100644
--- a/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php
+++ b/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php
@@ -29,6 +29,8 @@ class AuthorizationChecker implements AuthorizationCheckerInterface
private $accessDecisionManager;
private $authenticationManager;
private $alwaysAuthenticate;
+ /** @var AccessDecision */
+ private $lastAccessDecision;
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, bool $alwaysAuthenticate = false)
{
@@ -53,6 +55,19 @@ final public function isGranted($attribute, $subject = null): bool
$this->tokenStorage->setToken($token = $this->authenticationManager->authenticate($token));
}
- return $this->accessDecisionManager->decide($token, [$attribute], $subject);
+ $this->lastAccessDecision = $this->accessDecisionManager->decide($token, [$attribute], $subject);
+
+ if (\is_bool($this->lastAccessDecision)) {
+ trigger_deprecation('symfony/security', 5.1, 'Returning a boolean from the "%s::decide()" method is deprecated. Return an "%s" object instead', \get_class($this->accessDecisionManager), AccessDecision::class);
+
+ return $this->lastAccessDecision;
+ }
+
+ return $this->lastAccessDecision->isGranted();
+ }
+
+ public function getLastAccessDecision(): AccessDecision
+ {
+ return $this->lastAccessDecision;
}
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
index 7690b3e2264bc..e6d6931eebd28 100644
--- a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Core\Authorization;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
/**
@@ -48,7 +49,7 @@ public function __construct(AccessDecisionManagerInterface $manager)
/**
* {@inheritdoc}
*/
- public function decide(TokenInterface $token, array $attributes, $object = null): bool
+ public function decide(TokenInterface $token, array $attributes, $object = null)
{
$currentDecisionLog = [
'attributes' => $attributes,
@@ -71,9 +72,9 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
* Adds voter vote and class to the voter details.
*
* @param array $attributes attributes used for the vote
- * @param int $vote vote of the voter
+ * @param Vote $vote vote of the voter
*/
- public function addVoterVote(VoterInterface $voter, array $attributes, int $vote)
+ public function addVoterVote(VoterInterface $voter, array $attributes, Vote $vote)
{
$currentLogIndex = \count($this->currentLog) - 1;
$this->currentLog[$currentLogIndex]['voterDetails'][] = [
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/AccessTrait.php b/src/Symfony/Component/Security/Core/Authorization/Voter/AccessTrait.php
new file mode 100644
index 0000000000000..c008d7cf0c4d0
--- /dev/null
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/AccessTrait.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Core\Authorization\Voter;
+
+/**
+ * @author Dany Maillard
+ */
+trait AccessTrait
+{
+ /** @var int One of the VoterInterface::ACCESS_* constants */
+ protected $access;
+
+ public function getAccess(): int
+ {
+ return $this->access;
+ }
+
+ public function isGranted(): bool
+ {
+ return VoterInterface::ACCESS_GRANTED === $this->access;
+ }
+
+ public function isAbstain(): bool
+ {
+ return VoterInterface::ACCESS_ABSTAIN === $this->access;
+ }
+
+ public function isDenied(): bool
+ {
+ return VoterInterface::ACCESS_DENIED === $this->access;
+ }
+}
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php
index 48f7651256710..210b585851b6d 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php
@@ -33,9 +33,9 @@ public function __construct(VoterInterface $voter, EventDispatcherInterface $eve
$this->eventDispatcher = $eventDispatcher;
}
- public function vote(TokenInterface $token, $subject, array $attributes): int
+ public function vote(TokenInterface $token, $subject, array $attributes)
{
- $result = $this->voter->vote($token, $subject, $attributes);
+ $result = \is_int($vote = $this->voter->vote($token, $subject, $attributes)) ? Vote::create($vote) : $vote;
$this->eventDispatcher->dispatch(new VoteEvent($this->voter, $subject, $attributes, $result), 'debug.security.authorization.vote');
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/Vote.php b/src/Symfony/Component/Security/Core/Authorization/Voter/Vote.php
new file mode 100644
index 0000000000000..6a7e734b50caf
--- /dev/null
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/Vote.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Core\Authorization\Voter;
+
+/**
+ * A Vote is returned by a Voter and contains the access (granted, abstain or denied). It can also contains a reason
+ * explaining the why of the access which has been decided.
+ *
+ * @author Dany Maillard
+ */
+final class Vote
+{
+ use AccessTrait;
+
+ private $reason;
+ private $parameters;
+
+ /**
+ * @param int $access One of the VoterInterface::ACCESS_* constants
+ */
+ private function __construct(int $access, string $reason = '', array $parameters = [])
+ {
+ $this->access = $access;
+ $this->reason = $reason;
+ $this->parameters = $parameters;
+ }
+
+ public static function create(int $access, string $reason = '', array $parameters = []): self
+ {
+ return new self($access, $reason, $parameters);
+ }
+
+ public static function createGranted(string $reason, array $parameters = []): self
+ {
+ return new self(VoterInterface::ACCESS_GRANTED, $reason, $parameters);
+ }
+
+ public static function createAbstrain(string $reason, array $parameters = []): self
+ {
+ return new self(VoterInterface::ACCESS_ABSTAIN, $reason, $parameters);
+ }
+
+ public static function createDenied(string $reason, array $parameters = []): self
+ {
+ return new self(VoterInterface::ACCESS_DENIED, $reason, $parameters);
+ }
+
+ public function merge(self $vote): void
+ {
+ $this->reason .= trim(' '.$vote->getReason());
+ }
+
+ public function getReason(): string
+ {
+ return $this->reason;
+ }
+
+ public function getParameters(): array
+ {
+ return $this->parameters;
+ }
+}
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
index 1ef95d0e979dd..3556113700fcd 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
@@ -18,6 +18,7 @@
*
* @author Roman Marintšenko
* @author Grégoire Pineau
+ * @author Dany Maillard
*/
abstract class Voter implements VoterInterface
{
@@ -27,7 +28,7 @@ abstract class Voter implements VoterInterface
public function vote(TokenInterface $token, $subject, array $attributes)
{
// abstain vote by default in case none of the attributes are supported
- $vote = self::ACCESS_ABSTAIN;
+ $vote = $this->abstain();
foreach ($attributes as $attribute) {
if (!$this->supports($attribute, $subject)) {
@@ -35,17 +36,44 @@ public function vote(TokenInterface $token, $subject, array $attributes)
}
// as soon as at least one attribute is supported, default is to deny access
- $vote = self::ACCESS_DENIED;
+ $vote = $this->deny();
- if ($this->voteOnAttribute($attribute, $subject, $token)) {
+ $v = \is_bool($v = $this->voteOnAttribute($attribute, $subject, $token)) ? Vote::create($v) : $v; // BC layer
+ if ($v->isGranted()) {
// grant access as soon as at least one attribute returns a positive response
- return self::ACCESS_GRANTED;
+ return $v;
+ } else {
+ $vote->merge($v);
}
}
return $vote;
}
+ /**
+ * Creates a granted vote.
+ */
+ public function grant(string $reason = '', array $parameters = []): Vote
+ {
+ return Vote::createGranted($reason, $parameters);
+ }
+
+ /**
+ * Creates an abstained vote.
+ */
+ public function abstain(string $reason = '', array $parameters = []): Vote
+ {
+ return Vote::createAbstrain($reason, $parameters);
+ }
+
+ /**
+ * Creates a denied vote.
+ */
+ public function deny(string $reason = '', array $parameters = []): Vote
+ {
+ return Vote::createDenied($reason, $parameters);
+ }
+
/**
* Determines if the attribute and subject are supported by this voter.
*
@@ -62,7 +90,7 @@ abstract protected function supports(string $attribute, $subject);
*
* @param mixed $subject
*
- * @return bool
+ * @return bool|Vote Returning a boolean is deprecated since Symfony 5.1. Return a Vote object instead.
*/
abstract protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token);
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php
index 76c1968f5e2e1..8bd7d352ef548 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php
@@ -33,7 +33,7 @@ interface VoterInterface
* @param mixed $subject The subject to secure
* @param array $attributes An array of attributes associated with the method being invoked
*
- * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED
+ * @return int|Vote either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED
*/
public function vote(TokenInterface $token, $subject, array $attributes);
}
diff --git a/src/Symfony/Component/Security/Core/Event/VoteEvent.php b/src/Symfony/Component/Security/Core/Event/VoteEvent.php
index 78ac2f900ba48..aaa56f96eb2b4 100644
--- a/src/Symfony/Component/Security/Core/Event/VoteEvent.php
+++ b/src/Symfony/Component/Security/Core/Event/VoteEvent.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Security\Core\Event;
+use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Contracts\EventDispatcher\Event;
@@ -28,7 +29,7 @@ final class VoteEvent extends Event
private $attributes;
private $vote;
- public function __construct(VoterInterface $voter, $subject, array $attributes, int $vote)
+ public function __construct(VoterInterface $voter, $subject, array $attributes, Vote $vote)
{
$this->voter = $voter;
$this->subject = $subject;
@@ -51,7 +52,7 @@ public function getAttributes(): array
return $this->attributes;
}
- public function getVote(): int
+ public function getVote(): Vote
{
return $this->vote;
}
diff --git a/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php b/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php
index 0e59dc4077a91..881431e9abf6c 100644
--- a/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php
+++ b/src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php
@@ -11,6 +11,9 @@
namespace Symfony\Component\Security\Core\Exception;
+use Symfony\Component\Security\Core\Authorization\AccessDecision;
+use Symfony\Component\Security\Core\Authorization\Voter\Vote;
+
/**
* AccessDeniedException is thrown when the account has not the required role.
*
@@ -20,6 +23,8 @@ class AccessDeniedException extends RuntimeException
{
private $attributes = [];
private $subject;
+ /** @var AccessDecision */
+ private $accessDecision;
public function __construct(string $message = 'Access Denied.', \Throwable $previous = null)
{
@@ -57,4 +62,19 @@ public function setSubject($subject)
{
$this->subject = $subject;
}
+
+ /**
+ * Sets an access decision and appends the denied reasons to the exception message.
+ */
+ public function setAccessDecision(AccessDecision $accessDecision)
+ {
+ $this->accessDecision = $accessDecision;
+ $reasons = array_map(function (Vote $vote) { return $vote->getReason(); }, $this->accessDecision->getDeniedVotes());
+ $this->message .= rtrim(' '.implode(' ', $reasons));
+ }
+
+ public function getAccessDecision(): AccessDecision
+ {
+ return $this->accessDecision;
+ }
}
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy