@@ -66,43 +66,72 @@ Setup: Checking for Access in a Controller
6666
6767Suppose you have a ``Post `` object and you need to decide whether or not the current
6868user can *edit * or *view * the object. In your controller, you'll check access with
69- code like this::
69+ code like this:
7070
71- // src/Controller/PostController.php
71+ .. configuration-block ::
7272
73- // ...
74- class PostController extends AbstractController
75- {
76- #[Route('/posts/{id}', name: 'post_show')]
77- public function show($id): Response
78- {
79- // get a Post object - e.g. query for it
80- $post = ...;
73+ ..code-block ::php-attributes
8174
75+ // src/Controller/PostController.php
76+
77+ // ...
78+ use Symfony\Component\Security\Http\Attribute\IsGranted;
79+
80+ class PostController extends AbstractController
81+ {
82+ #[Route('/posts/{id}', name: 'post_show')]
8283 // check for "view" access: calls all voters
83- $this->denyAccessUnlessGranted('view', $post);
84+ #[IsGranted('show', 'post')]
85+ public function show(Post $post): Response
86+ {
87+ // ...
88+ }
8489
85- // ...
90+ #[Route('/posts/{id}/edit', name: 'post_edit')]
91+ // check for "edit" access: calls all voters
92+ #[IsGranted('edit', 'post')]
93+ public function edit(Post $post): Response
94+ {
95+ // ...
96+ }
8697 }
8798
88- #[Route('/posts/{id}/edit', name: 'post_edit')]
89- public function edit($id): Response
99+ ..code-block ::php
100+
101+ // src/Controller/PostController.php
102+
103+ // ...
104+
105+ class PostController extends AbstractController
90106 {
91- // get a Post object - e.g. query for it
92- $post = ...;
107+ #[Route('/posts/{id}', name: 'post_show')]
108+ public function show(Post $post): Response
109+ {
110+ // check for "view" access: calls all voters
111+ $this->denyAccessUnlessGranted('view', $post);
93112
94- // check for "edit" access: calls all voters
95- $this->denyAccessUnlessGranted('edit', $post);
113+ // ...
114+ }
96115
97- // ...
116+ #[Route('/posts/{id}/edit', name: 'post_edit')]
117+ public function edit(Post $post): Response
118+ {
119+ // check for "edit" access: calls all voters
120+ $this->denyAccessUnlessGranted('edit', $post);
121+
122+ // ...
123+ }
98124 }
99- }
100125
101- The ``denyAccessUnlessGranted() `` method (and also the ``isGranted() `` method)
126+ The ``#[IsGranted()] `` attribute or `` denyAccessUnlessGranted() `` method (and also the ``isGranted() `` method)
102127calls out to the "voter" system. Right now, no voters will vote on whether or not
103128the user can "view" or "edit" a ``Post ``. But you can create your *own * voter that
104129decides this using whatever logic you want.
105130
131+ ..versionadded ::6.2
132+
133+ The ``#[IsGranted()] `` attribute was introduced in Symfony 6.2.
134+
106135Creating the custom Voter
107136-------------------------
108137
@@ -423,3 +452,35 @@ must implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\Ac
423452 // ...
424453 ;
425454 };
455+
456+ .. _security-voters-change-message-and-status-code :
457+
458+ Changing the message and status code returned
459+ ---------------------------------------------
460+
461+ By default, the ``#[IsGranted] `` attribute will throw a
462+ :class: `Symfony\\ Component\\ Security\\ Core\\ Exception\\ AccessDeniedException `
463+ and return an http **403 ** status code with **Access Denied ** as message.
464+
465+ However, you can change this behavior by specifying the message and status code returned::
466+
467+ // src/Controller/PostController.php
468+
469+ // ...
470+ use Symfony\Component\Security\Http\Attribute\IsGranted;
471+
472+ class PostController extends AbstractController
473+ {
474+ #[Route('/posts/{id}', name: 'post_show')]
475+ #[IsGranted('show', 'post', 'Post not found', 404)]
476+ public function show(Post $post): Response
477+ {
478+ // ...
479+ }
480+ }
481+
482+ ..tip ::
483+
484+ If the status code is different than 403, a
485+ :class: `Symfony\\ Component\\ HttpKernel\\ Exception\\ HttpException `
486+ will be throw instead.