Skip to content

Commit 5fb388e

Browse files
committed
Merge branch '7.4' into 8.0
* 7.4: [Security] Update the main voters article
2 parents b10fcb0 + a1b2446 commit 5fb388e

File tree

1 file changed

+83
-40
lines changed

1 file changed

+83
-40
lines changed

security/voters.rst

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,6 @@ which makes creating a voter even easier::
5656

5757
.. _how-to-use-the-voter-in-a-controller:
5858

59-
.. tip::
60-
61-
Checking each voter several times can be time consuming for applications
62-
that perform a lot of permission checks. To improve performance in those cases,
63-
you can make your voters implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\CacheableVoterInterface`.
64-
This allows the access decision manager to remember the attribute and type
65-
of subject supported by the voter, to only call the needed voters each time.
66-
6759
Setup: Checking for Access in a Controller
6860
------------------------------------------
6961

@@ -310,6 +302,89 @@ If you're using the :ref:`default services.yaml configuration <service-container
310302
you're done! Symfony will automatically pass the ``security.helper``
311303
service when instantiating your voter (thanks to autowiring).
312304

305+
Improving Voter Performance
306+
---------------------------
307+
308+
If your application defines many voters and checks permissions on many objects
309+
during a single request, this can impact performance. Most of the time, voters
310+
only care about specific permissions (attributes), such as ``EDIT_BLOG_POST``,
311+
or specific object types, such as ``User`` or ``Invoice``. That's why Symfony
312+
can cache the voter resolution (i.e. the decision to apply or skip a voter for
313+
a given attribute or object).
314+
315+
To enable this optimization, make your voter implement
316+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\CacheableVoterInterface`.
317+
This is already the case when extending the abstract ``Voter`` class shown above.
318+
Then, override one or both of the following methods::
319+
320+
use App\Entity\Post;
321+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
322+
// ...
323+
324+
class PostVoter extends Voter
325+
{
326+
const VIEW = 'view';
327+
const EDIT = 'edit';
328+
329+
protected function supports(string $attribute, mixed $subject): bool
330+
{
331+
// ...
332+
}
333+
334+
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
335+
{
336+
// ...
337+
}
338+
339+
// this method returns true if the voter applies to the given attribute;
340+
// if it returns false, Symfony won't call it again for this attribute
341+
public function supportsAttribute(string $attribute): bool
342+
{
343+
return in_array($attribute, [self::VIEW, self::EDIT], true);
344+
}
345+
346+
// this method returns true if the voter applies to the given object class/type;
347+
// if it returns false, Symfony won't call it again for that type of object
348+
public function supportsAttribute(string $attribute): bool
349+
{
350+
// you can't use a simple Post::class === $subjectType comparison
351+
// because the subject type might be a Doctrine proxy class
352+
return is_a($subjectType, Post::class, true);
353+
}
354+
}
355+
356+
.. _security-voters-change-message-and-status-code:
357+
358+
Changing the message and status code returned
359+
---------------------------------------------
360+
361+
By default, the ``#[IsGranted]`` attribute will throw a
362+
:class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`
363+
and return an http **403** status code with **Access Denied** as message.
364+
365+
However, you can change this behavior by specifying the message and status code returned::
366+
367+
// src/Controller/PostController.php
368+
369+
// ...
370+
use Symfony\Component\Security\Http\Attribute\IsGranted;
371+
372+
class PostController extends AbstractController
373+
{
374+
#[Route('/posts/{id}', name: 'post_show')]
375+
#[IsGranted('show', 'post', 'Post not found', 404)]
376+
public function show(Post $post): Response
377+
{
378+
// ...
379+
}
380+
}
381+
382+
.. tip::
383+
384+
If the status code is different than 403, an
385+
:class:`Symfony\\Component\\HttpKernel\\Exception\\HttpException`
386+
will be thrown instead.
387+
313388
.. _security-voters-change-strategy:
314389

315390
Changing the Access Decision Strategy
@@ -481,35 +556,3 @@ must implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\Ac
481556
// ...
482557
;
483558
};
484-
485-
.. _security-voters-change-message-and-status-code:
486-
487-
Changing the message and status code returned
488-
---------------------------------------------
489-
490-
By default, the ``#[IsGranted]`` attribute will throw a
491-
:class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`
492-
and return an http **403** status code with **Access Denied** as message.
493-
494-
However, you can change this behavior by specifying the message and status code returned::
495-
496-
// src/Controller/PostController.php
497-
498-
// ...
499-
use Symfony\Component\Security\Http\Attribute\IsGranted;
500-
501-
class PostController extends AbstractController
502-
{
503-
#[Route('/posts/{id}', name: 'post_show')]
504-
#[IsGranted('show', 'post', 'Post not found', 404)]
505-
public function show(Post $post): Response
506-
{
507-
// ...
508-
}
509-
}
510-
511-
.. tip::
512-
513-
If the status code is different than 403, an
514-
:class:`Symfony\\Component\\HttpKernel\\Exception\\HttpException`
515-
will be thrown instead.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy