From 53018dabf9f7ee9bf21ef09bd0f078dd9b1e6cee Mon Sep 17 00:00:00 2001 From: MrYamous Date: Tue, 25 Feb 2025 20:45:57 +0100 Subject: [PATCH 1/4] [Security] Add ability for voters to explain their vote --- security.rst | 8 +++++++- security/voters.rst | 22 ++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/security.rst b/security.rst index ca910765be0..82f664202c4 100644 --- a/security.rst +++ b/security.rst @@ -2704,13 +2704,14 @@ anonymous users access by checking if there is no user set on the token:: // ... use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\User\UserInterface; + use Symfony\Component\Security\Core\Authorization\Voter\Vote; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter { // ... - protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool { // ... @@ -2722,6 +2723,11 @@ anonymous users access by checking if there is no user set on the token:: } } +.. versionnadded:: 7.3 + + The vote parameter in the :method:`Symfony\Component\Security\Core\Authorization\Voter\VoterInterface::voteOnAttribute` method + voteOnAttribute method was introduced in Symfony 7.3. + Setting Individual User Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/security/voters.rst b/security/voters.rst index e7452fadf99..6dd8477bedb 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -40,14 +40,20 @@ or extend :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\Vote which makes creating a voter even easier:: use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Authorization\Voter\Vote; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; abstract class Voter implements VoterInterface { abstract protected function supports(string $attribute, mixed $subject): bool; - abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool; + abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool; } +.. versionnadded:: 7.3 + + The vote parameter in the :method:`Symfony\Component\Security\Core\Authorization\Voter\VoterInterface::voteOnAttribute` method + voteOnAttribute method was introduced in Symfony 7.3. + .. _how-to-use-the-voter-in-a-controller: .. tip:: @@ -140,6 +146,7 @@ would look like this:: use App\Entity\Post; use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Authorization\Voter\Vote; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter @@ -163,12 +170,14 @@ would look like this:: return true; } - protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool + protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool { $user = $token->getUser(); + $vote ??= new Vote(); if (!$user instanceof User) { // the user must be logged in; if not, deny access + $vote->reasons[] = 'The user is not logged in.'; return false; } @@ -215,11 +224,12 @@ To recap, here's what's expected from the two abstract methods: return ``true`` if the attribute is ``view`` or ``edit`` and if the object is a ``Post`` instance. -``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token)`` +``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null)`` If you return ``true`` from ``supports()``, then this method is called. Your job is to return ``true`` to allow access and ``false`` to deny access. - The ``$token`` can be used to find the current user object (if any). In this - example, all of the complex business logic is included to determine access. + The ``$token`` can be used to find the current user object (if any). The ``$vote`` + argument can be used to add a reason to the vote. In this example, all of the + complex business logic is included to determine access. .. _declaring-the-voter-as-a-service: @@ -256,7 +266,7 @@ with ``ROLE_SUPER_ADMIN``:: ) { } - protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token): bool + protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool { // ... From 14ff672a13ff4c71f27a3a80fa8a6104f051a979 Mon Sep 17 00:00:00 2001 From: MrYamous Date: Tue, 25 Feb 2025 21:18:30 +0100 Subject: [PATCH 2/4] [Security] Add ability for voters to explain their vote --- security.rst | 4 ++-- security/voters.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/security.rst b/security.rst index 82f664202c4..49e05905a7c 100644 --- a/security.rst +++ b/security.rst @@ -2723,9 +2723,9 @@ anonymous users access by checking if there is no user set on the token:: } } -.. versionnadded:: 7.3 +.. versionadded:: 7.3 - The vote parameter in the :method:`Symfony\Component\Security\Core\Authorization\Voter\VoterInterface::voteOnAttribute` method + The vote parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method voteOnAttribute method was introduced in Symfony 7.3. Setting Individual User Permissions diff --git a/security/voters.rst b/security/voters.rst index 6dd8477bedb..e15ed6b331a 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -49,9 +49,9 @@ which makes creating a voter even easier:: abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool; } -.. versionnadded:: 7.3 +.. versionadded:: 7.3 - The vote parameter in the :method:`Symfony\Component\Security\Core\Authorization\Voter\VoterInterface::voteOnAttribute` method + The vote parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method voteOnAttribute method was introduced in Symfony 7.3. .. _how-to-use-the-voter-in-a-controller: From 4d63f1afbe8e39f0f156ea59ce4f301243cc756e Mon Sep 17 00:00:00 2001 From: MrYamous Date: Sun, 2 Mar 2025 16:13:49 +0100 Subject: [PATCH 3/4] update from reviews --- security.rst | 2 +- security/voters.rst | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/security.rst b/security.rst index 49e05905a7c..0e58fdf2180 100644 --- a/security.rst +++ b/security.rst @@ -2726,7 +2726,7 @@ anonymous users access by checking if there is no user set on the token:: .. versionadded:: 7.3 The vote parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method - voteOnAttribute method was introduced in Symfony 7.3. + was introduced in Symfony 7.3. Setting Individual User Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/security/voters.rst b/security/voters.rst index e15ed6b331a..1d07aa22cbc 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -52,7 +52,7 @@ which makes creating a voter even easier:: .. versionadded:: 7.3 The vote parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method - voteOnAttribute method was introduced in Symfony 7.3. + was introduced in Symfony 7.3. .. _how-to-use-the-voter-in-a-controller: @@ -206,7 +206,12 @@ would look like this:: private function canEdit(Post $post, User $user): bool { // this assumes that the Post object has a `getOwner()` method - return $user === $post->getOwner(); + if ($user === $post->getOwner()) { + return true; + } + + $vote->reasons[] = 'You are not the owner of the Post.'; + return false; } } From d062c608afe355516e28eab810f6ad68f33e5baf Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Wed, 19 Mar 2025 16:06:52 +0100 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Oskar Stark --- security.rst | 2 +- security/voters.rst | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/security.rst b/security.rst index 0e58fdf2180..5a6fc15ae2e 100644 --- a/security.rst +++ b/security.rst @@ -2725,7 +2725,7 @@ anonymous users access by checking if there is no user set on the token:: .. versionadded:: 7.3 - The vote parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method + The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method was introduced in Symfony 7.3. Setting Individual User Permissions diff --git a/security/voters.rst b/security/voters.rst index 1d07aa22cbc..ba4c96fe6fd 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -51,7 +51,7 @@ which makes creating a voter even easier:: .. versionadded:: 7.3 - The vote parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method + The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method was introduced in Symfony 7.3. .. _how-to-use-the-voter-in-a-controller: @@ -211,6 +211,7 @@ would look like this:: } $vote->reasons[] = 'You are not the owner of the Post.'; + return false; } }