diff --git a/doctrine/events.rst b/doctrine/events.rst index 8769c44211d..65f48d46047 100644 --- a/doctrine/events.rst +++ b/doctrine/events.rst @@ -164,7 +164,7 @@ listener in the Symfony application by creating a new service for it and .. configuration-block:: - .. code-block:: attribute + .. code-block:: php-attributes // src/App/EventListener/SearchIndexer.php namespace App\EventListener; diff --git a/security.rst b/security.rst index 48f1915b70a..77efcdb37b0 100644 --- a/security.rst +++ b/security.rst @@ -2288,7 +2288,7 @@ will happen: .. _security-securing-controller-annotations: -Another way to secure one or more controller actions is to use an attribute. +Another way to secure one or more controller actions is to use the ``#[IsGranted()]`` attribute. In the following example, all controller actions will require the ``ROLE_ADMIN`` permission, except for ``adminDashboard()``, which will require the ``ROLE_SUPER_ADMIN`` permission: diff --git a/security/voters.rst b/security/voters.rst index 0755e17f39d..45b8c196507 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -66,43 +66,72 @@ Setup: Checking for Access in a Controller Suppose you have a ``Post`` object and you need to decide whether or not the current user can *edit* or *view* the object. In your controller, you'll check access with -code like this:: +code like this: - // src/Controller/PostController.php +.. configuration-block:: - // ... - class PostController extends AbstractController - { - #[Route('/posts/{id}', name: 'post_show')] - public function show($id): Response - { - // get a Post object - e.g. query for it - $post = ...; + .. code-block:: php-attributes + // src/Controller/PostController.php + + // ... + use Symfony\Component\Security\Http\Attribute\IsGranted; + + class PostController extends AbstractController + { + #[Route('/posts/{id}', name: 'post_show')] // check for "view" access: calls all voters - $this->denyAccessUnlessGranted('view', $post); + #[IsGranted('show', 'post')] + public function show(Post $post): Response + { + // ... + } - // ... + #[Route('/posts/{id}/edit', name: 'post_edit')] + // check for "edit" access: calls all voters + #[IsGranted('edit', 'post')] + public function edit(Post $post): Response + { + // ... + } } - #[Route('/posts/{id}/edit', name: 'post_edit')] - public function edit($id): Response + .. code-block:: php + + // src/Controller/PostController.php + + // ... + + class PostController extends AbstractController { - // get a Post object - e.g. query for it - $post = ...; + #[Route('/posts/{id}', name: 'post_show')] + public function show(Post $post): Response + { + // check for "view" access: calls all voters + $this->denyAccessUnlessGranted('view', $post); - // check for "edit" access: calls all voters - $this->denyAccessUnlessGranted('edit', $post); + // ... + } - // ... + #[Route('/posts/{id}/edit', name: 'post_edit')] + public function edit(Post $post): Response + { + // check for "edit" access: calls all voters + $this->denyAccessUnlessGranted('edit', $post); + + // ... + } } - } -The ``denyAccessUnlessGranted()`` method (and also the ``isGranted()`` method) +The ``#[IsGranted()]`` attribute or ``denyAccessUnlessGranted()`` method (and also the ``isGranted()`` method) calls out to the "voter" system. Right now, no voters will vote on whether or not the user can "view" or "edit" a ``Post``. But you can create your *own* voter that decides this using whatever logic you want. +.. versionadded:: 6.2 + + The ``#[IsGranted()]`` attribute was introduced in Symfony 6.2. + Creating the custom Voter ------------------------- @@ -423,3 +452,35 @@ must implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\Ac // ... ; }; + +.. _security-voters-change-message-and-status-code: + +Changing the message and status code returned +--------------------------------------------- + +By default, the ``#[IsGranted]`` attribute will throw a +:class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` +and return an http **403** status code with **Access Denied** as message. + +However, you can change this behavior by specifying the message and status code returned:: + + // src/Controller/PostController.php + + // ... + use Symfony\Component\Security\Http\Attribute\IsGranted; + + class PostController extends AbstractController + { + #[Route('/posts/{id}', name: 'post_show')] + #[IsGranted('show', 'post', 'Post not found', 404)] + public function show(Post $post): Response + { + // ... + } + } + +.. tip:: + + If the status code is different than 403, a + :class:`Symfony\\Component\\HttpKernel\\Exception\\HttpException` + will be throw instead.