Vous êtes sur la page 1sur 48

Symfony 5 : gestion d’utilisateurs

Achref El Mouelhi

Docteur de l’université d’Aix-Marseille


Chercheur en Programmation par contrainte (IA)
Ingénieur en Génie logiciel

elmouelhi.achref@gmail.com

H & H: Research and Training 1 / 34


Plan

1 Introduction
2 Création d’utilisateur
3 Préparation de l’authentification
4 Déconnexion
5 Contrôle d’accès
Dans security.yaml
Dans le contrôleur
Dans la vue
6 Utilisateur authentifié
Dans le contrôleur
Dans la vue
7 Rôles hiérarchiques

H & H: Research and Training 2 / 34


Introduction

Symfony

But de la sécurité

H I ©
Interdire, à un utilisateur, l’accès à une ressource à laquelle il n’a pas
droit
UEL
O
f E LM
ch r e
©A

H & H: Research and Training 3 / 34


Introduction

Symfony

But de la sécurité

H I ©
Interdire, à un utilisateur, l’accès à une ressource à laquelle il n’a pas
droit
UEL
O
f E LM
Deux étapes
ch r e
©A
Qui veut accéder à la ressource ?
A t-il le droit d’y accéder ?

H & H: Research and Training 3 / 34


Introduction

Symfony

Configuration de la sécurité

En utilisant des données statiques (en mémoire)


H I ©
EL
En utilisant des données dynamiques (stockées dans une base de
U
données) O
f E LM
ch r e
©A

H & H: Research and Training 4 / 34


Introduction

Symfony

Configuration de la sécurité

En utilisant des données statiques (en mémoire)


H I ©
EL
En utilisant des données dynamiques (stockées dans une base de
U
données) O
f E LM
ch r e
Pour cela ©A
On va utiliser un bundle Symfony à savoir security-bundle

H & H: Research and Training 4 / 34


Introduction

Symfony

Configuration de la sécurité
En utilisant les annotations
H I ©
Et en définissant quelques règles dans UEL
O
config/packages/security.yaml
f E LM
ch r e
Mais on peut aussi utiliser :

©A
le format XML
les tableaux imbriqués de PHP

H & H: Research and Training 5 / 34


Introduction

Symfony

Contenu de security.yaml

security:
# https://symfony.com/doc/current/security.html#where-do-users-come
-from-user-providers
H I ©
EL
providers:
users_in_memory: { memory: null }
firewalls:
O U
dev:

f E LM
pattern: ˆ/(_(profiler|wdt)|css|images|js)/
security: false
ch r e
©A
main:
anonymous: lazy
provider: users_in_memory
access_control:
# - { path: ˆ/admin, roles: ROLE_ADMIN }
# - { path: ˆ/profile, roles: ROLE_USER }

H & H: Research and Training 6 / 34


Introduction

Symfony

Plusieurs étapes
H I ©
se L
Préparation de la partie utilisateur (qui va E connecter)
U
L MO (formulaire
Préparation de la partie authentification
e f E
d’authentification, déconnexion...)
r
Gestion de rA ch
© ôles

H & H: Research and Training 7 / 34


Introduction

Symfony

H I ©
Si on ne choisit pas la version complète à la crE
U L du projet
éation

L MO
composer require symfony/security-bundle

r e f E
A ch
©

H & H: Research and Training 8 / 34


Création d’utilisateur

Symfony
Pour créer la classe User

exécutez la commande php bin/console make:user

répondez à The name of the security user class par User

I ©
répondez à Do you want to store user data in the database (via
H
EL
Doctrine)? par yes

O U
répondez à Enter a property name that will be the unique "display"
name for the user par email

f E LM
r e
répondez à Does this app need to hash/check user passwords? par yes
ch
©A

H & H: Research and Training 9 / 34


Création d’utilisateur

Symfony
Pour créer la classe User

exécutez la commande php bin/console make:user

répondez à The name of the security user class par User

I ©
répondez à Do you want to store user data in the database (via
H
EL
Doctrine)? par yes

O U
répondez à Enter a property name that will be the unique "display"
name for the user par email

f E LM
r e
répondez à Does this app need to hash/check user passwords? par yes
ch
©A
Le résultat est
created: src/Entity/User.php
created: src/Repository/UserRepository.php
updated: src/Entity/User.php
updated: config/packages/security.yaml

H & H: Research and Training 9 / 34


Création d’utilisateur

Nouveau contenu de security.yaml

security:
encoders:
App\Entity\User:
algorithm: auto

# https://symfony.com/doc/current/security.html#where-do-users-come
-from-user-providers
providers:
H I ©
EL
# used to reload user from session & other features (e.g.
switch_user)
O U
LM
app_user_provider:
entity:

e f E
class: App\Entity\User
r
ch
property: email

©A
firewalls:
dev:
pattern: ˆ/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: app_user_provider

access_control:

H & H: Research and Training 10 / 34


Création d’utilisateur

Symfony

Pour créer la table User

exécutez la commande php bin/console make:migration

I ©
et ensuite la commande php bin/console doctrine:migrations:migrate
H
UEL
O
f E LM
ch r e
©A

H & H: Research and Training 11 / 34


Création d’utilisateur

Symfony

Pour créer la table User

exécutez la commande php bin/console make:migration

I ©
et ensuite la commande php bin/console doctrine:migrations:migrate
H
UEL
O
f E LM
r e
Pour remplir la table User avec des données aléatoires
ch
©A
installez le bundle de fixture composer require --dev
doctrine/doctrine-fixtures-bundle

demandez à ce bundle de remplir la table php bin/console make:fixtures

répondez à The class name of the fixtures to create par UserFixtures

H & H: Research and Training 11 / 34


Création d’utilisateur

Symfony
Contenu généré pour UserFixtures

namespace App\DataFixtures;

use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
H I ©
class UserFixtures extends Fixture
UEL
O
LM
{

r e f E
public function load(ObjectManager $manager)
ch
©A
{
// $product = new Product();
// $manager->persist($product);

$manager->flush();
}
}

H & H: Research and Training 12 / 34


Création d’utilisateur
Nouveau contenu de UserFixtures

class UserFixtures extends Fixture


{
private $passwordEncoder;

public function __construct(UserPasswordEncoderInterface $passwordEncoder)


{
$this->passwordEncoder = $passwordEncoder;
}
public function load(ObjectManager $manager)

©
{
$user = new User();

H I
EL
$user->setEmail(’wick@wick.us’);
$user->setRoles([’ROLE_ADMIN’]);

$user,
O U
$user->setPassword($this->passwordEncoder->encodePassword(

LM
’wick’
));
$manager->persist($user);

r e f E
ch
$user2 = new User();
$user2->setEmail(’john@john.us’);

©A
$user2->setPassword($this->passwordEncoder->encodePassword(
$user2,
’john’
));
$manager->persist($user2);
$manager->flush();
}
}

H & H: Research and Training 13 / 34


Création d’utilisateur
Nouveau contenu de UserFixtures

class UserFixtures extends Fixture


{
private $passwordEncoder;

public function __construct(UserPasswordEncoderInterface $passwordEncoder)


{
$this->passwordEncoder = $passwordEncoder;
}
public function load(ObjectManager $manager)

©
{
$user = new User();

H I
EL
$user->setEmail(’wick@wick.us’);
$user->setRoles([’ROLE_ADMIN’]);

$user,
O U
$user->setPassword($this->passwordEncoder->encodePassword(

LM
’wick’
));
$manager->persist($user);

r e f E
ch
$user2 = new User();
$user2->setEmail(’john@john.us’);

©A
$user2->setPassword($this->passwordEncoder->encodePassword(
$user2,
’john’
));
$manager->persist($user2);
$manager->flush();
}
}

Les use nécessaires

use App\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

H & H: Research and Training 13 / 34


Création d’utilisateur

Symfony

H I ©
EL
Pour insérer l’utilisateur dans la base de données, exécutez
U
O
LM
php bin/console doctrine:fixtures:load ou php
bin/console d:f:l
r e f E
ch
©A

H & H: Research and Training 14 / 34


Préparation de l’authentification

À partir du terminal, exécutez la commande suivante


php bin/console make:auth

What style of authentication do you want? [Empty authenticator]:


[0] Empty authenticator
[1] Login form authenticator
> 1

The class name of the authenticator to create (e.g.


H I ©
EL
AppCustomAuthenticator):
> LoginFormAuthenticator
O U
SecurityController]:
f E LM
Choose a name for the controller class (e.g. SecurityController) [

> SecurityController
ch r e
©A
Do you want to generate a ’/logout’ URL? (yes/no) [yes]:
> yes

H & H: Research and Training 15 / 34


Préparation de l’authentification

À partir du terminal, exécutez la commande suivante


php bin/console make:auth

What style of authentication do you want? [Empty authenticator]:


[0] Empty authenticator
[1] Login form authenticator
> 1

The class name of the authenticator to create (e.g.


H I ©
EL
AppCustomAuthenticator):
> LoginFormAuthenticator
O U
SecurityController]:
f E LM
Choose a name for the controller class (e.g. SecurityController) [

> SecurityController
ch r e
©A
Do you want to generate a ’/logout’ URL? (yes/no) [yes]:
> yes

Le résultat est
created: src/Security/LoginFormAuthenticator.php
updated: config/packages/security.yaml
created: src/Controller/SecurityController.php
created: templates/security/login.html.twig
H & H: Research and Training 15 / 34
Préparation de l’authentification

Symfony

Pour tester, allez sur la route /login


essayez de vous connecter avec un email inexistant
ensuite essayez de vous connecter avec un email existant et un
H I ©
mot de passe incorrect
U EL
M O
f E L
enfin connectez-vous avec wick@wick.us et wick

c h re
©A

H & H: Research and Training 16 / 34


Préparation de l’authentification

Symfony

Pour tester, allez sur la route /login


essayez de vous connecter avec un email inexistant
ensuite essayez de vous connecter avec un email existant et un
H I ©
mot de passe incorrect
U EL
M O
f E L
enfin connectez-vous avec wick@wick.us et wick

c h re
©A
Remarque

Problème de redirection après la connexion

H & H: Research and Training 16 / 34


Préparation de l’authentification

Symfony
Pour résoudre ce problème, il faut modifier la méthode
onAuthenticationSuccess définie dans
security/LoginFormAuthenticator pour rediriger vers la
route home route
public function onAuthenticationSuccess(Request
H I ©
$request, TokenInterface $token, $providerKey)
UEL
O
LM
{

f E
if ($targetPath = $this->getTargetPath($request
r e
ch
->getSession(), $providerKey)) {

} ©A
return new RedirectResponse($targetPath);

return new RedirectResponse($this->urlGenerator


->generate(’home_route’));
}

H & H: Research and Training 17 / 34


Préparation de l’authentification

Symfony
Pour modifier les messages d’erreurs de la page d’accueil, créez un fichier
security.en.xlf dans translations avec le contenu suivant
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1
.2">
<file source-language="en" datatype="plaintext" original="
H I ©
EL
file.ext">
<body>
O U
LM
<trans-unit id="Invalid credentials.">

r e E
<source>Invalid credentials.</source>
f
<target>Le mot de passe est invalide</target>
ch
©A
</trans-unit>
<trans-unit id="Email could not be found.">
<source>Email could not be found.</source>
<target>Email non-trouvé</target>
</trans-unit>
</body>
</file>
</xliff>

H & H: Research and Training 18 / 34


Déconnexion

Symfony

Pour se déconnecter
essayez la route /logout H I ©
UEL
O
f E LM
ch r e
©A

H & H: Research and Training 19 / 34


Déconnexion

Symfony

Pour se déconnecter
essayez la route /logout H I ©
UEL
O
f E LM
Question ch r e
©A
Comment rediriger vers la page d’authentification ?

H & H: Research and Training 19 / 34


Déconnexion

Symfony

Allez à la section logout de security.yaml


logout:
path: app_logout
# where to redirect after logout
H I ©
# target: app_any_route
UEL
O
f E LM
ch r e
©A

H & H: Research and Training 20 / 34


Déconnexion

Symfony

Allez à la section logout de security.yaml


logout:
path: app_logout
# where to redirect after logout
H I ©
# target: app_any_route
UEL
O
f E LM
ch r e
Décommentez la clé target et ajoutez la route
logout: ©A
path: app_logout
# where to redirect after logout
target: app_login

H & H: Research and Training 20 / 34


Contrôle d’accès

Symfony

Pour interdire l’accès à une page : deux solutions possibles


H I ©
soit en configurant la section access control dans
UEL
security.yaml O
soit dans le contrôleur f E LM
ch r e
©A
soit en utilisant la fonction is granted() dans la vue

H & H: Research and Training 21 / 34


Contrôle d’accès Dans security.yaml

Symfony

Pour interdire l’accès à tout utilisateur non-authentifié

access_control:
- { path: ’ˆ/login’, roles: IS_AUTHENTICATED_ANONYMOUSLY }
H I ©
- { path: ’ˆ/*’, roles: [IS_AUTHENTICATED_FULLY] }
U EL
O
f E LM
ch r e
©A

H & H: Research and Training 22 / 34


Contrôle d’accès Dans security.yaml

Symfony

Pour interdire l’accès à tout utilisateur non-authentifié

access_control:
- { path: ’ˆ/login’, roles: IS_AUTHENTICATED_ANONYMOUSLY }
H I ©
- { path: ’ˆ/*’, roles: [IS_AUTHENTICATED_FULLY] }
U EL
O
f E LM
r e
Pour autoriser les utilisateurs qui ont le rôle admin (ROLE ADMIN)
ch
access_control:
©A
- { path: ’ˆ/login’, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ’ˆ/*’, roles: [ROLE_ADMIN] }

H & H: Research and Training 22 / 34


Contrôle d’accès Dans security.yaml

Symfony

Remarques

La clé path accepte les expressions régulières H I ©


U EL
O
Le nom d’un rôle doit être écrit en majuscule

f E LM
Les mots composants le nom d’un rôle doivent être séparés par
un underscore. ch r e
©A
La clé roles accepte une valeur ou un tableau de valeurs

H & H: Research and Training 23 / 34


Contrôle d’accès Dans security.yaml

Symfony

Pour restreindre l’accès aux routes du contrôleur PersonneController aux


utilisateurs ayant le rôle ROLE ADMIN ou ROLE USER

access_control:
H I ©
EL
- { path: ’ˆ/personne’, roles: [ROLE_USER, ROLE_ADMIN] }

O U
f E LM
ch r e
©A

H & H: Research and Training 24 / 34


Contrôle d’accès Dans security.yaml

Symfony

Pour restreindre l’accès aux routes du contrôleur PersonneController aux


utilisateurs ayant le rôle ROLE ADMIN ou ROLE USER

access_control:
H I ©
EL
- { path: ’ˆ/personne’, roles: [ROLE_USER, ROLE_ADMIN] }

O U
f E LM
ch r e
En testant, le message d’erreur suivant est affiché

©A
Passing more than one Security attribute to "Symfony\Component
\Security\Core\Authorization\AccessDecisionManager::decide()"
is not supported.

H & H: Research and Training 24 / 34


Contrôle d’accès Dans security.yaml

Symfony

Explication

Bug dans la version 5 de Symfony


H I ©
U EL
Pour le corriger, il faut aller dans vendor\symfony\security-core
MO
\Authorization\TraceableAccessDecisionManager.php
L
e
Cherchez la méthoderdecide f E
A ch indiquées dans la slide suivante ou dans
©
Faites les modifications
https://github.com/symfony/symfony/
commit/63984b013c92f5cd2373d81c19554b4270c4b776

H & H: Research and Training 25 / 34


Contrôle d’accès Dans security.yaml

Symfony
Remplacez
public function decide(TokenInterface $token, array $attributes,
$object = null): bool

H I ©
U EL
O
f E LM
ch r e
©A

H & H: Research and Training 26 / 34


Contrôle d’accès Dans security.yaml

Symfony
Remplacez
public function decide(TokenInterface $token, array $attributes,
$object = null): bool

Par
H I ©
public function decide(TokenInterface $token, array $attributes,
U EL
O
$object = null/*, bool $allowMultipleAttributes = false*/): bool

f E LM
ch r e
©A

H & H: Research and Training 26 / 34


Contrôle d’accès Dans security.yaml

Symfony
Remplacez
public function decide(TokenInterface $token, array $attributes,
$object = null): bool

Par
H I ©
public function decide(TokenInterface $token, array $attributes,
U EL
O
$object = null/*, bool $allowMultipleAttributes = false*/): bool

f E LM
ch r e
©A
Et
$result = $this->manager->decide($token, $attributes, $object);

H & H: Research and Training 26 / 34


Contrôle d’accès Dans security.yaml

Symfony
Remplacez
public function decide(TokenInterface $token, array $attributes,
$object = null): bool

Par
H I ©
public function decide(TokenInterface $token, array $attributes,
U EL
O
$object = null/*, bool $allowMultipleAttributes = false*/): bool

f E LM
ch r e
©A
Et
$result = $this->manager->decide($token, $attributes, $object);

Par
$result = $this->manager->decide($token, $attributes, $object, 3 < \
func_num_args() && func_get_arg(3));

H & H: Research and Training 26 / 34


Contrôle d’accès Dans le contrôleur

Symfony

Pour restreindre l’accès à une méthode de PersonneController


aux utilisateurs authentifiés
class PersonneController extends AbstractController
{
H I ©
EL
/**
* @Route("/personne/add", name="personne_add")
O U
*/
f E LM
ch r e
public function addForm(EntityManagerInterface
$entityManager, Request $request)
{ ©A
$this->denyAccessUnlessGranted(’
IS_AUTHENTICATED_FULLY’);
// le reste du contenu
}

H & H: Research and Training 27 / 34


Contrôle d’accès Dans le contrôleur

Symfony

Pour restreindre l’accès à toutes les méthodes de


PersonneController aux utilisateurs ayant le rôle ROLE ADMIN
/**
H I ©
*
U EL
O
LM
* @IsGranted("ROLE_ADMIN")
*/
r e f E
ch
class PersonneController extends AbstractController
{
// le contenu ©A
}

H & H: Research and Training 28 / 34


Contrôle d’accès Dans le contrôleur

Symfony

Pour restreindre l’accès à une méthode de PersonneController


aux utilisateurs ayant le rôle ROLE ADMIN
class PersonneController extends AbstractController
{
H I ©
/**
U EL
* @IsGranted("ROLE_ADMIN") O
E LM
* @Route("/personne/add", name="personne_add")
f
*/
ch r e
©A
public function addForm(EntityManagerInterface
$entityManager, Request $request)
{
// le reste du contenu
}

H & H: Research and Training 29 / 34


Contrôle d’accès Dans la vue

Symfony

Pour restreindre une partie de la vue aux utilisateurs ayant le rôle


I ©
ROLE ADMIN (contenu à ajouter dans home/index.html.twig)
H
{% if is_granted(’ROLE_ADMIN’) %} EL
M OU
<a href="{{ url(’personne_add’) }}">
f
Ajouter une L
Epersonne
</a>
ch r e
{% endif %} A
©

H & H: Research and Training 30 / 34


Utilisateur authentifié Dans le contrôleur

Symfony
Pour récupérer l’utilisateur authentifié dans une méthode de
contrôleur
class PersonneController extends AbstractController
{
/**
H I ©
* @Route("/personne/add", name="personne_add")
U EL
O
LM
*/
f E
public function addForm(EntityManagerInterface
r e
ch
$entityManager, Request $request)
{
©A
$this->denyAccessUnlessGranted(’
IS_AUTHENTICATED_FULLY’);
$user = $this->getUser();
// le reste du contenu
}

H & H: Research and Training 31 / 34


Utilisateur authentifié Dans le contrôleur

Symfony
Pour récupérer les rôles de l’utilisateur
class PersonneController extends AbstractController
{
/**
* @Route("/personne/add", name="personne_add")
H I ©
*/
U EL
O
LM
public function addForm(EntityManagerInterface

f E
$entityManager, Request $request)
r e
{
ch
©A
$this->denyAccessUnlessGranted(’
IS_AUTHENTICATED_FULLY’);
$user = $this->getUser();
roles = $user->getRoles();
// le reste du contenu
}

H & H: Research and Training 32 / 34


Utilisateur authentifié Dans la vue

Symfony

Pour récupérer l’email de la personne authentifié (contenu à


H I ©
ajouter dans personne/index.html.twig)
U EL
O
LM
{% if is_granted(’ROLE_ADMIN’) %}

f
<p>Email: {{ app.user.email }}</p>
r e E
{% endif %}
ch
©A

H & H: Research and Training 33 / 34


Rôles hiérarchiques

Symfony

Dans security.yaml

security:
# ...

role_hierarchy:
H I ©
ROLE_ADMIN: ROLE_USER
UEL
O
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

f E LM
ch r e
Remarques
©A
L’utilisateur ayant le rôle ROLE ADMIN a aussi le rôle ROLE USER

L’utilisateur ayant le rôle ROLE SUPER ADMIN a aussi les rôle ROLE ADMIN,
ROLE USER et ROLE ALLOWED TO SWITCH

H & H: Research and Training 34 / 34

Vous aimerez peut-être aussi