Live Coding : Gérer les rôles utilisateur avec Symfony 4

Par Nouvelle-Techno.fr le 10 janvier 2020 - Catégories : MVC PHP Tutoriel Symfony Live-Coding

Lire l'article sur le site d'origine

Dans la plupart des sites, il est nécessaire de créer plusieurs rôles pour les utilisateurs afin de leur donner accès à différentes parties du site en fonction de leur profil.

Nous aurons donc assez régulièrement des profils du type :

Dans cet article, nous allons voir comment créer et utiliser ces rôles dans notre site réalisé avec Symfony 4.

Nous allons donc commencer par définir les différents types de profils dont nous aurons besoin, et leur donner une hiérarchie.

Ainsi, si un utilisateur a un rôle "supérieur" au rôle requis pour accéder à une fonctionnalité, il y aura accès sans pour autant avoir besoin qu'on lui attribue le rôle en question.

Dans notre exemple, nous aurons 3 rôles pour notre site :

Les utilisateurs pourront lire et commenter, les éditeurs pourront ajouter des articles et les administrateurs pourront gérer le site.

Déclarer les rôles

Nous allons déclarer les rôles que nous utiliserons, leur hiérarchie et les routes qui leur sont réservées dans le fichier "config/packages/security.yaml"

security:

    # ici se trouve votre contenu par défaut

    # Dans la partie access_control nous définissons les routes protégées
    access_control:
        - { path: ^/admin, roles: ROLE_ADMIN }
        - { path: ^/profil, roles: ROLE_USER }

    # La partie role_hierarchy contient la hiérarchie des rôles
    # Un éditeur sera également utilisateur
    # Un administrateur sera également éditeur et donc utilisateur
    role_hierarchy:
        ROLE_EDITOR: ROLE_USER
        ROLE_ADMIN: ROLE_EDITOR

Attribuer les rôles

Nous attribuons les rôles aux utilisateurs après leur inscription. Nous partirons du principe que seuls les administrateurs pourront modifier ces rôles.

1er utilisateur

ATTENTION, le 1er administrateur doit être configuré dans la base de données.

Il faudra acéder à PHPMyAdmin (ou tout autre outil de gestion de la base de données) et lui attribuer le rôle administrateur en modifiant son rôle en mettant ceci

[
    "ROLE_ADMIN"
]

Gestion des utilisateurs

Une fois le 1er administrateur créé, nous allons pouvoir créer nos pages de gestion des utilisateurs, qui permettront de modifier ou supprimer des utilisateurs.

Pour la gestion des utilisateurs, il est possible d'utiliser "Easy Admin", traité dans le tutoriel "Créer une interface d'administration". Dans cet exemple, nous allons créer les pages sans l'utiliser.

Création du contrôleur

Nous allons commencer par créer le contrôleur d'administration. Nous l'appellerons "AdminController". Saisir cette commande

php bin/console make:controller

Pour protéger la totalité des routes gérées par ce contrôleur, nous allons lui attribuer la route "/admin" en modifiant les annotations comme ceci

/**
 * @Route("/admin", name="admin_")
 */
class AdminController extends AbstractController
{
    /**
     * @Route("/", name="accueil")
     */
    public function index()
    {
        return $this->render('admin/index.html.twig', [
            'controller_name' => 'AdminController',
        ]);
    }
}

Lister les utilisateurs

Dans notre contrôleur d'administration, nous allons créer une méthode permettant de lister les utilisateurs inscrits ainsi que leurs rôles.

Cette méthode, que nous appellerons "usersList", par exemple, s'écrira comme suit

/**
 * @Route("/utilisateurs", name="utilisateurs")
 */
public function usersList(UsersRepository $users)
{
    return $this->render('admin/users.html.twig', [
        'users' => $users->findAll(),
    ]);
}

Il nous faut donc également créer un fichier twig, que nous stockerons dans le dossier "/templates/admin" et que nous appellerons "users.html.twig"

Ce fichier contiendra ce qui suit

{% extends 'base.html.twig' %}

{% block title %}Liste des utilisateurs{% endblock %}

{% block body %}
    <h1>Liste des utilisateurs</h1>
    <table>
        <thead>
            <th>ID</th>
            <th>Email</th>
            <th>Rôles</th>
            <th>Articles</th>
            <th>Actions</th>
        </thead>
        <tbody>
            {# On boucle sur les utilisateurs #}
            {% for user in users %}
                <tr>
                    <td>{{ user.id }}</td>
                    <td>{{ user.email }}</td>
                    <td>
                        {# On boucle sur les rôles #}
                        {% for role in user.roles %}
                            {% if role == "ROLE_USER" %}
                                Utilisateur
                                {% elseif role == "ROLE_EDITOR" %}
                                Editeur
                            {% elseif role == "ROLE_ADMIN" %}
                                Administrateur
                            {% endif %}
                        {% endfor %}
                    </td>
                    <td>{{ user.articles|length }}</td>
                    <td><a href="{{ path('admin_modifier_utilisateur', {'id': user.id}) }}">Modifier</a></td>
                </tr>
                
            {% else %}
                <p>Aucun utilisateur inscrit</p>
            {% endfor %}
        </tbody>
    </table>
{% endblock %}

A ce stade, nous aurons une erreur indiquant que la route "admin_modifier_utilisateur" n'a pas été trouvée. Nous allons donc la créer à l'étape suivante.

Modifier un utilisateur

Pour modifier un utilisateur, nous allons devoir créer un formulaire, une méthode dans notre contrôleur et un fichier Twig pour l'afficher.

Le formulaire sera créé au moyen de la commande

php bin/console make:form

Nous allons nommer notre formulaire "EditUserType". Il contiendra ce code par défaut.

<?php

namespace App\Form;

use App\Entity\Users;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class EditUserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email')
            ->add('roles')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Users::class,
        ]);
    }
}

