12/03/2023

0. Utilisateurs

Lu 487 fois Licence Creative Commons

À l'heure actuelle, notre site n'est qu'informatif. Avec des utilisateurs, on pourra amener un peu d'interaction, ce qui fait nécessairement intervenir des sujets de sécurité comme la connexion, les mots de passe, les droits d'accès à certaines pages.
Installons le SecurityBundle

composer req security

Utilisateurs

Nos utilisateurs seront stockés en base de données, il leur faut donc une entité dédiée. Cependant nous n'allons pas utiliser la commande make:entity car les utilisateurs doivent posséder certaines propriétés bien définies. On exécute plutôt make:user pour une classe User stockée en base de données:

php bin/console make:user
[output]
[output] The name of the security user class (e.g. User) [User]:
 > 
[output]
[output] Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:
 > 

Il faut ensuite indiquer une propriété qui devra être unique pour chaque utilisateur et servira à l'identifier. On gardera la propriété email:

[output] Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:
 > 

Pour finir, indiquer que nous nous chargerons des mots de passe:

[output] Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).
[output]
[output] Does this app need to hash/check user passwords? (yes/no) [yes]:
 > 

Avant de générer une migration, modifier l'entité pour ajouter un pseudo:

php bin/console make:entity                                                                                                                                 
[output]                                                                                                                                                              
[output] Class name of the entity to create or update (e.g. BraveChef):                                                                                               
 > User
[output]
[output] Your entity already exists! So let's add some new fields!
[output]
[output] New property name (press <return> to stop adding fields):
 > pseudo
[output]
[output] Field type (enter ? to see all types) [string]:
 > 
[output]
[output] Field length [255]:
 > 30
[output]
[output] Can this field be null in the database (nullable) (yes/no) [no]:
 > 
[output]
[output] updated: src/Entity/User.php

Le pseudo doit être unique, comme l'email, car il servira à la connexion. Ouvrir le fichier de l'entité et ajouter un argument unique à l'attribut Column:

#[ORM\Column(length: 30, unique: true)]
private ?string $pseudo = null;

Enfin, mettre à jour la base de données:

php bin/console make:migration
php bin/console doctrine:migrations:migrate

Fixtures

Créer le fichier de fixtures des utilisateurs: /fixtures/user.yaml. Pour faciliter la connexion, les emails prendront tous la forme user<X>@mail.org, ce qui les rendra uniques. Pour que les pseudos soient uniques, on ajoute (unique) après la clé pseudo pour utiliser le mode d'unicité de Faker:

App\Entity\User:
    user_{1..100}:
        email: 'user<current()>\@mail.org'
        pseudo (unique): '<userName()>'
        password: ''

La syntaxe {1..100} permet une itération de 1 à 100. La fonction <current()> permet de récupérer ce nombre.
Note: on doit échapper le @ car il sert habituellement à référencer d'autres fixtures.

Un problème se pose pour le mot de passe des utilisateurs. En base de données il doit être hashé, et avec Symfony, on passe par un service pour hasher les mots de passe. Faker ne fournissant évidemment pas de fonction pour satisfaire ce besoin, nous allons suivre la documentation du bundle et créer une classe App\Fixtures\HashedPasswordProvider:

<?php

namespace App\Fixtures;

use Faker\Provider\Base;

class HashedPasswordProvider extends Base
{

}

En héritant de Faker\Provider\Base, la classe sera automatiquement prise en compte grâce à l'autoconfiguration qui enregistrera le service avec un tag utilisé par le bundle.
On injecte le service pour hasher les mots de passe:

// ...
use Faker\Generator;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

class HashedPasswordProvider extends Base
{
    public function __construct(
        private readonly UserPasswordHasherInterface $passwordHasher,
        Generator $generator,
    ) {
        parent::__construct($generator);
    }
}

Puis on rajoute notre méthode qui deviendra utilisable dans les fixtures:

public function hashedPassword(string $password): string
{
    // N'ayant pas accès à l'objet User généré par la fixture, on triche !
	return $this->passwordHasher->hashPassword(new User(), $password);
}

On complète les fixtures en définissant le même mot de passe pour tous les utilisateurs afin de simplifier la connexion en environnement de développement: P@ssw0rd.

App\Entity\User:
    user_{1..100}:
        # ...
        password: '<hashedPassword(P\@ssw0rd)>'

Et on recharge les données:

php bin/console hautelook:fixtures:load