Archives Wild Code School
Symfony

23 - Symfony : Gestion des utilisateurs

Quête 26 sur 35 · 2 images archivées

Objectifs

  • ✅ Utiliser la commande make:user, présentation de l'entité User.
  • ✅ Utilisation des fixtures pour créer différents Users.
  • ✅ Créer un formulaire de login
  • ✅ Ajouter une fonctionnalité de "déconnexion".

Pré-requis

​ Avoir terminé la quête

Quêtes liées
title-book
Introduction

User stories 📋

en tant que visiteur, je souhaite avoir accès à un formulaire d'inscription afin de pouvoir me créer un compte

In progress 🤸

en tant que visiteur, je souhaite me connecter

Doing 🏗️

création de l'entité User

création du formulaire de login

Done ✅

en tant que visiteur, je dois voir un visuel pour chaque acteur afin de pouvoir identifier plus facilement quelqu'un sans avoir à lire son nom.

Dans cette quête, tu vas aborder une notion très importante lors du développement d'une application : l’authentification. Et plus particulièrement, la gestion des utilisateurs.

Sommaire

Création des Users

Dans ton Wild Series, tu vas avoir besoin d'implémenter la notion d'utilisateur, "User" en anglais, pour plusieurs raisons :

  • D'une part, toutes les parties de ton site ne doivent pas être accessibles à tout le monde. Par exemple, le CRUD des séries ne sera accessible qu'à des utilisateurs authentifiés.

  • D'autre part, tu vas devoir définir "qui a écrit quoi" et le sauvegarder en base de données. Tu vas pouvoir ainsi intégrer la notion d'auteur au sein de ton Wild Séries.

Dans cette quête, tu vas principalement suivre les consignes de la documentation officielle. La gestion des utilisateurs se fait par le composant SecurityBundle, mais tu n'as pas à l'installer si tu as bien initialisé ton projet avec l'option --webapp (dans ce cas il est présent par défaut). Concentre toi sur la seconde partie concernant la création de l'entité User, en t'arrêtant bien aux migrations suite à la création de cette entité User !

Attention

Quand tu arrives au titre "Loading the User: The User Provider", ne va pas plus loin ! C'est le sujet de la suite de la quête.

Lors de la création de l'entité, conserve les options par défaut que te propose Symfony. Ainsi, tu viens de générer une simple entité User, qui permettra de stocker les informations sur tes utilisateurs, au minimum leur identifiant et leur mot de passe. Si tu as besoin d'autres informations sur tes Users (nom, date de naissance, pays, etc.) tu peux simplement éditer la classe et y ajouter les propriétés voulues (N'oublie pas que tu peux utiliser la commande make:entity pour mettre à jour ton entité User).

↗
Create your User Class

Suis la documentation en partant de l'installation de SecurityBundle jusqu'à la migration de l'entité User

symfony.com

Un peu de sécurité

La gestion des utilisateurs demande un minimum de sécurité. Pour se connecter, ton User a généralement besoin d'un identifiant (par exemple un email ou nom d'utilisateur) et d'un mot de passe. Il existe d'autres stratégies d'identification (double authentification, biométrie, etc.) mais celles-ci sont plus complexes à mettre en place et ne seront pas traitées ici.

Tu dois te rappeler une règle essentielle : un mot de passe en base de données ne doit JAMAIS être stocké "en clair". Celui-ci doit systématiquement être encodé (via des algorithmes de hachage). Regarde cette vidéo jusqu'à 2'30 pour mieux comprendre ces fonctions. Il existe de nombreuses fonctions de hachage, plus ou moins performantes et sécurisées, de nouvelles sont régulièrement créées, apportant toujours plus de sécurité et de performance.

Lors de la tentative de connexion au site, le mot de passe saisi par l'utilisateur est haché par la fonction, et la chaîne ainsi obtenue est comparée à celle qui se trouve en base de données. Si les deux chaînes sont identiques, c'est que le bon mot de passe a été saisi et l'utilisateur est bien reconnu. Note bien qu'à aucun moment en tant que développeur, tu n'auras accès au mot de passe "en clair" de l'utilisateur. Si c'est le cas, c'est une grave faille de sécurité.

La sécurisation de l'authentification est un sujet complexe et critique. Heureusement, Symfony gère cela pour toi ! Le framework est même réglé par défaut pour utiliser la fonction de hachage la plus à jour. C'est le but du auto dans la partie password_hashers du fichier security.yaml.

↗
Registering the User: Hashing Passwords
symfony.com

À l'heure actuelle, en fonction de la version de PHP utilisée, ce sont les fonctions bcrypt ou argon2 qui sont utilisées par Symfony.