Nous allons modifier le "builder" pour personnaliser l'affichage du formulaire :

Le code sera le suivant

$builder
    ->add('email', EmailType::class,[
        'constraints' => [
            new NotBlank([
                'message' => 'Merci d\'entrer un e-mail',
            ]),
        ],
        'required' => true,
        'attr' => ['class' =>'form-control'],
    ])
    ->add('roles', ChoiceType::class, [
        'choices' => [
            'Utilisateur' => 'ROLE_USER',
            'Editeur' => 'ROLE_EDITOR',
            'Administrateur' => 'ROLE_ADMIN'
        ],
        'expanded' => true,
        'multiple' => true,
        'label' => 'Rôles' 
    ])
    ->add('valider', SubmitType::class)
;

Avec ces nouvelles modifications, nous allons devoir ajouter des dépendances

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Validator\Constraints\NotBlank;

Dans notre contrôleur, la méthode doit récupérer l'information de l'utilisateur à modifier, créer le formulaire et le gérer si le formulaire est soumis.

Le code sera le suivant

/**
 * @Route("/utilisateurs/modifier/{id}", name="modifier_utilisateur")
 */
public function editUser(Users $user, Request $request)
{
    $form = $this->createForm(EditUserType::class, $user);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($user);
        $entityManager->flush();

        $this->addFlash('message', 'Utilisateur modifié avec succès');
        return $this->redirectToRoute('admin_utilisateurs');
    }
    
    return $this->render('admin/edituser.html.twig', [
        'userForm' => $form->createView(),
    ]);
}

Enfin, dans le fichier twig, que nous pourrons, par exemple, appeler "edituser.html.twig", nous allons appeler ce formulaire.

{% extends 'base.html.twig' %}

{% block title %}Modifier un utilisateur{% endblock %}

{% block body %}
    <h1>Modifier un utilisateur</h1>
    {{ form(userForm) }}
{% endblock %}

Protéger les routes

Une fois les rôles utilisateurs gérés, nous allons pouvoir donner des permissions d'accès à nos routes.

Il y a plusieurs façons de protéger les routes, les principales sont traitées dans les parties suivantes.

security.yaml

Au début de cet article, nous avons modifié le fichier "security.yaml" pour y intégrer la hiérarchie des rôles et certaines protections de routes.

Cette protection se trouve dans cette partie

access_control:
    - { path: ^/admin, roles: ROLE_ADMIN }
    - { path: ^/profil, roles: ROLE_USER }

Dans le code ci-dessus, nous imposons un rôle d'administrateur pour accéder à la route "/admin" et un rôle d'utilisateur pour accéder à la route "/profil"

Annotations

Il est également possible de protéger les routes par l'intermédiaire des annotations.

Dans un article précédent nous avons créé une route pour ajouter un article sur notre blog. Nous avions protégé cette route pour la réserver aux utilisateurs connectés en ajoutant cette annotation

/**
 * @IsGranted("ROLE_USER")
 * @Route("/article/ajouter", name="ajout_article")
 */

Si nous souhaitons réserver cette route aux éditeurs et administrateurs, nous allons modifier l'annotation de la façon suivante

/**
 * @IsGranted("ROLE_EDITOR")
 * @Route("/article/ajouter", name="ajout_article")
 */

Contrôleur

Enfin, une autre méthode commune de protection des routes est une méthode php dans le contrôleur.

Il est possible d'ajouter la ligne suivante où nous le souhaitons dans nos méthodes de contrôleurs

$this->denyAccessUnlessGranted('ROLE_ADMIN');

Dans cet exemple, l'utilisateur qui n'a pas le rôle administrateur se verra interdire l'accès.

Gérer l'affichage en fonction des rôles

Pour terminer, il est possible de gérer l'affichage dans nos fichiers twig en fonction du rôle des utilisateurs.

Ainsi, nous pourrons afficher certaines informations ou certains liens selon le contexte.

Vérifier si l'utilisateur est connecté

Certains contenus sont affichés si l'utilisateur est connecté, quel que soit son rôle.

Avec Symfony, tout utilisateur connecté aura le rôle "utilisateur" par défaut.

De ce fait, nous pourrons vérifier si ce rôle existe dans notre fichier twig de cette façon

{% if is_granted('ROLE_USER') %}

    {# Ici le contenu réservé aux utilisateurs connectés #}

{% else %}

    {# Ici le contenu affiché aux utilisateurs non connectés #}

{% endif %}

Vérifier si l'utilisateur a un certain rôle

Certains contenus sont affichés uniquement si l'utilisateur a un certain rôle.

Nous pourrons utiliser la même méthode pour vérifier les rôles.

{% if is_granted('ROLE_EDITOR') %}

    {# Ici le contenu réservé aux éditeurs #}

{% endif %}

{% if is_granted('ROLE_ADMIN') %}

    {# Ici le contenu réservé aux administrateurs#}

{% endif %}

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder aux forums de Nouvelle-Techno.fr ou au serveur Discord pour une entraide par chat

#Tutoriel #Controllers #Framework #MVC #PHP #Routes #Utilisateurs #Symfony #Live-Coding #Formulaires