> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Autorisation (Gates et Policies)

> Comment gérer les droits d'action des utilisateurs avec les Gates et Policies de Laravel.

## Différence entre authentification et autorisation

L'**authentification** vérifie « qui » est l'utilisateur (par exemple via la connexion).
L'**autorisation** décide « ce qu'il peut faire » : n'éditer que ses propres publications, seuls les administrateurs modifient les paramètres, etc.

Après avoir implémenté la connexion via l'[authentification](./authentication), l'étape suivante est l'autorisation.

<Info>
  Laravel propose deux approches d'autorisation : les Gates et les Policies. Le choix dépend de votre cas d'usage.
</Info>

**Guide de choix**

| Scénario                                   | Recommandation |
| ------------------------------------------ | -------------- |
| Décision simple non liée à un modèle       | Gate           |
| Gestion CRUD sur un modèle                 | Policy         |
| Contrôle d'accès au dashboard admin        | Gate           |
| Droits CRUD sur les publications d'un blog | Policy         |

## Gates

Les Gates sont des vérifications d'autorisation simples basées sur des closures. Idéales pour les jugements non liés à un modèle particulier.

### Flux d'autorisation via Gate

```mermaid theme={null}
flowchart TD
    A["AppServiceProvider::boot()<br>(App\\Providers\\AppServiceProvider)"] --> B["Gate::define('ability', fn)<br>Enregistrement de la logique"]
    C["Contrôle depuis un contrôleur"] --> D{"Gate::before()<br>défini ?"}
    D -->|"Oui"| E{"Retour de before()"}
    E -->|"true / false"| F["Utilisation du résultat de before()"]
    E -->|"null"| G["Exécution de la closure de define()"]
    D -->|"Non"| G
    G --> H{"Retour de la closure"}
    H -->|"true"| I["✓ Accès autorisé"]
    H -->|"false"| J["✗ Accès refusé"]
    F -->|"true"| I
    F -->|"false"| J
    J -->|"Gate::allows()"| K["Retourne false<br>→ abort(403) manuel"]
    J -->|"Gate::authorize()"| L["AuthorizationException<br>→ 403 Forbidden automatique"]
```

### Définir une Gate

Définissez la Gate dans la méthode `boot` de `App\Providers\AppServiceProvider` via `Gate::define()`.

```php theme={null}
// app/Providers/AppServiceProvider.php

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
```

La closure reçoit toujours l'utilisateur authentifié en premier argument, puis le modèle ou d'autres informations.

### Contrôler la permission

Dans un contrôleur, utilisez `Gate::allows()` ou `Gate::denies()`.

```php theme={null}
use Illuminate\Support\Facades\Gate;

public function update(Request $request, Post $post): RedirectResponse
{
    if (! Gate::allows('update-post', $post)) {
        abort(403);
    }

    // Mise à jour...

    return redirect('/posts');
}
```

### `Gate::authorize()` pour lever une exception

Pour retourner automatiquement une réponse 403 en cas d'échec, utilisez `Gate::authorize()` (pas besoin d'écrire `abort(403)`).

```php theme={null}
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);

    // Ici la permission est accordée
    // Mise à jour...

    return redirect('/posts');
}
```

<Tip>
  `Gate::authorize()` lève `Illuminate\Auth\Access\AuthorizationException` en cas de refus, que Laravel convertit automatiquement en 403.
</Tip>

### Bypass administrateur (`before`)

Pour donner tous les droits aux administrateurs, utilisez `Gate::before()`.

```php theme={null}
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    // $ability contient le nom de la Gate en cours ('update-post', etc.)
    Gate::before(function (User $user, string $ability) {
        if ($user->isAdministrator()) {
            return true;
        }
    });

    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
```

Si la closure `before` retourne une valeur non nulle, cette valeur devient le résultat. Si elle retourne `null` (ou rien), le contrôle normal se poursuit.

### `@can` / `@cannot` dans Blade

Dans les templates, les directives `@can` et `@cannot` sont pratiques.

