Pourquoi Access Control n’est pas un composant Symfony

Access Control a commencé comme une pull request contre Symfony lui-même. Dix-neuf mois plus tard, je l’ai fermée moi-même, et le composant est sorti en bibliothèque indépendante. Voici pourquoi c’était la bonne issue, et ce qu’elle a rapporté.

La pull request a été ouverte en janvier 2025 : un composant d’autorisation avec des stratégies et des votants, posé à côté de Security plutôt que dedans. Elle a attiré une longue discussion, et l’objection principale était juste.

L’objection

L’autorisation dans security-core est déjà fondée sur des attributs, porte déjà un sujet, et a déjà des votants. Un second composant qui couvre le même terrain doit justifier plus qu’un vocabulaire différent : il doit justifier la maintenance, la confusion de deux façons de faire une chose, et le chemin de dépréciation de celle qui perdra.

C’est une exigence équitable pour un framework dont la promesse de rétrocompatibilité fait qu’une erreur reste des années. Ce n’est pas une exigence équitable pour une idée qui est encore en train de prendre forme.

Ce que le framework n’aurait pas pu lui donner

En relisant la discussion close à la lumière de ce que le composant est devenu, la liste est plus longue que je ne l’attendais.

  • Son propre vocabulaire. Les algorithmes de combinaison portent leurs noms XACML plutôt que ceux de Security. À l’intérieur de Symfony, cette divergence aurait été une source permanente de commentaires du genre « corrigez cela par mimétisme ». Au dehors, c’est un choix documenté.
  • Un demandeur qui n’est pas un utilisateur. Rien dans le contrat ne type contre UserInterface ni contre un jeton. Dans un composant qui vivrait à côté de Security, l’attraction gravitationnelle vers ses interfaces aurait été très difficile à contenir.
  • La liberté de refuser. L’ACL au niveau du champ lève plutôt que de répondre faux, et déclarer deux fois le même réglage arrête la compilation. Un composant de framework ne peut pas se permettre de refuser de s’installer ; une bibliothèque le peut, et ce refus est une fonctionnalité.
  • Le temps. Dix-neuf mois de conception, trois configurations testées, une application de démonstration et un site de documentation. Ce rythme n’entre pas dans un cycle de publication.

Fermer sa propre pull request

Ce qui y a mis fin n’est pas un refus. C’est un résumé : l’état de la discussion en un seul endroit, et une demande de trancher. Je l’ai fermée, en disant que le composant deviendrait une bibliothèque et un bundle indépendants, où il pourrait mûrir seul, et que s’il faisait ses preuves nous pourrions en reparler.

Cette phrase vaut plus qu’il n’y paraît. Une pull request laissée ouverte des années n’est pas neutre : elle occupe des relecteurs, elle signale aux utilisateurs que quelque chose arrive, et elle rend chaque décision de conception provisoire. La fermer a transformé une proposition à l’arrêt en une chose livrée.

Ce que l’indépendance coûte

Elle n’est pas gratuite, et prétendre le contraire serait malhonnête. Un bundle extérieur doit installer un paquet dont il ne se sert jamais, parce que FrameworkExtension refuse une garde de workflow sans lui. Ses étiquettes sont signalées comme inutilisées par debug:container, cette liste vivant dans FrameworkBundle. Et il doit retirer les écouteurs de Security au lieu qu’on lui demande d’abord, ce qui est une main plus lourde qu’il n’en faudrait à un composant intérieur.

Chacun de ces points est un endroit où un point d’extension du framework aiderait tous les bundles, pas seulement celui-ci, et c’est la forme de contribution que je préférerais envoyer en amont désormais : non pas le composant, mais les jointures dont il a eu besoin.

Le code vit ici, la documentation sur docs.spomky-labs.com/acf. S’il fait ses preuves, la conversation pourra reprendre, avec une implémentation à montrer plutôt qu’une proposition à imaginer.