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 ton 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 tu souhaite 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 rôles. 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
2 novembre 2019 à 10h03
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
24 août 2017 à 18h15
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 !!
28 août 2017 à 8h52
@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.
3 novembre 2019 à 1h12
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.
14 avril 2016 à 10h46
tip top merci !
31 mars 2016 à 12h37
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
5 février 2016 à 12h07
Nice tip 😉
2 novembre 2015 à 1h41
Super !! un article bien utile et clair.
Merci
17 septembre 2015 à 9h29
C’est parfait ! Merci pour l’astuce !
22 août 2015 à 22h27
Pile ce que je cherchais.
Explication claire : merci bcp!!
5 mai 2015 à 0h05
This sounds like FOSUserBundle provide this feature: http://stackoverflow.com/a/24078528
Thank you for the tip