```blade theme={null}
@can('update-post', $post)
    <a href="{{ route('posts.edit', $post) }}">Modifier</a>
@endcan

@cannot('update-post', $post)
    <p>Vous n'avez pas la permission de modifier cette publication.</p>
@endcannot
```

## Policies

Une Policy regroupe la logique d'autorisation d'un modèle particulier dans une classe. Pour gérer les droits CRUD sur `Post`, les Policies conviennent mieux que les Gates.

### Flux d'autorisation via Policy

```mermaid theme={null}
flowchart TD
    A["Requête HTTP"] --> B["Contrôleur"]
    B --> C["Gate::authorize('update', $post)"]
    C --> D{"PostPolicy::before()<br>définie ?"}
    D -->|"Oui"| E{"Retour de before()"}
    E -->|"true / false"| F["Utilisation du résultat de before()"]
    E -->|"null"| G["Exécution de PostPolicy::update(User, Post)"]
    D -->|"Non"| G
    G --> H{"$user->id === $post->user_id ?"}
    H -->|"true"| I["✓ Poursuite du traitement"]
    H -->|"false"| J["✗ AuthorizationException<br>→ 403 Forbidden"]
    F -->|"true"| I
    F -->|"false"| J
```

### Générer une Policy

Utilisez `make:policy`.

```shell theme={null}
php artisan make:policy PostPolicy
```

Pour générer les méthodes CRUD standard, utilisez `--model`.

```shell theme={null}
php artisan make:policy PostPolicy --model=Post
```

`app/Policies/PostPolicy.php` est créé.

### Détection automatique modèle / policy

Par défaut, Laravel détecte la Policy selon la convention de nommage.

* Modèle : `app/Models/Post.php`
* Policy : `app/Policies/PostPolicy.php`

Aucun enregistrement manuel n'est nécessaire si vous respectez la convention.

<Info>
  Sinon, ou pour enregistrer manuellement, utilisez `Gate::policy()` dans `AppServiceProvider::boot`.

  ```php theme={null}
  use App\Models\Post;
  use App\Policies\PostPolicy;
  use Illuminate\Support\Facades\Gate;

  Gate::policy(Post::class, PostPolicy::class);
  ```

  Sous Laravel 13, vous pouvez aussi utiliser l'attribut `#[UsePolicy]`.

  ```php theme={null}
  use App\Policies\PostPolicy;
  use Illuminate\Database\Eloquent\Attributes\UsePolicy;

  #[UsePolicy(PostPolicy::class)]
  class Post extends Model {}
  ```
</Info>

### Implémenter les méthodes

Une Policy générée avec `--model` contient les méthodes CRUD standard.

```php theme={null}
<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * Peut consulter la liste des publications ?
     */
    public function viewAny(User $user): bool
    {
        return true; // Tout utilisateur authentifié
    }

    /**
     * Peut consulter une publication ?
     */
    public function view(User $user, Post $post): bool
    {
        return true;
    }

    /**
     * Peut créer une publication ?
     */
    public function create(User $user): bool
    {
        return true;
    }

    /**
     * Peut mettre à jour une publication ?
     */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id; // Uniquement ses propres publications
    }

    /**
     * Peut supprimer une publication ?
     */
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}
```

### Bypass administrateur (`before`)

Les Policies acceptent aussi une méthode `before`.

```php theme={null}
/**
 * Contrôle préalable exécuté avant les autres méthodes.
 * $ability contient le nom de la méthode ('update', 'delete'...).
 */
public function before(User $user, string $ability): bool|null
{
    if ($user->isAdministrator()) {
        return true; // Tout est permis pour l'admin
    }

    return null; // Poursuivre avec la méthode normale
}
```

<Warning>
  `before` n'est appelé que si la méthode correspondante existe dans la Policy. Sans méthode `update`, `before` n'est pas appelé non plus.
</Warning>

### Ordre d'exécution du callback `before`

