All, AtLeastOneOf, When : composer des politiques d’accès

Trois combinateurs transforment les politiques d’accès en arbre : toutes, au moins une, ou seulement quand une condition tient. Ce sont des attributs, comme les politiques qu’ils enveloppent, et le profileur montre la forme plutôt qu’une liste plate.

Les trois sont répétables, les trois prennent un message qui leur est propre, et les trois s’imbriquent.

All

Toute politique imbriquée doit accorder. Il coupe court au premier refus.

#[All([
    new AccessPolicy('EDIT', new Argument('post')),
    new AccessPolicy('ROLE_EDITOR'),
])]

Deux attributs #[IsGranted] sur la même action forment une conjonction implicite ; All est la même exigence énoncée à voix haute. La différence apparaît au moment du refus : le panneau nomme l’opérateur et la branche qui a fermé la porte, au lieu de deux décisions côte à côte sans relation déclarée.

AtLeastOneOf

Une politique qui accorde suffit. Il coupe court à la première autorisation.

#[AtLeastOneOf([
    new AccessPolicy('EDIT', new Argument('post')),
    new AccessPolicy('ROLE_ADMIN'),
])]

Sa forme de tous les jours : le propriétaire, ou quelqu’un qui a autorité pour agir en son nom. Écrite en une seule expression, elle marcherait aussi, et elle cesserait de dire laquelle des deux a laissé passer la requête.

When

N’exige les politiques imbriquées que lorsqu’une condition tient, et s’efface sinon, en s’abstenant plutôt qu’en refusant.

#[When(new Expression('request.isMethod("POST")'), [
    new AccessPolicy('EDIT', new Argument('post')),
])]

La condition parle des circonstances de l’appel, pas du demandeur. Le point d’entrée remet son propre contexte : une méthode HTTP s’atteint par request.isMethod("POST"), une option de console par input.getOption("force"). C’est ce que faisait le paramètre methods d’#[IsGranted], généralisé, et c’est ce qui distingue un appel web d’un appel en console.

La condition est une Expression et non une chaîne, délibérément. Une chaîne nue serait compilée en silence comme telle, ce qui est exactement le piège pour lequel le votant d’expression a dû être corrigé : une valeur qui a l’air d’une donnée et qui se comporte comme du code, sans rien sur le site d’appel pour vous dire laquelle des deux c’est.

Relire l’arbre

L’évaluateur annonce chaque nœud qu’il évalue, avec son parent, et c’est ce qui permet au panneau du profileur de montrer l’opérateur et ses branches :

  • All(A, B) montre l’opérateur avec les deux branches en dessous, et non deux décisions sans rapport.
  • AtLeastOneOf(A, B) où A accorde montre l’opérateur et la seule branche qu’il a atteinte, au lieu d’avoir l’air d’une simple politique sur A.
  • When(false, ...) montre qu’il s’est effacé et pourquoi, au lieu de ne rien montrer du tout.

Ce dernier point est la raison pour laquelle le panneau juge du vide sur les questions posées plutôt que sur les décisions prises. Un When qui s’efface ne prend aucune décision, et une version antérieure du panneau annonçait « aucune décision d’accès n’a été prise » sur une page qui était pourtant gardée.

Les composites de votre cru reçoivent le même traitement sans qu’il faille en écrire une ligne, parce que l’arbre est bâti par l’évaluateur et non par les attributs. En écrire un fait l’objet d’un article plus loin dans cette série ; la page des politiques d’accès couvre les trois qui sont fournis.