13.x de la documentation officielle de Laravel ainsi que sur l’implémentation et les tests officiels de la dernière version de Framework, v13.42.0, vérifiés le 10 novembre 2026. Pour les bases de l’autorisation, consultez le guide d’autorisation. Pour le chargement des fournisseurs, consultez la découverte automatique des packages.
Ne pas dépendre de la classe User de l’application cliente
Prenons l’exemple de l’ajout d’une autorisationupdate au modèle Acme\Reports\Models\Report du package. On suppose que la table acme_reports possède une colonne owner_id qui stocke l’identifiant d’authentification du propriétaire. La manière de fournir les migrations est traitée dans les migrations de package.
src/Models/Report.php est un modèle Eloquent ordinaire.
App\Models\User, elle dépend de l’espace de noms et de la structure des modèles de l’application cliente. Si vous n’avez besoin que de l’identifiant d’authentification de l’utilisateur, utilisez le contrat Authenticatable de Laravel.
Placez le code suivant dans src/Policies/ReportPolicy.php.
null sont d’abord exclus, puis la différence de type entre un entier issu de la base de données et une chaîne est absorbée en comparant les deux sous forme de chaînes. Dans cet exemple, seul le propriétaire peut mettre à jour. Dans les applications qui gèrent plusieurs types d’utilisateurs ou plusieurs guards, vérifiez aussi que l’identifiant suffit à identifier le sujet de manière unique. Si ce n’est pas le cas, remplacez-la par votre propre policy qui tient compte des frontières de type d’utilisateur ou de tenant.
Authenticatable est un contrat portant notamment sur l’identifiant d’authentification. Il ne fournit ni isAdministrator() ni méthodes pour récupérer des rôles. Si vous avez besoin d’informations de permission propres au package, définissez un autre contrat et laissez l’application cliente injecter une implémentation.Enregistrer la correspondance dans le fournisseur de services
Dans leboot() de src/ReportsServiceProvider.php, n’enregistrez que les modèles qui appartiennent au package.
Gate::policy() enregistre le nom de classe de la policy avec le nom de classe du modèle comme clé. L’enregistrement lui-même ne crée pas la policy, n’interroge pas la base de données et n’évalue aucun utilisateur. La policy est créée avec make() du conteneur lorsqu’elle devient nécessaire, par exemple via Gate::getPolicyFor(). Vous pouvez donc injecter des dépendances dans son constructeur.
L’emplacement de Report et ReportPolicy convient aussi à la découverte automatique standard, mais l’enregistrement est ici explicite pour rendre clair ce que fournit le package. L’enregistrement seul n’applique aucune autorisation aux endpoints HTTP. Avant la mise à jour, appelez ce qui suit, par exemple dans un contrôleur.
AuthorizationException est levée, ce qui produit une réponse 403 dans le traitement HTTP habituel. Les arguments de la méthode de policy diffèrent entre update, qui reçoit un modèle, et les capacités comme create, qui reçoivent le nom de classe du modèle. Suivez les modes d’appel décrits dans le guide d’autorisation.
Lire l’ordre de résolution des policies
Gate::getPolicyFor() convertit une cible objet en son nom de classe, puis cherche dans l’ordre suivant. Il s’agit de l’ordre de sélection de la policy, pas de l’ordre d’exécution de l’ensemble des hooks d’autorisation.
Ainsi, tant qu’un enregistrement explicite existe pour la même classe
Report, modifier seulement #[UsePolicy] ou la convention de nommage ne remplace pas la policy. En revanche, si l’application cliente utilise un modèle qui hérite de Report, une policy trouvée via l’attribut propre de la classe enfant ou par déduction passe avant l’enregistrement explicite du parent. Vérifiez également avec la classe de modèle réellement utilisée.
Remplacer explicitement dans l’application cliente
Appeler à nouveaupolicy() pour le même modèle remplace la correspondance par le dernier enregistrement. Le placer simplement dans le boot() d’un autre fournisseur dépend de l’ordre de chargement. Une solution consiste à enregistrer un callback de fin de démarrage depuis AppServiceProvider::boot() de l’application cliente.
ApplicationReportPolicy est une policy implémentée par l’application cliente. Si le package enregistre dans un boot() normal, ce callback s’exécute après. Ce n’est pas un mécanisme qui l’emporte inconditionnellement, y compris face aux réenregistrements dans d’autres callbacks booted ou aux fournisseurs chargés plus tard. Dans cet exemple, le fournisseur d’enregistrement du package n’est pas un fournisseur différé.
Autre possibilité : laisser la correspondance inchangée et remplacer, dans le conteneur, la cible de résolution de ReportPolicy::class. resolvePolicy() demande la résolution au conteneur à chaque fois, mais l’obtention effective d’une nouvelle instance dépend du binding de cette policy. Évitez les conceptions qui récupèrent des informations d’utilisateur ou de requête dans le constructeur et les conservent dans une policy singleton.
Séparer les hooks qui affectent toute l’autorisation
Lors d’une vérification Gate normale, leGate::before() global s’exécute en premier. S’il renvoie autre chose que null, l’évaluation par la policy n’a pas lieu.
Ce diagramme ne montre que le chemin où une policy est trouvée et où la méthode correspondante peut être appelée. Il omet les hooks after globaux qui suivent, ainsi que le repli sur les définitions du Gate lorsque la policy n’a pas la méthode visée.
N’enregistrez pas un Gate::before() global qui autorise toutes les capacités simplement pour gérer les administrateurs dans votre package. Vous autoriseriez aussi les vérifications des autres packages et de l’application cliente. Un before() dans la policy se limite à cette policy, mais il n’est pas appelé lorsque la méthode de la capacité n’existe pas. Pour laisser la méthode suivante décider, renvoyez null plutôt que false.
Par ailleurs, le update() de cet exemple exige un Authenticatable non nullable, si bien que la méthode de policy ne s’exécute pas pour les invités. Sauf si un hook global remplace le résultat, l’accès est refusé. Pour autoriser les invités, par exemple pour une consultation publique, prévoyez séparément un argument utilisateur nullable et une vérification explicite.
Tester séparément l’enregistrement et l’évaluation
On suppose que vous utilisez la classe de base Testbench présentée dans les tests de package et quegetPackageProviders() charge ReportsServiceProvider::class. Voici un exemple de tests/Feature/ReportPolicyTest.php. Les utilisateurs sont représentés par le modèle d’authentification générique de Laravel, ce qui permet d’évaluer sans rien enregistrer en base.
forUser() ne modifie pas l’utilisateur du Gate partagé. Il crée un Gate distinct en lui transmettant les correspondances, capacités et callbacks du moment. Un Gate par utilisateur déjà créé ne reflète donc pas les modifications ultérieures des enregistrements de policies du Gate partagé. Terminez les enregistrements au démarrage et ne stockez pas de Gate par utilisateur dans des services à longue durée de vie.
Côté application cliente, il faut aussi des vérifications avec le guard et le modèle utilisateur réels, ainsi que des tests HTTP sur l’endpoint de mise à jour. Les tests ci-dessus ne vérifient ni que l’autorisation est appliquée à la route, ni le format de stockage de l’identifiant du propriétaire en base.
Contrats à préserver lors des mises à jour
- Les résultats pour le propriétaire, les autres utilisateurs, les invités et les enregistrements sans propriétaire sont-ils inchangés ?
- Les noms de capacités publiés correspondent-ils aux arguments des méthodes de policy ?
- Le remplacement de la policy par l’application cliente fonctionne-t-il toujours ?
- Si vous prenez en charge des modèles enfants, la policy réellement sélectionnée pour la classe enfant est-elle correcte ?
- Les vraies requêtes de mise à jour sont-elles autorisées ou refusées comme prévu, hooks globaux de l’application compris ?
Sources primaires consultées
- Documentation officielle de Laravel : Autorisation
- Documentation officielle de Laravel : Développement de packages
- Framework v13.42.0 : enregistrement, résolution et forUser du Gate
- Framework v13.42.0 : AuthServiceProvider, qui enregistre le Gate en singleton
- Framework v13.42.0 : boot et booted de Application
- Framework v13.42.0 : tests officiels du Gate d’autorisation