Huit routes personnalisées, huit fois la même ligne current_user_can( 'manage_options' ) recopiée dans huit fichiers différents : ce constat, fait sur un projet client au moment d’auditer une extension métier, résume un problème d’architecture bien plus qu’un problème de sécurité isolé. Chaque copie de cette vérification est une occasion supplémentaire d’oublier de la mettre à jour le jour où la capacité change.
Enregistrer une route personnalisée avec register_rest_route() impose de fournir un permission_callback, une fonction chargée de dire si la requête est autorisée à s’exécuter. Sur un projet avec plusieurs routes protégées par la même règle métier — un rôle donné, une capacité personnalisée, une appartenance à un groupe —, écrire cette vérification en dur dans chaque route revient à multiplier les points de défaillance en cas d’évolution de la règle.
Le problème concret de la duplication
Quand une règle de permission change — par exemple, remplacer manage_options par une capacité personnalisée plus fine gerer_mon_extension — il faut repérer et modifier chaque occurrence dispersée dans le code. Un oubli laisse une route protégée par l’ancienne règle, ce qui peut soit bloquer un utilisateur qui devrait avoir accès, soit, plus grave, laisser un accès trop large sur une route sensible que la migration a manqué.

Construire une fabrique de fonctions de permission
La solution consiste à écrire une fonction qui, au lieu de vérifier directement une permission, retourne une fonction de vérification paramétrée par la capacité demandée. Ce motif s’appuie sur les fonctions anonymes de PHP, disponibles depuis longtemps dans le langage :
function mon_extension_fabrique_permission( string $capacite ): callable {
return function ( WP_REST_Request $requete ) use ( $capacite ) {
if ( ! current_user_can( $capacite ) ) {
return new WP_Error(
'rest_forbidden',
'Vous n\'avez pas la permission d\'effectuer cette action.',
array( 'status' => 403 )
);
}
return true;
};
}
Chaque route appelle ensuite cette fabrique avec la capacité qui lui est propre, sans jamais réécrire la logique de vérification elle-même :
register_rest_route( 'mon-extension/v1', '/commandes', array(
'methods' => 'GET',
'callback' => 'mon_extension_lister_commandes',
'permission_callback' => mon_extension_fabrique_permission( 'gerer_mon_extension' ),
) );
register_rest_route( 'mon-extension/v1', '/statistiques', array(
'methods' => 'GET',
'callback' => 'mon_extension_lire_statistiques',
'permission_callback' => mon_extension_fabrique_permission( 'voir_statistiques_extension' ),
) );
Ce que ce motif apporte concrètement
La logique de vérification — retourner une erreur explicite avec le bon code HTTP, plutôt qu’un simple booléen — n’existe désormais qu’à un seul endroit. Modifier le message d’erreur, ajouter une journalisation des tentatives refusées, ou changer le code de statut retourné se fait en un seul point du code, appliqué instantanément à toutes les routes qui utilisent la fabrique.
- Chaque route conserve sa propre capacité, sans perdre en granularité.
- La logique commune de refus (message, code HTTP, format d’erreur) reste centralisée.
- Ajouter une route protégée supplémentaire ne demande qu’un appel à la fabrique, pas une nouvelle fonction complète.
Aller plus loin : combiner plusieurs conditions
Ce motif se généralise facilement à des règles plus complexes, en acceptant plusieurs capacités ou une fonction de condition personnalisée :
function mon_extension_fabrique_permission_ou( array $capacites ): callable {
return function () use ( $capacites ) {
foreach ( $capacites as $capacite ) {
if ( current_user_can( $capacite ) ) {
return true;
}
}
return new WP_Error( 'rest_forbidden', 'Accès refusé.', array( 'status' => 403 ) );
};
}
Une règle de permission écrite une seule fois et réutilisée partout se maintient ; la même règle copiée huit fois finit toujours par diverger d’une route à l’autre.
En résumé
Factoriser la vérification de permission derrière une fonction de fabrique ne change rien au comportement observé par l’utilisateur final, mais transforme profondément la maintenabilité d’une API REST personnalisée. Le gain se mesure le jour où une règle de sécurité doit évoluer : un seul endroit à modifier, plutôt qu’un audit complet de chaque route pour vérifier qu’aucune n’a été oubliée.