L’Autorisation avec les Policies (Stratégies)
Les Policies (Stratégies) sont des classes qui organisent la logique d’autorisation autour d’un Modèle (Model) particulier. Elles sont beaucoup plus structurées que les Gates et sont idéales pour regrouper toute la logique d’autorisation complexe liée à une ressource d’application métiers.
Créer des Policies
Laravel fournit une commande Artisan pour générer rapidement une Policy associée à un Modèle :
# Crée une Policy dédiée au modèle Post
php artisan make:policy PostPolicy --model=Post
Cela crée le fichier app/Policies/PostPolicy.php avec toutes les méthodes CRUD de base pré-générées :
<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
/**
* L'utilisateur peut-il voir LA LISTE de TOUS les articles ? (index)
*/
public function viewAny(User $user): bool
{
return true; // N'importe quel utilisateur connecté peut voir la liste
}
/**
* L'utilisateur peut-il voir UN article spécifique ? (show)
*/
public function view(User $user, Post $post): bool
{
// Autorisé si : L'article est public OU l'utilisateur est l'auteur
return $post->is_public || $user->id === $post->user_id;
}
/**
* L'utilisateur peut-il CRÉER de nouveaux articles ? (create/store)
*/
public function create(User $user): bool
{
// Autorisé uniquement si l'adresse email a été vérifiée
return $user->email_verified_at !== null;
}
/**
* L'utilisateur peut-il MODIFIER cet article ? (edit/update)
*/
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
/**
* L'utilisateur peut-il SUPPRIMER cet article ? (destroy)
*/
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
/**
* L'utilisateur peut-il RESTAURER cet article (Corbeille/Soft Deletes) ?
*/
public function restore(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
/**
* L'utilisateur peut-il SUPPRIMER DÉFINITIVEMENT cet article ?
*/
public function forceDelete(User $user, Post $post): bool
{
return $user->is_admin; // Seul un admin
}
}
Enregistrer les Policies
Laravel “découvre” automatiquement les policies par convention de nommage, tant que vos dossiers respectent la norme :
Modèle : App\Models\Post
Policy : App\Policies\PostPolicy
Si vous avez une structure personnalisée, vous devez enregistrer manuellement le mapping dans AppServiceProvider :
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
// Indique à Laravel que PostPolicy décide des droits du modèle Post
Gate::policy(Post::class, PostPolicy::class);
}
Utiliser les Policies
La beauté des Policies réside dans le fait qu’une fois créées, elles s’utilisent exactement de la même manière que les Gates !
Dans les Contrôleurs
Le helper $this->authorize() est votre meilleur ami dans les contrôleurs :
class PostController extends Controller
{
public function index()
{
// Puisqu'il n'y a pas encore d'objet $post, on passe le Nom de la Classe Modèle
$this->authorize('viewAny', Post::class);
return view('posts.index', ['posts' => Post::all()]);
}
public function show(Post $post)
{
// Laravel sait automatiquement qu'il doit cibler la vue `view` de `PostPolicy` !
$this->authorize('view', $post);
return view('posts.show', compact('post'));
}
public function edit(Post $post)
{
$this->authorize('update', $post);
return view('posts.edit', compact('post'));
}
public function update(Request $request, Post $post)
{
// Va lever une exception 403 HTTP si $post->user_id != l'utilisateur actuel
$this->authorize('update', $post);
$post->update($request->validated());
return redirect()->route('posts.show', $post);
}
public function destroy(Post $post)
{
$this->authorize('delete', $post);
$post->delete();
return redirect()->route('posts.index');
}
}
Via le Modèle Utilisateur en PHP
if ($user->can('update', $post)) {
// L'utilisateur peut modifier
}
if ($user->cannot('delete', $post)) {
// L'utilisateur ne peut pas supprimer
}
// Pour des méthodes qui n'ont pas encore d'instance (comme 'create')
if ($user->can('create', Post::class)) {
// L'utilisateur a le droit d'afficher le formulaire de création
}
Via la Façade Gate
if (Gate::allows('update', $post)) {
// Autorisé
}
Gate::authorize('delete', $post); // Lance l'erreur 403
Dans les Vues Blade
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Modifier</a>
@endcan
@can('create', App\Models\Post::class)
<a href="{{ route('posts.create') }}">Créer un Article</a>
@endcan
@cannot('delete', $post)
<p>Vous n'avez pas le profil requis pour supprimer ceci.</p>
@endcannot
Via Middleware (Le moyen le plus propre !)
Sécurisez directement vos routes avec le middleware intégré can :
// Fichier: routes/web.php
// Structure : can:action,nom_du_parametre_de_la_route
Route::put('/posts/{post}', [PostController::class, 'update'])
->middleware('can:update,post');
// Pour une action sans modèle existant (index, create, store)
Route::post('/posts', [PostController::class, 'store'])
->middleware('can:create,App\Models\Post');
Référence des Méthodes de Policy
Si vous utilisez des Resources Controllers (Route::resource), il y a une correspondance officielle entre les méthodes classiques et le nom des capacités (abilities) de la Policy.
| Méthode Policy | Méthode du Contrôleur (Route) | Paramètre Modèle Requis ? |
|---|---|---|
viewAny |
index |
Non |
view |
show |
Oui |
create |
create, store |
Non |
update |
edit, update |
Oui |
delete |
destroy |
Oui |
restore |
restore (Corbeille) |
Oui |
forceDelete |
forceDelete |
Oui |
La Méthode Initiale before() (Super Admin)
Si vous définissez une méthode before sur une Policy, elle s’exécutera avant toute autre méthode de cette Policy. C’est l’endroit parfait pour donner tous les droits à un Administrateur Système.
public function before(User $user, string $ability): bool|null
{
// Les Admins peuvent tout faire sans même devoir vérifier la logique spécifique au post !
if ($user->is_admin) {
return true;
}
// Retourner null laisse Laravel poursuivre vers la méthode d'autorisation habituelle (ex: update())
return null;
}
Utilisateurs Invités (Guest Users)
Par défaut, si quelqu’un n’est pas connecté, Laravel retourne false immédiatement sans même exécuter la Policy. Vous pouvez modifier ce comportement en rendant l’utilisateur de la Policy typé nullable :
// Ajouter un point d'interrogation ?User pour autoriser les non-connectés à passer dans la fonction
public function view(?User $user, Post $post): bool
{
// N'importe qui sur Internet a le droit de lire un article public
if ($post->is_public) {
return true;
}
// Si c'est privé, on vérifie si la personne (qui peut être nulle) est le bon propriétaire
return $user?->id === $post->user_id;
}
Réponses Détaillées (Policy Responses)
Comme pour les Gates, vous pouvez renvoyer une réponse explicite plutôt qu’un bête false :
use Illuminate\Auth\Access\Response;
public function update(User $user, Post $post): Response
{
if ($user->id !== $post->user_id) {
return Response::deny("Vous n'êtes pas propriétaire de cet article.");
}
if ($post->is_locked) {
// En frontend ($response->message()), on pourra afficher ce texte !
return Response::deny("Cet article a été verrouillé par la modération.");
}
return Response::allow();
}
Ressources
- Autorisation - Stratégies (Policies) — Documentation officielle sur les Policies
📘 Cette leçon fait partie du cours Authentification et Autorisation Laravel sur la plateforme d’apprentissage RostoDev.
Prêt à passer à la suite ?
Vous avez terminé cette leçon sur L'Autorisation avec les Policies (Stratégies). Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.