vendredi 25 septembre 2026

À propos

Contact

Tests

Tester les capacités personnalisées avec une matrice de rôles

Ajouter une capacité personnalisée sans vérifier systématiquement qui peut ou ne peut pas l'exercer laisse toujours un rôle oublié. Une matrice de test élimine ce risque.

Par Clément Hadrot • 11 août 2022 • 4 min de lecture • Aucun commentaire
Tester les capacités personnalisées avec une matrice de rôles

Une plateforme éditoriale multi-rédaction que nous maintenons a introduit une capacité personnalisée valider_article_partenaire, réservée aux rédacteurs en chef et aux administrateurs, pour distinguer la validation d’articles rédigés par des partenaires externes de la publication classique. Un audit de sécurité effectué six mois plus tard a révélé qu’un rôle « contributeur senior », ajouté entre-temps par une autre personne de l’équipe pour un besoin non lié, avait hérité de cette capacité par un mauvais copier-coller de définition de rôle. Aucun test n’avait détecté cette dérive, faute de couverture systématique croisant rôles et capacités.

Le problème n’était pas la capacité elle-même, correctement implémentée au départ, mais l’absence de garde-fou testant explicitement qui peut et qui ne peut pas l’exercer, à chaque évolution future des rôles.

Lister exhaustivement la matrice attendue

Avant d’écrire le moindre test, il faut formaliser noir sur blanc ce qui est attendu — ce tableau devient ensuite la référence pour la génération du test :

Rôlevalider_article_partenaire
administratorAutorisé
editor (rédacteur en chef)Autorisé
authorRefusé
contributorRefusé
contributeur_senior (personnalisé)Refusé
subscriberRefusé

Générer la matrice avec un data provider

Plutôt que d’écrire six méthodes de test presque identiques, un data provider parcourt la matrice définie ci-dessus et délègue à une seule méthode de test la vérification pour chaque rôle :

public static function matrice_roles_capacite(): array {
    return [
        'administrator autorisé'       => ['administrator', true],
        'editor autorisé'              => ['editor', true],
        'author refusé'                => ['author', false],
        'contributor refusé'           => ['contributor', false],
        'contributeur_senior refusé'   => ['contributeur_senior', false],
        'subscriber refusé'            => ['subscriber', false],
    ];
}

#[DataProvider('matrice_roles_capacite')]
public function test_capacite_valider_article_partenaire(string $role, bool $attendu): void {
    $utilisateur_id = $this->factory()->user->create(['role' => $role]);
    wp_set_current_user($utilisateur_id);

    $this->assertSame($attendu, current_user_can('valider_article_partenaire'));
}
L'essentiel à retenir : Lister tous les rôles concernés avant d'écrire le test ; Générer les combinaisons rôle par action automatiquement ; Vérifier aussi les rôles qui doivent être refusés

Ce test aurait immédiatement échoué au moment où le rôle « contributeur senior » a hérité par erreur de la capacité, puisque son entrée dans la matrice attend explicitement false.

Couvrir aussi map_meta_cap si la capacité en dépend

Certaines capacités ne s’évaluent pas uniquement sur le rôle global, mais aussi sur le contexte d’un article précis via map_meta_cap — par exemple, un rédacteur en chef ne peut valider que les articles de sa propre rédaction, pas ceux d’une rédaction sœur sur une plateforme multisite. Ce cas mérite une matrice distincte, croisant rôle et contexte d’article, plutôt que d’être mélangé à la vérification globale de capacité :

public function test_redacteur_en_chef_ne_valide_pas_hors_de_sa_redaction(): void {
    $redacteur_id = $this->factory()->user->create(['role' => 'editor']);
    update_user_meta($redacteur_id, 'redaction_id', 3);
    wp_set_current_user($redacteur_id);

    $article_autre_redaction = $this->factory()->post->create(['post_type' => 'article_partenaire']);
    update_post_meta($article_autre_redaction, 'redaction_id', 7);

    $this->assertFalse(
        current_user_can('valider_article_partenaire', $article_autre_redaction)
    );
}

Ne pas oublier les rôles ajoutés par des extensions tierces

  • Un rôle ajouté par une extension de commerce ou de gestion des membres peut hériter silencieusement de capacités du rôle sur lequel il se base (souvent subscriber ou author).
  • La matrice de test doit être révisée à chaque ajout de rôle personnalisé sur le projet, pas seulement à la création initiale de la capacité.
  • Un test de garde générique, qui vérifie qu’aucun rôle en dehors de la liste attendue ne possède la capacité, complète utilement la matrice explicite :
public function test_aucun_role_non_liste_ne_possede_la_capacite(): void {
    $roles_autorises = ['administrator', 'editor'];

    foreach (wp_roles()->roles as $slug_role => $donnees_role) {
        if (in_array($slug_role, $roles_autorises, true)) {
            continue;
        }
        $this->assertArrayNotHasKey(
            'valider_article_partenaire',
            $donnees_role['capabilities'],
            "Le rôle $slug_role ne devrait pas avoir cette capacité"
        );
    }
}

Une capacité personnalisée non testée par matrice de rôles est une porte qui reste ouverte jusqu’au jour où quelqu’un, sans mauvaise intention, ajoute un rôle qui hérite d’un peu trop de permissions.

En résumé

La matrice rôle par action transforme une vérification de sécurité difficile à maintenir de mémoire en une liste explicite, versionnée dans le code, que tout nouveau rôle ajouté au projet doit venir compléter. Ce test ne remplace pas une compréhension fine de map_meta_cap, mais garantit qu’aucune capacité sensible ne se retrouve accordée à un rôle par accident, sans qu’un test ne le signale immédiatement.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi