Les bases 5/6 : Contrôle d’accès

Publié le 17 octobre 2022 par William Suppo
Couverture de l'article Les bases 5/6 : Contrôle d’accès

Le contrôle d’accès aux ressources est fondamental au sein d’une application. Cette sécurité permet de garantir que le voisin n’ira pas consulter votre relevé de compte ou bien qu’il ne pourra pas changer votre photo de profil sur votre réseau social préféré.

Sommaire

  1. Création du modèle
  2. Création du contrôleur
  3. Création des vues
  4. Validation des données
  5. Contrôle d’accès
  6. Les tests

Les cas d’usage

Sur notre application de gestion de restaurants, nous allons garantir à nos restaurateurs qu’ils sont seuls à pouvoir gérer leur périmètre, excepté l'admin, en appliquant ces contrôles :

La génération de la Policy

Les Policies sont des classes dont la vocation est de vérifier les accès à un modèle particulier.

Les accès sont définis en fonction des actions souhaité sur le modèle.

Une commande existe pour nous simplifier la création de notre Policy. On peut même y définir le modèle cible, comme ceci :

1php artisan make:policy RestaurantPolicy --model=Restaurant

On obtient le fichier app/Policies/RestaurantPolicy.php préalablement remplit avec toutes les actions possibles, il nous reste plus qu’à y intégrer la logique en fonction de nos cas d’usages en passant en revue chacune d’une méthode

Dans le cas des méthodes viewAny et view étant donné que l’on souhaite que tout le monde puisse accéder à ces pages nous devons rendre la fourniture de notre instance User optionnelle pour le cas de l’utilisateur non-authentifié. Pour le contenu de la méthode, on retourne tout simplement true car nous n’avons pas de contrainte particulière :

1public function viewAny(?User $user)
2{
3 return true;
4}
5 
6public function view(?User $user, Restaurant $restaurant)
7{
8 return true;
9}

On enchaîne avec la méthode create, comme seul un restaurateur peut créer des restaurants nous avons y vérifier que le rôle de l’utilisateur fournit est bien owner :

1public function create(User $user)
2{
3 return $user->isOwner();
4}

Ensuite, on traite la méthode update où a été définit que seul le propriétaire du restaurant ciblé peut agir, chose qu’on peut garantir en comparant les identifiants :

1public function update(User $user, Restaurant $restaurant)
2{
3 return $user->id === $restaurant->user_id;
4}

Enfin, la méthode delete où l’on souhaite que le restaurateur ainsi qu’un admin puisse déclencher la suppression d’un restaurant :

1public function delete(User $user, Restaurant $restaurant)
2{
3 return $user->isAdmin() || $user->id === $restaurant->user_id;
4}

Voilà, on a répondu à nos divers cas d’usages.

L’utilisation au sein du contrôleur

On passe dorénavant à l’utilisation de notre Policy.

Il est possible de contrôler l’accès d’un utilisateur de plusieurs manières au sein du contrôleur. Dans le cas d’une utilisation sur une ou quelques méthodes spécifiques, il existe la méthode authorize :

1public function update(Request $request, Restaurant $restaurant)
2{
3 $this->authorize('update', $restaurant);
4 
5 // L'utilisateur est autorisé à éditer le restaurant ...
6}
7 
8public function create(Request $request)
9{
10 $this->authorize('create', Restaurant::class);
11 
12 // L'utilisateur est autorisé à créer un restaurant ...
13}

Dans le cas où on souhaiterait complètement déléguer le contrôle d’accès à la Policy, on peut définir la ressource cible dans le constructeur du contrôleur comme ceci :

1public function __construct()
2{
3 $this->authorizeResource(Restaurant::class, 'restaurant');
4}

L’alternative via les routes

D’une autre manière, il est possible d’appliquer les contrôles d’accès directement sur les routes via la méthode middleware :

1Route::get('/restaurants', [RestaurantController::class, 'index'])
2 ->name('restaurants.index')
3 ->middleware(['can:viewAny,App\Models\Restaurant']);
4 
5Route::get('/restaurants/create', [RestaurantController::class, 'create'])
6 ->name('restaurants.create')
7 ->middleware(['auth', 'can:create,App\Models\Restaurant']);
8 
9Route::get('/restaurants/{restaurant}/edit', [RestaurantController::class, 'edit'])
10 ->name('restaurants.edit')
11 ->middleware(['auth', 'can:update,restaurant']);
12 
13// ...

Accommoder l’affichage en fonction des accès

Pour des raisons d’expérience utilisateur, il est aussi important d’afficher à l’utilisateur uniquement ce qu’il est en droit de faire. Pour cela Blade fournit la directive @can :

1@can('create', App\Models\Restaurant::class)
2 <a href="{{ route('restaurants.create') }}">Créer un restaurant</a>
3@endcan
4 
5@can('update', $restaurant)
6 <a href="{{ route('restaurants.edit', $restaurant) }}">Editer le restaurant</a>
7@endcan

Nous avons dorénavant sécurisé les accès aux restaurants, c’est la fin de ce chapitre.

Dans le prochain épisode…

Pour ce dernier épisode, nous allons nous assurer du bon fonctionnement de notre application à travers les tests !

Source : https://github.com/laravel-fr/support-les-bases/tree/v5
William Suppo avatar
William Suppo
Je mange du PHP au p'tit dej', et vous ?

A lire

Autres articles de la même catégorie