```mermaid theme={null}
flowchart TD
    A["Début de la vérification<br>authorize('update', $post)"] --> B["Exécution de PostPolicy::before()"]
    B --> C{"Valeur de retour : null ?"}
    C -->|"non-null (true/false)"| D["Résultat de before() utilisé"]
    C -->|"null"| E["Exécution de la méthode normale<br>update() / delete() etc."]
    E --> F{"Retour de la méthode"}
    F -->|"true"| G["✓ Accès autorisé"]
    F -->|"false"| H["✗ Accès refusé → 403"]
    D -->|"true (bypass admin)"| G
    D -->|"false"| H
```

## Utiliser une Policy dans un contrôleur

### Méthode `authorize()`

Le contrôleur de base Laravel expose `authorize()` (utilisez `Gate::authorize()` sinon).

<Tabs>
  <Tab title="Gate::authorize()">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\RedirectResponse;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Gate;

    class PostController extends Controller
    {
        public function update(Request $request, Post $post): RedirectResponse
        {
            Gate::authorize('update', $post);

            // Mise à jour...

            return redirect()->route('posts.index');
        }

        public function store(Request $request): RedirectResponse
        {
            Gate::authorize('create', Post::class);

            // Création...

            return redirect()->route('posts.index');
        }

        public function destroy(Post $post): RedirectResponse
        {
            Gate::authorize('delete', $post);

            $post->delete();

            return redirect()->route('posts.index');
        }
    }
    ```
  </Tab>

  <Tab title="User::can() / cannot()">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\RedirectResponse;
    use Illuminate\Http\Request;

    class PostController extends Controller
    {
        public function update(Request $request, Post $post): RedirectResponse
        {
            if ($request->user()->cannot('update', $post)) {
                abort(403);
            }

            // Mise à jour...

            return redirect()->route('posts.index');
        }
    }
    ```
  </Tab>
</Tabs>

### `authorizeResource()` pour un contrôleur RESTful

`authorizeResource()` associe automatiquement chaque action du contrôleur à la méthode correspondante de la Policy.

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }

    // Les méthodes index(), show(), create(), store(), edit(), update(), destroy()
    // sont automatiquement liées aux méthodes de policy correspondantes
}
```

Correspondance :

| Action du contrôleur | Méthode de Policy |
| -------------------- | ----------------- |
| `index`              | `viewAny`         |
| `show`               | `view`            |
| `create` / `store`   | `create`          |
| `edit` / `update`    | `update`          |
| `destroy`            | `delete`          |

<Tip>
  `authorizeResource()` évite d'appeler `authorize()` dans chaque action. Idéal avec les contrôleurs de ressource REST.
</Tip>

## Autorisation via middleware

Pour vérifier au niveau de la route, utilisez le middleware `can`.

```php theme={null}
use App\Models\Post;

// Accessible aux utilisateurs autorisés à mettre à jour
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

// Accessible aux utilisateurs autorisés à créer
Route::post('/posts', [PostController::class, 'store'])
    ->middleware('can:create,App\Models\Post');
