8000 poc · symfony/symfony@949cf7d · GitHub
[go: up one dir, main page]

Skip to content

Commit 949cf7d

Browse files
committed
poc
1 parent b1bee60 commit 949cf7d

File tree

16 files changed

+300
-42
lines changed

16 files changed

+300
-42
lines changed

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

Lines 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

Lines 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>
Lines 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

Lines 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 = < F438 span class="pl-s1">$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

Lines 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

Lines 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

Lines 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'][] = [
Lines 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+
}
Lines 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+
}
Lines 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

Lines 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