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 b53c509

Browse filesBrowse files
committed
[Security] Vote can have multiple messages
1 parent 831850c commit b53c509
Copy full SHA for b53c509

File tree

4 files changed

+126
-28
lines changed
Filter options

4 files changed

+126
-28
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authorization/Voter/Vote.php
+61-18Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,36 @@
1111

1212
namespace Symfony\Component\Security\Core\Authorization\Voter;
1313

14+
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
15+
1416
/**
1517
* A Vote is returned by a Voter and contains the access (granted, abstain or denied).
16-
* It can also contain a message explaining the vote decision.
18+
* It can also contain one or multiple messages explaining the vote decision.
1719
*
1820
* @author Dany Maillard <danymaillard93b@gmail.com>
21+
* @author Antoine Lamirault <lamiraultantoine@gmail.com>
1922
*/
2023
final class Vote
2124
{
22-
/** @var int One of the VoterInterface::ACCESS_* constants */
23-
private $access;
24-
private $message;
25-
private $context;
25+
/**
26+
* @var int One of the VoterInterface::ACCESS_* constants
27+
*/
28+
private int $access;
29+
30+
/**
31+
* @var string[]
32+
*/
33+
private array $messages;
34+
35+
private array $context;
2636

2737
/**
2838
* @param int $access One of the VoterInterface::ACCESS_* constants
2939
*/
30-
public function __construct(int $access, string $message = '', array $context = [])
40+
public function __construct(int $access, string|array $messages = [], array $context = [])
3141
{
3242
$this->access = $access;
33-
$this->message = $message;
43+
$this->setMessages($messages);
3444
$this->context = $context;
3545
}
3646

@@ -54,34 +64,67 @@ public function isDenied(): bool
5464
return VoterInterface::ACCESS_DENIED === $this->access;
5565
}
5666

57-
public static function create(int $access, string $message = '', array $context = []): self
67+
/**
68+
* @param string|string[] $messages
69+
*/
70+
public static function create(int $access, string|array $messages = [], array $context = []): self
5871
{
59-
return new self($access, $message, $context);
72+
return new self($access, $messages, $context);
6073
}
6174

62-
public static function createGranted(string $message = '', array $context = []): self
75+
/**
76+
* @param string|string[] $messages
77+
*/
78+
public static function createGranted(string|array $messages = [], array $context = []): self
6379
{
64-
return new self(VoterInterface::ACCESS_GRANTED, $message, $context);
80+
return new self(VoterInterface::ACCESS_GRANTED, $messages, $context);
6581
}
6682

67-
public static function createAbstain(string $message = '', array $context = []): self
83+
/**
84+
* @param string|string[] $messages
85+
*/
86+
public static function createAbstain(string|array $messages = [], array $context = []): self
6887
{
69-
return new self(VoterInterface::ACCESS_ABSTAIN, $message, $context);
88+
return new self(VoterInterface::ACCESS_ABSTAIN, $messages, $context);
7089
}
7190

72-
public static function createDenied(string $message = '', array $context = []): self
91+
/**
92+
* @param string|string[] $messages
93+
*/
94+
public static function createDenied(string|array $messages = [], array $context = []): self
95+
{
96+
return new self(VoterInterface::ACCESS_DENIED, $messages, $context);
97+
}
98+
99+
/**
100+
* @param string|string[] $messages
101+
*/
102+
public function setMessages(string|array $messages)
103+
{
104+
$this->messages = (array) $messages;
105+
foreach ($this->messages as $message) {
106+
if (!\is_string($message)) {
107+
throw new InvalidArgumentException(sprintf('Message must be string, "%s" given.', get_debug_type($message)));
108+
}
109+
}
110+
}
111+
112+
public function addMessage(string $message)
73113
{
74-
return new self(VoterInterface::ACCESS_DENIED, $message, $context);
114+
$this->messages[] = $message;
75115
}
76116

77-
public function setMessage(string $message)
117+
/**
118+
* @return string[]
119+
*/
120+
public function getMessages(): array
78121
{
79-
$this->message = $message;
122+
return $this->messages;
80123
}
81124

82125
public function getMessage(): string
83126
{
84-
return $this->message;
127+
return implode(', ', $this->messages);
85128
}
86129

