Skip to content

Commit 203c0f8

Browse files
MrYamousjaviereguiluz
authored andcommitted
[Security] Add ability for voters to explain their vote
1 parent 15cddbe commit 203c0f8

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

security.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2704,13 +2704,14 @@ anonymous users access by checking if there is no user set on the token::
27042704
// ...
27052705
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
27062706
use Symfony\Component\Security\Core\Authentication\User\UserInterface;
2707+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
27072708
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
27082709

27092710
class PostVoter extends Voter
27102711
{
27112712
// ...
27122713

2713-
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
2714+
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
27142715
{
27152716
// ...
27162717

@@ -2722,6 +2723,11 @@ anonymous users access by checking if there is no user set on the token::
27222723
}
27232724
}
27242725

2726+
.. versionadded:: 7.3
2727+
2728+
The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method
2729+
was introduced in Symfony 7.3.
2730+
27252731
Setting Individual User Permissions
27262732
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27272733

security/voters.rst

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,20 @@ or extend :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\Vote
4040
which makes creating a voter even easier::
4141

4242
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
43+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
4344
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
4445

4546
abstract class Voter implements VoterInterface
4647
{
4748
abstract protected function supports(string $attribute, mixed $subject): bool;
48-
abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool;
49+
abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool;
4950
}
5051

52+
.. versionadded:: 7.3
53+
54+
The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method
55+
was introduced in Symfony 7.3.
56+
5157
.. _how-to-use-the-voter-in-a-controller:
5258

5359
.. tip::
@@ -140,6 +146,7 @@ would look like this::
140146
use App\Entity\Post;
141147
use App\Entity\User;
142148
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
149+
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
143150
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
144151

145152
class PostVoter extends Voter
@@ -163,12 +170,14 @@ would look like this::
163170
return true;
164171
}
165172

166-
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
173+
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
167174
{
168175
$user = $token->getUser();
176+
$vote ??= new Vote();
169177

170178
if (!$user instanceof User) {
171179
// the user must be logged in; if not, deny access
180+
$vote->reasons[] = 'The user is not logged in.';
172181
return false;
173182
}
174183

@@ -197,7 +206,13 @@ would look like this::
197206
private function canEdit(Post $post, User $user): bool
198207
{
199208
// this assumes that the Post object has a `getOwner()` method
200-
return $user === $post->getOwner();
209+
if ($user === $post->getOwner()) {
210+
return true;
211+
}
212+
213+
$vote->reasons[] = 'You are not the owner of the Post.';
214+
215+
return false;
201216
}
202217
}
203218

@@ -215,11 +230,12 @@ To recap, here's what's expected from the two abstract methods:
215230
return ``true`` if the attribute is ``view`` or ``edit`` and if the object is
216231
a ``Post`` instance.
217232

218-
``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token)``
233+
``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null)``
219234
If you return ``true`` from ``supports()``, then this method is called. Your
220235
job is to return ``true`` to allow access and ``false`` to deny access.
221-
The ``$token`` can be used to find the current user object (if any). In this
222-
example, all of the complex business logic is included to determine access.
236+
The ``$token`` can be used to find the current user object (if any). The ``$vote``
237+
argument can be used to add a reason to the vote. In this example, all of the
238+
complex business logic is included to determine access.
223239

224240
.. _declaring-the-voter-as-a-service:
225241

@@ -256,7 +272,7 @@ with ``ROLE_SUPER_ADMIN``::
256272
) {
257273
}
258274

259-
protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token): bool
275+
protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
260276
{
261277
// ...
262278

0 commit comments

Comments
 (0)