```

Version plus concise avec la méthode `can` :

```php theme={null}
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->can('update', 'post');
```

## Utiliser une Policy dans Blade

Une fois la Policy enregistrée, `@can` / `@cannot` l'utilisent automatiquement.

```blade theme={null}
{{-- Bouton d'édition visible uniquement pour les utilisateurs autorisés --}}
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}" class="btn">Modifier</a>
@endcan

{{-- Bouton de suppression --}}
@can('delete', $post)
    <form method="POST" action="{{ route('posts.destroy', $post) }}">
        @csrf
        @method('DELETE')
        <button type="submit">Supprimer</button>
    </form>
@endcan

{{-- Bouton de création --}}
@can('create', App\Models\Post::class)
    <a href="{{ route('posts.create') }}">Nouvelle publication</a>
@endcan
```

## Exemple : gestion des droits d'un blog

Exemple d'application blog où « seul l'auteur peut modifier/supprimer sa publication ».

<Steps>
  <Step title="Générer la Policy">
    ```shell theme={null}
    php artisan make:policy PostPolicy --model=Post
    ```
  </Step>

  <Step title="Implémenter les méthodes">
    ```php theme={null}
    <?php

    namespace App\Policies;

    use App\Models\Post;
    use App\Models\User;

    class PostPolicy
    {
        /**
         * Bypass administrateur.
         */
        public function before(User $user, string $ability): bool|null
        {
            if ($user->is_admin) {
                return true;
            }

            return null;
        }

        public function viewAny(User $user): bool
        {
            return true;
        }

        public function view(User $user, Post $post): bool
        {
            return true;
        }

        public function create(User $user): bool
        {
            return true;
        }

        public function update(User $user, Post $post): bool
        {
            return $user->id === $post->user_id;
        }

        public function delete(User $user, Post $post): bool
        {
            return $user->id === $post->user_id;
        }
    }
    ```
  </Step>

  <Step title="Ajouter `authorizeResource()` au contrôleur">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\RedirectResponse;
    use Illuminate\Http\Request;
    use Illuminate\View\View;

    class PostController extends Controller
    {
        public function __construct()
        {
            $this->authorizeResource(Post::class, 'post');
        }

        public function index(): View
        {
            $posts = Post::latest()->paginate(10);
            return view('posts.index', compact('posts'));
        }

        public function store(Request $request): RedirectResponse
        {
            $post = $request->user()->posts()->create(
                $request->validate([
                    'title' => ['required', 'string', 'max:255'],
                    'body'  => ['required', 'string'],
                ])
            );

            return redirect()->route('posts.show', $post);
        }

        public function update(Request $request, Post $post): RedirectResponse
        {
            $post->update(
                $request->validate([
                    'title' => ['required', 'string', 'max:255'],
                    'body'  => ['required', 'string'],
                ])
            );

            return redirect()->route('posts.show', $post);
        }

        public function destroy(Post $post): RedirectResponse
        {
            $post->delete();

            return redirect()->route('posts.index');
        }
    }
    ```
  </Step>

  <Step title="Contrôle des droits dans Blade">
    ```blade theme={null}
    {{-- resources/views/posts/show.blade.php --}}
    <h1>{{ $post->title }}</h1>
    <p>{{ $post->body }}</p>
    <p>Auteur : {{ $post->user->name }}</p>

    @can('update', $post)
        <a href="{{ route('posts.edit', $post) }}">Modifier</a>
    @endcan

    @can('delete', $post)
        <form method="POST" action="{{ route('posts.destroy', $post) }}">
            @csrf
            @method('DELETE')
            <button type="submit">Supprimer</button>
        </form>
    @endcan
    ```
  </Step>
</Steps>

## Récapitulatif

<AccordionGroup>
  <Accordion title="Choix entre Gate et Policy">
    * **Gate** : jugements simples non liés à un modèle (accès au dashboard admin, droit global de configuration).
    * **Policy** : gestion CRUD sur un modèle (`Post`, `Order`, `Comment`, etc.).
  </Accordion>

  <Accordion title="APIs fréquentes">
    ```php theme={null}
    // Gate
    Gate::allows('update-post', $post);      // true/false
    Gate::denies('update-post', $post);      // true/false
    Gate::authorize('update-post', $post);   // exception en cas d'échec

    // Depuis le modèle utilisateur
    $user->can('update', $post);     // true/false
    $user->cannot('update', $post);  // true/false

    // Dans un contrôleur
    Gate::authorize('update', $post);        // 403 si refus

    // Dans Blade
    @can('update', $post) ... @endcan
    @cannot('update', $post) ... @endcannot
    ```
  </Accordion>

  <Accordion title="Commandes Artisan utiles">
    ```shell theme={null}
    # Policy vide
    php artisan make:policy PostPolicy

    # Avec méthodes CRUD pour un modèle
    php artisan make:policy PostPolicy --model=Post
    ```
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel Telescope](/fr/telescope.md)
- [Chiffrement (Encryption)](/fr/encryption.md)
- [Gestion des erreurs](/fr/error-handling.md)
- [Laravel Sentinel — analyse du middleware de protection des routes](/fr/blog/sentinel-introduction.md)
- [Laravel Horizon](/fr/horizon.md)