87130
public function getContext(): array

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
+18-8Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ public function getVote(TokenInterface $token, mixed $subject, array $attributes
4343
}
4444

4545
// as soon as at least one attribute is supported, default is to deny access
46-
$vote = $this->deny();
46+
if (!$vote->isDenied()) {
47+
$vote = $this->deny();
48+
}
4749

4850
$decision = $this->voteOnAttribute($attribute, $subject, $token);
4951
if (\is_bool($decision)) {
@@ -56,7 +58,9 @@ public function getVote(TokenInterface $token, mixed $subject, array $attributes
5658
return $decision;
5759
}
5860

59-
$vote->setMessage($vote->getMessage().trim(' '.$decision->getMessage()));
61+
if ('' !== $decision->getMessage()) {
62+
$vote->addMessage($decision->getMessage());
63+
}
6064
}
6165

6266
return $vote;
@@ -71,26 +75,32 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes):
7175

7276
/**
7377
* Creates a granted vote.
78+
*
79+
* @param string|string[] $messages
7480
*/
75-
protected function grant(string $message = '', array $context = []): Vote
81+
protected function grant(string|array $messages = [], array $context = []): Vote
7682
{
77-
return Vote::createGranted($message, $context);
83+
return Vote::createGranted($messages, $context);
7884
}
7985

8086
/**
8187
* Creates an abstained vote.
88+
*
89+
* @param string|string[] $messages
8290
*/
83-
protected function abstain(string $message = '', array $context = []): Vote
91+
protected function abstain(string|array $messages = [], array $context = []): Vote
8492
{
85-
return Vote::createAbstain($message, $context);
93+
return Vote::createAbstain($messages, $context);
8694
}
8795

8896
/**
8997
* Creates a denied vote.
98+
*
99+
* @param string|string[] $messages
90100
*/
91-
protected function deny(string $message = '', array $context = []): Vote
101+
protected function deny(string|array $messages = [], array $context = []): Vote
92102
{
93-
return Vote::createDenied($message, $context);
103+
return Vote::createDenied($messages, $context);
94104
}
95105

96106
/**
+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\Tests\Authorization\Voter;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
16+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
17+
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
18+
19+
class VoteTest extends TestCase
20+
{
21+
public function testMessages()
22+
{
23+
$vote = new Vote(VoterInterface::ACCESS_GRANTED, 'foo');
24+
25+
$this->assertSame('foo', $vote->getMessage());
26+
27+
$vote->addMessage('bar');
28+
$this->assertSame('foo, bar', $vote->getMessage());
29+
}
30+
31+
public function testMessagesWithNotString()
32+
{
33+
$this->expectException(InvalidArgumentException::class);
34+
$this->expectExceptionMessage('Message must be string, "bool" given.');
35+
36+
new Vote(VoterInterface::ACCESS_GRANTED, ['foo', true]);
37+
}
38+
}

‎src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php
+9-2Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,17 @@ public function testVoteLegacy(VoterInterface $voter, array $attributes, $expect
110110
public function testVoteMessage()
111111
{
112112
$voter = new IntegerVoterVoteTest_Voter();
113-
$vote = $voter->getVote($this->token, new \stdClass(), [43, 44]);
113+
$vote = $voter->getVote($this->token, new \stdClass(), [43]);
114114
$this->assertSame('foobar message', $vote->getMessage());
115115
}
116116

117+
public function testVoteMessageMultipleAttributes()
118+
{
119+
$voter = new IntegerVoterVoteTest_Voter();
120+
$vote = $voter->getVote($this->token, new \stdClass(), [43, 44]);
121+
$this->assertSame('foobar message, foobar message', $vote->getMessage());
122+
}
123+
117124
public function testGetVoteWithTypeError()
118125
{
119126
$this->expectException(\TypeError::class);
@@ -183,7 +190,7 @@ protected function voteOnAttribute($attribute, $object, TokenInterface $token):
183190
return Vote::createGranted();
184191
}
185192

186-
return Vote::createDenied(' foobar message ');
193+
return Vote::createDenied('foobar message');
187194
}
188195

189196
protected function supports($attribute, $object): bool

0 commit comments

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