Refusé à voix haute plutôt que répondu faux

Un composant qui fait le pont entre deux écosystèmes doit décider quoi faire de ce qu’il ne sait pas transporter. Celui-ci lève, à la compilation quand il le peut, et la liste de ce qu’il refuse est un document à part entière.

Le principe derrière chaque ligne de cette liste : un refus qu’un appelant ne peut pas distinguer d’un vrai relève de la même famille de faute qu’une garde qui laisse passer. Les deux sont silencieux, et le silence est ce qui fait survivre un bogue d’autorisation pendant des mois.

Ce qui lève

  • L’ACL au niveau du champ. Le troisième argument d’is_granted() passe par symfony/acl, qu’aucun votant ici ne comprend. La fonction Twig lève, et une application qui l’emploie ne peut pas installer le bundle.
  • La même chose déclarée deux fois. Deux algorithmes de combinaison, deux jeux de règles d’URL, deux hiérarchies de rôles, deux valeurs contradictoires d’allow_if_all_abstain : toutes lèvent à la compilation.
  • Une fonction de gabarit que Security ajouterait plus tard. Les fonctions déléguées sont nommées une à une, la liste est comparée à l’extension de Security, et une inconnue arrête la compilation au lieu de disparaître de vos gabarits.
  • Une garde de workflow que personne ne peut appliquer. Sans Security ni ce bundle pour y répondre, le conteneur refuse de se construire plutôt que de compiler une transition que personne ne garde.
  • Une politique qu’aucun gestionnaire ne prend en charge, et un argument qui n’existe pas, et deux algorithmes de combinaison qui partagent un nom. Même raisonnement, un étage plus bas.

Le deuxième point a un chiffre attaché, et c’est bien pourquoi la règle existe : quatre des sept formes de security.access_decision_manager étaient ignorées en silence par une version antérieure de la passerelle. Ignorer une clé qu’on ne sait pas traiter, c’est ainsi qu’une passerelle relâche l’accès sans le dire.

Ce qui est nommé autrement, exprès

  • Les algorithmes de combinaison portent leurs noms XACML plutôt que ceux de Security, et la correspondance est exacte et documentée.
  • access_control_decision(), et non access_decision(), parce que les formes divergent : une énumération ici là où Security a un booléen, une propriété là où Security a une méthode.
  • access_control.rules, et non access_control.access_control, ce qu’aurait donné la clé autrement.

Ce qui est accepté comme un coût

Tout ne peut pas lever. Certaines divergences sont réelles, modestes, et consignées plutôt que cachées :

  • AccessDeniedException ne porte ni attributs, ni sujet, ni décision. Rien dans Symfony ne lit ces trois accesseurs, donc la perte se limite au code applicatif qui les lit.
  • Lu par la fonction de décision de Security, chaque vote nomme le gestionnaire de décision plutôt que le votant, un résultat ne sachant pas quel votant l’a produit. La fonction de gabarit de ce composant vous donne les vrais.
  • Le code de sortie de console est 113 et n’est pas configurable, l’échappatoire étant un écouteur.
  • Un refus ne dit rien dans la réponse, la raison allant au profileur, au journal et aux tests.
  • debug:container peut signaler les trois étiquettes comme inutilisées, la liste des étiquettes connues de FrameworkBundle ne comprenant pas un bundle qui vit hors du dépôt de Symfony.

Pourquoi écrire cette liste

Parce qu’une divergence que vous trouvez vous-même est un rapport de bogue, et qu’une divergence dont on vous a parlé est une décision autour de laquelle vous pouvez vous organiser. La page existe pour que la réponse à « est-ce que cela fonctionne comme Security » ne soit jamais « à peu près ».

C’est aussi une promesse sur l’avenir : rien de ce qui figure sur cette page n’est censé changer en silence. À lire dans les divergences connues, idéalement avant d’installer plutôt qu’après.