Symfony : Comment vérifier le rôle d’un utilisateur en respectant la hiérarchie des rôles
Imaginons une application dans laquelle on retrouve les rôles suivants :
- ROLE_SUPER_ADMIN
- ROLE_ADMIN
- ROLE_CLIENT
- ROLE_USER
Ainsi que cette hiérarchie :
- ROLE_SUPER_ADMIN a aussi le rôle ROLE_ADMIN qui a aussi le rôle ROLE_USER.
- ROLE_CLIENT a aussi le rôle ROLE_USER.
Dans votre fichier security.yml, on le définirait ainsi :
role_hierarchy: ROLE_CLIENT: ROLE_USER ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN]
La problématique
Imaginons que vous souhaitiez vérifier qu’un utilisateur a bien le ROLE_ADMIN. Très facile :
$this->container->get('security.authorization_checker')->isGranted('ROLE_ADMIN')
Ok mais cette fonction permet de vérifier le rôle de l’utilisateur actuellement connecté qui fait la demande.
Maintenant, comment faire pour vérifier qu’un autre utilisateur a bien le ROLE_ADMIN ? (Exemple, afficher une liste d’utilisateurs en affichant si oui ou non il est admin).
On pense tout de suite à :
if ($user->hasRole('ROLE_ADMIN'))
Mais un utilisateur SUPER_ADMIN retournerai « false » à cette fonction car la fonction hasRole ne vérifie pas la hiérarchie des rôles.
C’est le même problème si l’on veut vérifier que l’utilisateur a bien le ROLE_USER alors qu’il est admin ou client.
Résolution du problème
On peut utiliser un service présent dans le core de Symfony dans un controller :
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; class UserController extends AbstractController { /** * @Route("/{user}", name="app_user_view", requirements={"user"="\d+"}) */ public function view(AccessDecisionManagerInterface $accessDecisionManager, User $user): Response { $token = new UsernamePasswordToken($user, 'none', 'none', $user->getRoles()); if ($accessDecisionManager->decide($token, 'ROLE_ADMIN')) { // L'utilisateur $user a le rôle ROLE_ADMIN } ...
Le service AccessDecisionManager permet de vérifier si l’utilisateur a bien les droits tout en vérifiant la hiérarchie des roles. Ce service permet aussi de vérifier les droits selon des règles spécifiques définies par les Voter.
Créer un service dédié à la vérification des droits
<?php namespace App\Services; use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; class GrantedService { private $accessDecisionManager; /** * Constructor * * @param AccessDecisionManagerInterface $accessDecisionManager */ public function __construct(AccessDecisionManagerInterface $accessDecisionManager) { $this->accessDecisionManager = $accessDecisionManager; } public function isGranted(User $user, $attributes, $object = null) { if (!is_array($attributes)) $attributes = [$attributes]; $token = new UsernamePasswordToken($user, 'none', 'none', $user->getRoles()); return ($this->accessDecisionManager->decide($token, $attributes, $object)); } }
C’est tout, cette fonction fonctionne maintenant exactement comme la fonction isGranted disponible de base dans les controller sauf qu’il faut spécifier l’utilisateur à checker :
class UserController extends AbstractController { /** * @Route("/{user}", name="app_user_view", requirements={"user"="\d+"}) */ public function view(GrantedService $grantedService, User $user): Response { if ($grantedService->isGranted($user 'ROLE_ADMIN')) { // L'utilisateur $user a le rôle ROLE_ADMIN } ...
C’est tout pour aujourd’hui ! N’hésitez pas à poser vos questions dans les commentaires et à partager cet article ! Merci
Commentaire (11)
mickaelandrieu| 5 mai 2015
This sounds like FOSUserBundle provide this feature: http://stackoverflow.com/a/24078528
Thank you for the tip
reg| 22 août 2015
Pile ce que je cherchais.
Explication claire : merci bcp!!
Leenzur| 17 septembre 2015
C’est parfait ! Merci pour l’astuce !
misterhobbes44| 2 novembre 2015
Super !! un article bien utile et clair.
Merci
adrieng| 5 février 2016
Nice tip 😉
Sébastien| 31 mars 2016
Bravo pour la clarté des explications !
On peut aussi utiliser cette méthode: http://stackoverflow.com/questions/11288293/how-to-use-the-accessdecisionmanager-in-symfony2-for-authorization-of-arbitrary/22380765#22380765
pico34| 14 avril 2016
tip top merci !
lionel| 24 août 2017
Pas besoin d’un service pour si peut :
if( $user->isGranted(‘ROLE_QUE_TU_VEUX’) ) echo ‘Blaaaa !’;
Avant de créer un service, assurez-vous qu’il n’y a vraiment pas moyen de faire plus simple !!
Rémi| 28 août 2017
@lionel, par la méthode que vous utilisez, vous ne pouvez pas vérifier la hiérarchie des rôles, ce qui est le but de mon article. D’où l’utilisation d’un service.
Barbapapa| 3 novembre 2019
UserInterface ne fourni pas de méthode isGranted(). Cette méthode est éventuellement fournie par AbstractController, mais utilise toujours l’utilisateur connecté, ce qui ne résout pas le problème.
La création d’un service à donc toute son utilité.
Merci pour cet article très utile.
vincent| 2 novembre 2019
Bonjour à tous,
Je suis dév sur symfony est il y a une autre méthode qui me semble tout aussi simple : les voters
php bin/console make:voter