Il n’y a pas de Not, et c’est délibéré

Le composant fournit All, AtLeastOneOf et When, et pas de Not. Ce n’est pas un oubli, et ce n’est pas une tâche en attente. Il y a deux raisons, et chacune suffirait à elle seule.

La négation de trois valeurs n’est pas définie

Un votant répond accorder, refuser ou s’abstenir. Nier un booléen va de soi ; nier ces trois valeurs, non.

L’autorisation devient un refus, le refus devient une autorisation, et vient alors la question intéressante : que devient une abstention ? Faites-en une autorisation et « personne n’avait rien à dire » devient « accès accordé », ce qui est la panne contre laquelle tout ce composant est bâti. Faites-en un refus et l’opérateur n’est plus une négation, c’est une exigence que quelqu’un s’oppose.

Laissez-la telle quelle et vous avez un opérateur dont le résultat dépend d’un réglage déclaré ailleurs, ce qui est étrange pour un mot aussi petit que non.

Les permissions négatives sont un piège connu

Supposons la sémantique tranchée. C’est la règle que vous écririez avec qui pose problème.

Not(ROLE_ADMIN) autorise tout le monde sauf les administrateurs, et tout le monde comprend le visiteur anonyme qui ne porte aucun rôle. L’intention était presque toujours « un utilisateur connecté qui n’est pas administrateur », et la règle telle qu’elle est écrite dit quelque chose de bien plus large.

C’est un terrain rebattu de la littérature sur le contrôle d’accès : les autorisations négatives s’entendent mal avec les valeurs par défaut, avec les hiérarchies et entre elles, ce pourquoi XACML dépense sa complexité en algorithmes de combinaison plutôt qu’en négation. Les permissions positives se composent. Les négatives accumulent des exceptions.

Ce qu’il faut écrire à la place

  • Nommez l’attribut positif. Si la règle est « pas administrateur », il y a en général un état que vous visez vraiment : un simple membre, un compte en attente, une adresse non vérifiée. Donnez-lui un nom et un votant, et il devient lisible et testable.
  • Employez un votant, là où le refus est la règle. Un votant qui refuse quand le compte est suspendu le dit avec une raison, qu’une négation ne porte jamais.
  • Employez une expression, quand la condition est un calcul ou une propriété du sujet plutôt qu’une question pour les votants.

Et si vous en voulez un quand même

Rien ne vous en empêche. Une politique est n’importe quel objet qui implémente AccessPolicyInterface, et un gestionnaire est ce qui l’évalue : un Not côté application est un attribut et un gestionnaire dans votre propre src/, sans rien changer au composant ni le forker.

Le composant fournit même l’outillage de test pour cela. AccessPolicyHandlerTestTrait offre deux tests gratuits à un gestionnaire, et FixedOutcomeAccessPolicy répond ce que vous lui dites de répondre, si bien qu’un composite s’éprouve sans le moindre votant, gestionnaire ni demandeur en vue. La documentation prend un gestionnaire Not comme exemple travaillé, ce qui dit assez clairement que la porte est ouverte et que le choix vous revient.

Sequentially manque pour une raison plus terne : All et AtLeastOneOf coupent déjà court, donc ce serait un second nom pour quelque chose qui existe.