▶
Vidéo YouTube
https://youtu.be/wAiSu6oiK-Q

Utilisation des fixtures pour créer différents users.

Maintenant que ton application dispose d'une entité User et de son UserRepository associé, et que la base de données est mise à jour avec ces changements, tu vas pouvoir créer quelques utilisateurs dans ta table User. Pour cela, tu vas utiliser des fixtures. Commence par créer un fichier UserFixtures (via la commande make:fixtures) puis regarde la documentation sur le hashage de passwords

Tu vas créer deux faux utilisateurs (un contributeur et un admin), et ceux-ci auront un mot de passe. Cependant ce mot de passe devra être haché comme tout mot de passe, c'est le rôle du service $passwordHasher injecté dans le __construct en utilisant bien le type UserPasswordHasherInterface.

Suis bien la documentation, si tu rencontres des difficultés, une solution est disponible en cliquant ci-dessous.

Voir la solution
php
<?php

namespace App\DataFixtures;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

class UserFixtures extends Fixture
{
    public function __construct(
        private readonly UserPasswordHasherInterface $passwordHasher
    ) {
    }

    public function load(ObjectManager $manager): void
    {
        // Création d’un utilisateur de type “contributeur” (= auteur)
        $contributor = new User();
        $contributor->setEmail('contributor@monsite.com');
        $contributor->setRoles(['ROLE_CONTRIBUTOR']);
        $hashedPassword = $this->passwordHasher->hashPassword(
            $contributor,
            'contributorpassword'
        );

        $contributor->setPassword($hashedPassword);
        $manager->persist($contributor);

        // Création d’un utilisateur de type “administrateur”
        $admin = new User();
        $admin->setEmail('admin@monsite.com');
        $admin->setRoles(['ROLE_ADMIN']);
        $hashedPassword = $this->passwordHasher->hashPassword(
            $admin,
            'adminpassword'
        );
        $admin->setPassword($hashedPassword);
        $manager->persist($admin);

        // Sauvegarde des 2 nouveaux utilisateurs :
        $manager->flush();
    }
}

💻 Puis lance l'exécution de tes fixtures (pour rappel, cela vide par défaut toutes les tables de ta base de données, assure toi donc bien d'avoir des fixtures également pour les programmes, saisons, épisodes, etc. :

bash
php bin/console doctrine:fixtures:load

Vérifie en base de données que tes utilisateurs sont bien créés. Si c'est bon, passe à la suite.

Gestion du login

Maintenant que tu disposes d'utilisateurs, il serait pertinent de pouvoir se connecter avec ces différents comptes. Pour cela, il faut une interface de connexion. Celle-ci se présente généralement sous la forme d'un formulaire d'authentification, avec un champ identifiant (login) et un champ mot de passe (”password”).

💻 Suis les consignes suivantes dans leur intégralité et reprends la configuration fournie par la documentation. N'oublie pas la partie sur la sécurisation CSRF.

Au niveau de la redirection après connexion, configure plutôt l'application pour rediriger l'utilisateur à l'accueil de ton Wild Séries.

↗
How to Build a Login Form
symfony.com

Pour tester, ouvre la route /login dans ton navigateur et essaie de te connecter avec les différents comptes utilisateurs que tu as créé précédemment.

Si tout s'est bien passé, tu devrais, après identification, te retrouver à l'accueil de ton Wild Séries, avec, en bas de ta page, dans la barre de debug de Symfony, ton adresse email visible.

image

↗
Added a command to encode a password

Si un jour tu as besoin d’ajouter un mot de passe directement en base de données, ne te lance pas dans le chiffrement “de tête” de celui-ci ! Tu peux utiliser cette commande présentée dans cette ressource.

symfony.com

Ajout du logout

Maintenant qu'un utilisateur peut se connecter, il doit pouvoir se déconnecter. Ce serait une faille de sécurité importante s'il n'était pas possible à un utilisateur de fermer sa session. Cela voudrait dire que, si tu te connectes à un site sur ordinateur ou un smartphone partagé, n'importe qui ayant accès à ce support pourrait utiliser ton profil sur cette application.

💻 Lis la documentation à ce sujet , et suis à la lettre les instructions afin d'ajouter cette fonctionnalité.

↗
Logging Out
symfony.com

Un peu de mise en forme à tout ça !

Tu es arrivé jusqu'ici, bravo ! Mais ce n'est pas fini ! Tu disposes de fonctionnalités assez complètes, mais il manque un peu de liant à tout ça. Que dirais-tu de permettre à tous tes utilisateurs d'accéder facilement à l’ensemble de ces fonctionnalités ?

Cela peut se faire assez simplement en affichant un bouton connexion/déconnexion dans ta navbar

Pour cela, il te suffit d'ajouter ce fragment de code Twig :

html
{% if app.user %}
    {{ app.user.email }}
    <a href="{{ path('app_logout') }}"> Se déconnecter</a>
{% else %}
    <a href="{{ path('login') }}"> Se connecter</a>
{% endif %}

Car oui, depuis Twig, tu as accès à app.user, une variable dite "globale", qui contient toutes les informations de l'utilisateur actuellement authentifié.

↗
Retrieving the User Object from controller

Essentiel pour le challenge : pour récupérer les informations de l'utilisateur depuis un contrôleur.

symfony.com
↗
How to Access the User, Request, Session & more in Twig via the app Variable

Toutes les infos disponibles en permanence dans Twig.

symfony.com

Conclusion

Pour résumer, dans cette quête, tu as effectué beaucoup de travail avec quelques lignes de commandes et un peu d'ajustement. Imagine si tu avais dû réaliser tout ça à la main, dans un projet hors framework ! Encore une fois, tu vois l'intérêt d'utiliser un framework pour gagner du temps et de la sécurité. Et les avantages de le maîtriser, pour savoir où intervenir afin de rendre ton code conforme à tes attentes.

💪 Challenge

Gestion des commentaires utilisateurs

Sur la page détail d’un épisode, tu dois ajouter la fonctionnalité “Laisser un commentaire”

User stories 📋

en tant que visiteur, je souhaite avoir accès à un formulaire d'inscription afin de pouvoir me créer un compte

In progress 🤸

en tant que visiteur identifié, je souhaite voir un formulaire afin de laisser un commentaire à propos d'un épisode

Doing 🏗️

Done ✅

en tant que visiteur, je souhaite me connecter

création de l'entité User

création du formulaire de login

À l’aide des compétences acquises jusqu’à présent, crée une entité Comment correspondant au fragment de modèle ci-dessous. Lors de la création de la relation avec l’entité User, ne choisis pas l’option par défaut, mais nomme explicitement ton champ author plutôt que user.

has
1
0..*
belongs to
0..*
1
Episode
-id: int
-title: string
-synopsis: string
-number: int
-season: Season
-comments: ArrayCollection
Comment
-id: int
-comment: string
-rate: int
-episode: Episode
-author: User
User
-id: int
-email: string
-password: string
-roles: array
Diagramme de classe
episodeintidvarchartitleintnumbertextsynopsisintseason_idcommentintidvarcharcommentintrateintauthor_iduserintidvarcharemailvarcharpasswordjsonroleshasbelongs_toMLD des tables episode, comment et user

Tu peux constater que, dans le cadre d’une relation vers une entité donnée, tu n’es pas obligé de nommer la propriété correspondante de manière rigoureusement identique. Ainsi, la propriété “author” pointe vers l’entité “User”. Cela permet d’ajouter du sens à une relation. Cela permet aussi d’éviter les confusions quand une table est liée 2 fois à la même table. Par exemple, un commentaire sur Blablacar est relié 2 fois à la table user : 1 fois pour le chauffeur, une fois pour le passager. Evidemment, tu n’auras pas oublié d’effectuer la migration en deux temps avec bin/console make:migration, suivi de bin/console doctrine:migrations:migrate.

Ensuite, tu dois réaliser les fonctionnalités suivantes :

  • Si un utilisateur est non connecté sur une page détail d’un épisode, la fonctionnalité “Ajouter un commentaire” n’est pas visible.
  • Si un utilisateur est connecté sur une page détail d’un épisode, la fonctionnalité “Ajouter un commentaire” devient visible avec un formulaire composé d’un champ rate, d’un champ comment et d’un bouton de soumission.
  • Lorsqu’un commentaire est ajouté par un utilisateur, celui-ci est bien enregistré comme auteur de celui-ci.
  • Lorsqu’un commentaire est ajouté par un utilisateur, celui-ci est bien associé à l’épisode affiché.
  • La liste des commentaires liés à la série est affichée dans l’ordre chronologique.

Tu l’auras compris, la relation entre le commentaire et l’épisode, ainsi que celle entre le commentaire et l’auteur, sont à faire du côté du contrôleur.

Critères de validation

  • le formulaire de login est fonctionnel ;
  • le lien de déconnexion est fonctionnel ;
  • lorsque tu rédiges un commentaire, l’auteur actuellement connecté est bien associé en base de données ;
  • lorsque tu rédiges un commentaire, la série est bien associée en base de données ;
  • Les commentaires liés à l’épisode sont affichés dans l’ordre du plus vieux au plus récent ;
  • Le code est disponible sur un repository GitHub, avec une branche correspondant à cette quête ;

Ressources partagées sur cette quête