Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 949cf7d

Browse filesBrowse files
committed
poc
1 parent b1bee60 commit 949cf7d
Copy full SHA for 949cf7d

16 files changed

+300
-42
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use Symfony\Component\Routing\RouterInterface;
3838
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
3939
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
40+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
4041
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
4142
use Symfony\Component\Security\Core\User\UserInterface;
4243
use Symfony\Component\Security\Csrf\CsrfToken;
@@ -232,6 +233,7 @@ protected function denyAccessUnlessGranted($attributes, $subject = null, string
232233
$exception = $this->createAccessDeniedException($message);
233234
$exception->setAttributes($attributes);
234235
$exception->setSubject($subject);
236+
$exception->setAccessDecision($this->container->get('security.authorization_checker')->getLastAccessDecision());
235237

236238
throw $exception;
237239
}

‎src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,16 +346,17 @@
346346
<td class="font-normal text-small">attribute {{ voter_detail['attributes'][0] }}</td>
347347
{% endif %}
348348
<td class="font-normal text-small">
349-
{% if voter_detail['vote'] == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_GRANTED') %}
349+
{% if voter_detail['vote'].access == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_GRANTED') %}
350350
ACCESS GRANTED
351-
{% elseif voter_detail['vote'] == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_ABSTAIN') %}
351+
{% elseif voter_detail['vote'].access == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_ABSTAIN') %}
352352
ACCESS ABSTAIN
353-
{% elseif voter_detail['vote'] == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_DENIED') %}
353+
{% elseif voter_detail['vote'].access == constant('Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::ACCESS_DENIED') %}
354354
ACCESS DENIED
355355
{% else %}
356-
unknown ({{ voter_detail['vote'] }})
356+
unknown ({{ voter_detail['vote'].access }})
357357
{% endif %}
358358
</td>
359+
<td class="font-normal text-small">{{ voter_detail['vote'].reason }}</td>
359360
</tr>
360361
{% endfor %}
361362
</tbody>
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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;
13+
14+
use Symfony\Component\Security\Core\Authorization\Voter\AccessTrait;
15+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
16+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
17+
18+
class AccessDecision
19+
{
20+
use AccessTrait;
21+
22+
/** @var Vote[] */
23+
private $votes;
24+
25+
private function __construct(int $access, array $votes = [])
26+
{
27+
$this->access = $access;
28+
$this->votes = $votes;
29+
}
30+
31+
public static function createGranted(array $votes = []): self
32+
{
33+
return new self(VoterInterface::ACCESS_GRANTED, $votes);
34+
}
35+
36+
public static function createDenied(array $votes = []): self
37+
{
38+
return new self(VoterInterface::ACCESS_DENIED, $votes);
39+
}
40+
41+
/**
42+
* @return Vote[]
43+
*/
44+
public function getVotes(int $access = null): array
45+
{
46+
if (null === $access) {
47+
return $this->votes;
48+
}
49+
50+
return array_filter($this->votes, function (Vote $vote) use ($access) { return $vote->getAccess() === $access; });
51+
}
52+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
+42-22Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Core\Authorization;
1313

1414
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
1516
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
1617
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
1718

@@ -72,26 +73,27 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
7273
* If all voters abstained from voting, the decision will be based on the
7374
* allowIfAllAbstainDecisions property value (defaults to false).
7475
*/
75-
private function decideAffirmative(TokenInterface $token, array $attributes, $object = null): bool
76+
private function decideAffirmative(TokenInterface $token, array $attributes, $object = null): AccessDecision
7677
{
78+
$votes = [];
7779
$deny = 0;
7880
foreach ($this->voters as $voter) {
79-
$result = $voter->vote($token, $object, $attributes);
81+
$votes[] = $vote = $this->vote($voter, $token, $object, $attributes);
8082

81-
if (VoterInterface::ACCESS_GRANTED === $result) {
82-
return true;
83+
if ($vote->isGranted()) {
84+
return AccessDecision::createGranted($votes);
8385
}
8486

85-
if (VoterInterface::ACCESS_DENIED === $result) {
87+
if ($vote->isDenied()) {
8688
++$deny;
8789
}
8890
}
8991

9092
if ($deny > 0) {
91-
return false;
93+
return AccessDecision::createDenied($votes);
9294
}
9395

94-
return $this->allowIfAllAbstainDecisions;
96+
return $this->decideIfAllAbstainDecisions();
9597
}
9698

9799
/**
@@ -108,33 +110,37 @@ private function decideAffirmative(TokenInterface $token, array $attributes, $ob
108110
* If all voters abstained from voting, the decision will be based on the
109111
* allowIfAllAbstainDecisions property value (defaults to false).
110112
*/
111-
private function decideConsensus(TokenInterface $token, array $attributes, $object = null): bool
113+
private function decideConsensus(TokenInterface $token, array $attributes, $object = null): AccessDecision
112114
{
115+
$votes = [];
113116
$grant = 0;
114117
$deny = 0;
115118
foreach ($this->voters as $voter) {
116-
$result = $voter->vote($token, $object, $attributes);
119+
$votes[] = $vote = $this->vote($voter, $token, $object, $attributes);
117120

118-
if (VoterInterface::ACCESS_GRANTED === $result) {
121+
if ($vote->isGranted()) {
119122
++$grant;
120-
} elseif (VoterInterface::ACCESS_DENIED === $result) {
123+
} elseif ($vote->isDenied()) {
121124
++$deny;
122125
}
123126
}
124127

125128
if ($grant > $deny) {
126-
return true;
129+
return AccessDecision::createGranted($votes);
127130
}
128131

129132
if ($deny > $grant) {
130-
return false;
133+
return AccessDecision::createDenied($votes);
131134
}
132135

133136
if ($grant > 0) {
134-
return $this->allowIfEqualGrantedDeniedDecisions;
137+
return $this->allowIfEqualGrantedDeniedDecisions
138+
? AccessDecision::createGranted()
139+
: AccessDecision::createDenied()
140+
;
135141
}
136142

137-
return $this->allowIfAllAbstainDecisions;
143+
return $this->decideIfAllAbstainDecisions();
138144
}
139145

140146
/**
@@ -143,29 +149,30 @@ private function decideConsensus(TokenInterface $token, array $attributes, $obje
143149
* If all voters abstained from voting, the decision will be based on the
144150
* allowIfAllAbstainDecisions property value (defaults to false).
145151
*/
146-
private function decideUnanimous(TokenInterface $token, array $attributes, $object = null): bool
152+
private function decideUnanimous(TokenInterface $token, array $attributes, $object = null): AccessDecision
147153
{
154+
$votes = [];
148155
$grant = 0;
149156
foreach ($this->voters as $voter) {
150157
foreach ($attributes as $attribute) {
151-
$result = $voter->vote($token, $object, [$attribute]);
158+
$votes[] = $vote = $this->vote($voter, $token, $object, [$attribute]);
152159

153-
if (VoterInterface::ACCESS_DENIED === $result) {
154-
return false;
160+
if ($vote->isDenied()) {
161+
return AccessDecision::createDenied($votes);
155162
}
156163

157-
if (VoterInterface::ACCESS_GRANTED === $result) {
164+
if ($vote->isGranted()) {
158165
++$grant;
159166
}
160167
}
161168
}
162169

163170
// no deny votes
164171
if ($grant > 0) {
165-
return true;
172+
return AccessDecision::createGranted($votes);
166173
}
167174

168-
return $this->allowIfAllAbstainDecisions;
175+
return $this->decideIfAllAbstainDecisions();
169176
}
170177

171178
/**
@@ -191,4 +198,17 @@ private function decidePriority(TokenInterface $token, array $attributes, $objec
191198

192199
return $this->allowIfAllAbstainDecisions;
193200
}
201+
202+
private function decideIfAllAbstainDecisions(): AccessDecision
203+
{
204+
return $this->allowIfAllAbstainDecisions
205+
? AccessDecision::createGranted()
206+
: AccessDecision::createDenied()
207+
;
208+
}
209+
210+
private function vote(VoterInterface $voter, TokenInterface $token, $subject, array $attributes): Vote
211+
{
212+
return is_int($vote = $voter->vote($token, $subject, $attributes)) ? Vote::create($vote) : $vote;
213+
}
194214
}

‎src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ interface AccessDecisionManagerInterface
2626
* @param array $attributes An array of attributes associated with the method being invoked
2727
* @param object $object The object to secure
2828
*
29-
* @return bool true if the access is granted, false otherwise
29+
* @return bool|AccessDecision true if the access is granted, false otherwise
3030
*/
3131
public function decide(TokenInterface $token, array $attributes, $object = null);
3232
}

‎src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class AuthorizationChecker implements AuthorizationCheckerInterface
2929
private $accessDecisionManager;
3030
private $authenticationManager;
3131
private $alwaysAuthenticate;
32+
/** @var AccessDecision */
33+
private $lastAccessDecision;
3234

3335
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, bool $alwaysAuthenticate = false)
3436
{
@@ -53,6 +55,11 @@ final public function isGranted($attribute, $subject = null): bool
5355
$this->tokenStorage->setToken($token = $this->authenticationManager->authenticate($token));
5456
}
5557

56-
return $this->accessDecisionManager->decide($token, [$attribute], $subject);
58+
return ($this->lastAccessDecision = $this->accessDecisionManager->decide($token, [$attribute], $subject))->isGranted();
59+
}
60+
61+
public function getLastAccessDecision(): AccessDecision
62+
{
63+
return $this->lastAccessDecision;
5764
}
5865
}

‎src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Core\Authorization;
1313

1414
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
1516
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
1617

1718
/**
@@ -48,7 +49,7 @@ public function __construct(AccessDecisionManagerInterface $manager)
4849
/**
4950
* {@inheritdoc}
5051
*/
51-
public function decide(TokenInterface $token, array $attributes, $object = null): bool
52+
public function decide(TokenInterface $token, array $attributes, $object = null)
5253
{
5354
$currentDecisionLog = [
5455
'attributes' => $attributes,
@@ -71,9 +72,9 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
7172
* Adds voter vote and class to the voter details.
7273
*
7374
* @param array $attributes attributes used for the vote
74-
* @param int $vote vote of the voter
75+
* @param Vote $vote vote of the voter
7576
*/
76-
public function addVoterVote(VoterInterface $voter, array $attributes, int $vote)
77+
public function addVoterVote(VoterInterface $voter, array $attributes, Vote $vote)
7778
{
7879
$currentLogIndex = \count($this->currentLog) - 1;
7980
$this->currentLog[$currentLogIndex]['voterDetails'][] = [
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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\Voter;
13+
14+
trait AccessTrait
15+
{
16+
/** @var int */
17+
private $access;
18+
19+
public function getAccess(): int
20+
{
21+
return $this->access;
22+
}
23+
24+
public function isGranted(): bool
25+
{
26+
return VoterInterface::ACCESS_GRANTED === $this->access;
27+
}
28+
29+
public function isAbstain(): bool
30+
{
31+
return VoterInterface::ACCESS_ABSTAIN === $this->access;
32+
}
33+
34+
public function isDenied(): bool
35+
{
36+
return VoterInterface::ACCESS_DENIED === $this->access;
37+
}
38+
}
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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\Voter;
13+
14+
interface ExplainedVoterInterface
15+
{
16+
public function grant(string $reason = '', array $parameters = []): Vote;
17+
public function abstain(string $reason = '', array $parameters = []): Vote;
18+
public function deny(string $reason = '', array $parameters = []): Vote;
19+
}
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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\Voter;
13+
14+
trait ExplainedVoterTrait
15+
{
16+
public function grant(string $reason = '', array $parameters = []): Vote
17+
{
18+
return Vote::createGranted($reason, $parameters);
19+
}
20+
21+
public function abstain(string $reason = '', array $parameters = []): Vote
22+
{
23+
return Vote::createAbstrain($reason, $parameters);
24+
}
25+
26+
public function deny(string $reason = '', array $parameters = []): Vote
27+
{
28+
return Vote::createDenied($reason, $parameters);
29+
}
30+
}

‎src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ public function __construct(VoterInterface $voter, EventDispatcherInterface $eve
3333
$this->eventDispatcher = $eventDispatcher;
3434
}
3535

36-
public function vote(TokenInterface $token, $subject, array $attributes): int
36+
public function vote(TokenInterface $token, $subject, array $attributes)
3737
{
38-
$result = $this->voter->vote($token, $subject, $attributes);
38+
$result = is_int($vote = $this->voter->vote($token, $subject, $attributes)) ? Vote::create($vote) : $vote;
3939

4040
$this->eventDispatcher->dispatch(new VoteEvent($this->voter, $subject, $attributes, $result), 'debug.security.authorization.vote');
4141

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.