Symfony 5 : Utiliser la fonction « isGranted » sur n’importe quel objet utilisateur

Cet article est écrit pour la version 5 de Symfony mais à priori il devrait aussi fonctionner au moins pour les version 3 et 4 du framework

Sous Symfony la fonction permettant de savoir si l’utilisateur en train de parcourir l’application possède un certain rôle est « isGranted » elle est directement disponible à l’utilisation dans les templates twig, via les annotations, depuis le services « Symfony\Component\Security\Core\Security » ou directement dans les controllers de la façon suivante :

if ($this->isGranted('ROLE_ADMIN')) {
   //l'utilisateur à les droits admin
}

Par contre il n’existe pas de façon directe d’utiliser cette fonction sur un objet utilisateur différent de l’utilisateur courant.
Étant donnée le fonctionnement des rôles sous symfony, qui peuvent hériter des droits d’un autre rôle il ne faut pas utiliser un simple

if(in_array('ROLE_ADMIN', $user->getRoles())){
   //mauvaise pratique
}

En effet dans ce cas, si un utilisateur à le rôle ‘ROLE_SUPERADMIN’ qui hérite de ‘ROLE_ADMIN’ le contrôle ci dessus ne lui accordera pas les droits.
On imagine ici la hiérarchie des rôle suivante :

role_hierarchy:
    ROLE_ADMIN
: ROLE_USER
    ROLE_SUPERADMIN
: ROLE_ADMIN

Pour résoudre ce problème, nous allons créer la fonction « isGranted » prenant en paramètre un utilisateur et le rôle à vérifier dans un service « Securizer », je vous le livre directement prêt à utiliser :

namespace App\Service;

use App\Entity\User;//l'entité user de notre aplication
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;

class Securizer {

    private $accessDecisionManager;

    public function __construct(AccessDecisionManagerInterface $accessDecisionManager) {
        $this->accessDecisionManager = $accessDecisionManager;
    }

    public function isGranted(User $user, $attribute, $object = null) {
        $token = new UsernamePasswordToken($user, 'none', 'none', $user->getRoles());
        return ($this->accessDecisionManager->decide($token, [$attribute], $object));
    }

}

Grâce à la configuration par défaut de Symfony on peux sans plus attendre utiliser ce service dans notre application et vérifier correctement les droits de n’importe quel utilisateur en prenant bien en compte la hiérarchie des rôles configuré dans le fichier « security.yaml ». Par exemple dans un controller on peux désormais faire :

public function monController(App\Service\SecurizerSecurizer $securizer) {
   //récupération d'un utilisateur $user de n'importe quelle manière, par exemple via une requête en base de données.
   //verification des droits
   if($securizer->isGranted($user, 'ROLE_ADMIN')){
       //l'utilisateur à les droits admin
   }
}

Il y’a sans doute de nombreux cas ou ce besoin de contrôle des rôles sur un utilisateur autre que celui connecté se fait sentir (d’où la mise en place de cette fonctionnalité sous la forme d’un service), particulièrement lors de l’utilisation de Voters pour sécuriser la gestion des droits de l’application. Dans mon cas j’ai d’abord eu besoin de cette fonctionnalité pour contrôler finement la possibilité de se « connecter en tant que » (switch_user) pour mes administrateurs.
Voir les détails dans le prochain article : Limiter la possibilité de « switch_user » en fonction du rôle de l’utilisateur cible.

Source

1 réflexion sur « Symfony 5 : Utiliser la fonction « isGranted » sur n’importe quel objet utilisateur »

  1. Karrimor

    Bonjour,
    Je souhaiterais utiliser votre code cependant j’ai l’erreur suivante :
    « Catchable Fatal Error: Argument 1 passed to App\UserBundle\Service\Securizer::__construct() must implement interface Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface, none given, called in […] »

    Ce qui est logique puisque je ne lui donne aucun argument:
    services:
    App.user.securizer:
    class: App\UserBundle\Service\Securizer
    arguments: []*

    Quel argument dois-je fournir pour que cela puisse fonctionner ?
    D’avance merci

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.