Une responsable régionale ouvre un dossier reçu par courriel et découvre les remises, contacts et pièces contractuelles d’une filiale voisine. Elle est bien authentifiée et son rôle « manager » est valide ; c’est le périmètre de la ressource qui n’a jamais été vérifié. Cette fuite discrète est plus dangereuse qu’un écran inaccessible, car elle peut durer longtemps sans incident technique visible.
Dans une organisation multi-filiales, une même personne peut administrer une entité, consulter un groupe, remplacer temporairement un collègue et n’avoir aucun droit sur certaines données sensibles. Réduire ce modèle à une colonne role ou à un filtre d’interface produit des exceptions, puis des exports et des API qui contournent la règle initiale.
Le vrai enjeu est simple : le risque ne vient pas d’un rôle absent, mais d’une autorisation décidée sans la ressource, le périmètre et le contexte. Le rôle contribue à cette décision, mais ne la résume pas. Cette approche rejoint les recommandations officielles de l’OWASP sur l’autorisation : moindre privilège, refus par défaut et validation des permissions sur chaque requête.
Une application de développement web sur mesure doit donc porter le périmètre dans le modèle, les requêtes, les tâches asynchrones, les recherches, les exports et les outils de support. L’objectif n’est pas d’accumuler des rôles, mais de rendre chaque accès explicable, testable et révocable.
Pourquoi les droits multi-filiales cassent en silence
Le modèle mono-entité survit dans les hypothèses
Une application historique suppose souvent que l’utilisateur, le client et le dossier appartiennent à la même société. Lorsqu’une seconde filiale arrive, l’équipe ajoute un sélecteur d’entité et quelques conditions. Pourtant, les recherches globales, les pièces jointes, les tâches planifiées ou les identifiants devinables continuent d’utiliser l’ancienne hypothèse. L’interface semble segmentée tandis que les chemins secondaires restent ouverts.
Le premier diagnostic inventorie tous les points d’accès : pages, API, exports, notifications, jobs, webhooks, liens signés et outils d’administration. Pour chacun, il note le sujet, la ressource, l’action, le périmètre demandé et la source qui prouve l’appartenance. Une route non inventoriée n’est pas implicitement autorisée ; elle reste refusée jusqu’à ce que sa règle soit nommée.
L’authentification ne prouve pas l’autorisation
Un jeton valide prouve une identité selon le mécanisme choisi. Il ne prouve pas que cette identité peut lire la facture indiquée dans l’URL, exporter une autre filiale ou modifier un client après son transfert. La permission doit être recalculée sur l’objet courant, y compris lorsqu’un identifiant paraît aléatoire. Masquer un bouton ou utiliser un UUID ne remplace jamais ce contrôle serveur.
Séparer rôle, action, ressource et périmètre
Écrire la décision avant de choisir le mécanisme
Une règle lisible ressemble à ceci : « le responsable facturation peut valider une facture de sa filiale tant que le dossier n’est pas clôturé » ou « le contrôleur groupe peut consulter les agrégats de toutes les filiales, mais pas leurs pièces nominatives ». Le rôle, l’action, le type de ressource, l’appartenance et l’état sont distincts. Cette phrase permet de décider si un modèle par rôles suffit ou si des attributs et relations sont nécessaires.
Contre-intuitivement, le rôle admin_filiale_plus, créé pour une personne qui cumule trois exceptions, simplifie rarement le modèle. Au fil des demandes, il devient impossible de savoir ce qu’il autorise. Une politique composée de capacités nommées et de périmètres explicites garde la décision visible. Le nombre de capacités peut croître, mais chaque combinaison reste reconstruite et auditée depuis des règles stables.
Distinguer lecture détaillée et vision agrégée
Un siège peut avoir besoin de comparer chiffre d’affaires, délais ou volumes sans accéder aux commentaires, salaires ou documents locaux. Le reporting groupe n’implique pas une lecture universelle des dossiers. Une vue agrégée, construite avec les dimensions autorisées, réduit l’exposition et clarifie la finalité. Lorsque le détail devient nécessaire, une permission séparée et une justification peuvent être exigées.
Modéliser héritage, délégation et exceptions
Ne pas confondre hiérarchie juridique et accès
Une holding ne doit pas automatiquement hériter de tous les droits de ses filiales. La relation juridique, la responsabilité opérationnelle et l’accès aux données sont trois sujets différents. Le modèle décrit les groupes de périmètres autorisés plutôt que de parcourir aveuglément un arbre d’organisation. Une réorganisation capitalistique ne modifie ainsi pas les permissions sans décision métier.
Si des droits hérités existent, leur direction et leur profondeur sont bornées. L’application sait expliquer qu’un accès vient d’une affectation directe, d’un groupe régional ou d’une délégation. Elle sait aussi calculer l’effet d’un retrait avant de l’appliquer. Cette simulation évite de révoquer par surprise l’unique personne capable de traiter une opération critique.
Faire expirer les délégations
Une délégation contient un donneur, un bénéficiaire, des capacités, un périmètre, une date de début, une date de fin et un motif. Elle n’accorde jamais plus que ce que le donneur peut transmettre. L’absence d’un manager ne justifie pas un rôle global permanent. Un seuil local peut déclencher une revue lorsqu’une délégation dépasse un cycle de clôture ou couvre plusieurs filiales ; ce seuil dépend de l’organisation et n’est pas une norme universelle.
Appliquer le périmètre à toutes les données
Porter l’appartenance dans les requêtes
Le contrôle le plus robuste commence à la sélection : la requête cherche un dossier par identifiant et par ensemble de filiales autorisées, puis refuse l’absence sans révéler si l’objet existe ailleurs. Charger d’abord l’objet avant de vérifier augmente le risque qu’un log, un événement ou une sérialisation expose déjà une donnée. Les services d’accès centralisent la règle sans cacher les décisions particulières du métier.
Une relation indirecte mérite la même attention. Une pièce jointe appartient à un dossier, un contact à un compte et une notification à une commande. Le contrôleur ne fait pas confiance à un filiale_id fourni par le navigateur : il remonte la relation canonique depuis la ressource. Pour les écritures, il vérifie également que les objets reliés appartiennent à des périmètres compatibles.
Propager le contexte aux traitements asynchrones
Un worker n’agit pas avec les droits du système entier simplement parce qu’il n’a pas d’utilisateur interactif. Le message conserve l’acteur ou le compte de service, la capacité demandée, le périmètre et la décision source. Au moment du traitement, la politique sensible peut être réévaluée si les droits ont changé. Le journal distingue l’auteur de la demande de l’identité technique qui l’exécute.
Donner au support une capacité bornée
Préférer l’assistance explicite à l’admin global
Le support a besoin de diagnostiquer, pas forcément de modifier. Une vue d’assistance peut afficher configuration, chronologie et erreurs en masquant les champs non nécessaires. L’élévation temporaire exige un ticket, un motif, un périmètre et une durée. Elle reste visible pour la personne assistée lorsque le contexte le permet. Un compte partagé « support » empêche au contraire d’attribuer les actions et rend la révocation impraticable.
L’usurpation d’identité, lorsqu’elle est indispensable, ne doit jamais être invisible. L’interface affiche l’état d’assistance, interdit les actions irréversibles non prévues et trace l’opérateur réel autant que l’utilisateur simulé. La sortie restaure la session initiale sans conserver un périmètre élargi. Le guide sur le support international et sa documentation complète ce dispositif.
Sécuriser reporting, recherche et exports
Traiter l’export comme une lecture sensible
Un export CSV contourne facilement la pagination et rassemble davantage de données qu’un écran. La requête réapplique la même politique, borne les colonnes et inscrit le périmètre dans le fichier ou son manifeste. Un lien de téléchargement possède une durée courte et reste associé au bénéficiaire. Le stockage temporaire respecte une rétention définie ; générer le fichier dans un dossier public n’est pas acceptable.
La recherche transverse présente un autre risque : titre, extrait ou facette peut révéler l’existence d’un dossier inaccessible. L’index transporte le périmètre au moment de l’indexation, puis la recherche le filtre côté serveur. Après un transfert de filiale, une réindexation ciblée et une vérification d’absence empêchent l’ancien périmètre de conserver un résultat fantôme.
Implémenter les contrôles au bon endroit
Centraliser les invariants, garder le métier explicite
Un middleware peut vérifier l’authentification et charger le contexte, mais il ne connaît pas toujours l’objet final ni sa transition. Les contrôles génériques — refus par défaut, présence du périmètre, identité — s’appliquent globalement. Les règles métier — valider, transférer, exporter, clôturer — restent proches du cas d’usage. Cette combinaison évite à la fois les oublis de route et un moteur de règles opaque.
Les caches incluent les dimensions d’autorisation dans leur clé ou stockent seulement des résultats indépendants du périmètre. Une réponse calculée pour le siège ne doit pas être servie à une filiale parce que l’URL est identique. Après révocation, les sessions et caches sensibles sont invalidés selon le risque. Le système n’attend pas l’expiration naturelle d’un jeton long pour retirer un accès critique.
Dans Symfony, un Voter peut porter la décision liée à une ressource, tandis qu’un service de requête Doctrine applique le périmètre avant le chargement. L’entrée contient l’identité, la capacité et l’identifiant métier ; la sortie reste un objet autorisé ou un refus sans détail latéral. La journalisation conserve la règle et la corrélation. Cette responsabilité ne doit pas être dispersée dans chaque contrôleur ni déléguée à un filtre fourni par le navigateur.
Pour une tâche asynchrone, le message transporte le périmètre et la raison de l’action ; le handler recharge la ressource et réévalue la politique avant l’effet. La dépendance d’annuaire, le seuil d’expiration et le repli sont inscrits dans le runbook. Si l’identité source a perdu son droit, alors le worker classe la commande au lieu de l’exécuter avec un compte technique surpuissant.
Prévoir la migration du modèle existant
La migration commence en observation : l’ancien contrôle décide, le nouveau calcule en parallèle et les divergences sont journalisées sans élargir l’accès. L’équipe classe les écarts entre bug ancien, règle manquante et donnée d’appartenance incohérente. Elle active ensuite par action et par filiale, avec un retour au chemin précédent. Pour un existant mono-pays, la méthode de reprise multi-entités aide à identifier les hypothèses cachées.
Tester les refus autant que les permissions
Construire une matrice de scénarios
La matrice croise rôle, filiale, type de ressource, action et état. Elle contient au moins un cas autorisé et plusieurs refus proches : même rôle dans une autre filiale, délégation expirée, ressource transférée, identifiant deviné, export et appel API direct. Les tests ne se contentent pas du statut HTTP ; ils vérifient l’absence de données dans corps, logs, événements et effets secondaires.
Les propriétés sont parfois plus puissantes que les exemples. Pour tout utilisateur limité à la filiale A, aucune ressource de B ne doit apparaître quels que soient pagination, tri ou filtre. Pour toute délégation expirée, aucune écriture ne doit réussir. Des jeux générés révèlent les combinaisons oubliées, mais une revue humaine reste nécessaire pour les règles de responsabilité réelles.
Journaliser sans exposer davantage
Conserver la décision utile
Le journal d’autorisation enregistre acteur, action, ressource, périmètre, résultat, règle et identifiant de corrélation. Il évite de copier le contenu sensible de la ressource. Un refus répété sur plusieurs filiales peut indiquer une erreur d’interface ou une exploration hostile ; une élévation support sans ticket indique un défaut de procédure. Chaque alerte nomme une action et un propriétaire.
Le taux de refus seul n’est pas un objectif. Une politique plus sûre peut augmenter les refus légitimes, tandis qu’une fuite silencieuse n’en produit aucun. L’équipe suit plutôt les décisions sans règle identifiée, les délégations expirées encore utilisées, les accès globaux, les écarts de matrice et le délai de révocation. Les seuils sont calibrés sur le risque local et le volume normal.
Éprouver un transfert entre filiales
Cas concret hypothétique : un client change d’entité de gestion
Un compte B2B passe de la filiale Nord à la filiale Centre au milieu d’un litige. Avant le transfert, l’équipe liste dossiers ouverts, pièces, tâches, contacts et rapports dérivés. La décision précise ce qui change de propriétaire, ce qui reste dans l’historique Nord et qui peut encore consulter le litige. Une simple mise à jour du champ filiale_id ne suffit pas.
La recette vérifie quatre moments : avant transfert, pendant la transaction, après transfert et après réindexation. Un utilisateur Nord perd la modification immédiatement mais conserve, si la règle le prévoit, une preuve bornée du dossier historique. Centre récupère les actions ouvertes sans voir des données non transférées. Le support retrouve la décision et peut expliquer chaque différence sans corriger directement la base.
Savoir quand revoir le modèle d’accès
Cette revue devient prioritaire lorsqu’une application accueille une deuxième entité, qu’un siège demande une vision groupe, que le support reçoit un rôle global ou que les exports se multiplient. Elle concerne direction produit, RSSI, responsables métier, architecture, développeurs et exploitation. Chacun valide une dimension : responsabilité, finalité, menace, règle, implémentation et reprise.
Une petite organisation n’a pas besoin d’un moteur de politique complexe. Elle a besoin de règles explicites et de tests. À l’inverse, plusieurs hiérarchies, délégations et relations croisées peuvent justifier un modèle par attributs. La complexité de l’outil suit celle des décisions constatées, pas une ambition abstraite de sophistication.
Erreurs fréquentes de conception des accès
La première erreur masque les boutons sans contrôler l’API. La deuxième accepte le périmètre envoyé par le client. La troisième donne au support un administrateur global permanent. La quatrième applique la règle aux pages mais oublie exports, pièces jointes, recherche ou workers. La cinquième confond l’appartenance actuelle avec le droit de lire un historique.
Une autre erreur crée un rôle par personne exceptionnelle. Le modèle devient un inventaire de noms plutôt qu’une politique. Enfin, journaliser toutes les données « pour l’audit » augmente l’exposition au lieu de l’expliquer. Chaque trace doit répondre à une question de décision ou d’incident, avec une durée de conservation et des droits propres.
Plan d’action pour sécuriser les droits
Semaines 1 et 2 : cartographier et fermer les évidences
L’équipe inventorie sujets, ressources, actions, périmètres et chemins d’accès. Elle retire les comptes partagés, borne les admins globaux et ajoute des tests sur les fuites les plus probables. Elle choisit un parcours critique — dossier client, facture ou salarié — et écrit ses décisions sous forme de règles opposables.
Semaines 3 et 4 : instrumenter le nouveau modèle
Les services d’accès appliquent le périmètre dans les requêtes. API, exports, recherche et workers reçoivent le même contexte. Le nouveau calcul fonctionne en miroir et compare ses décisions à l’existant. Toute divergence possède une cause, un propriétaire et un verdict avant activation.
Semaines 5 et 6 : exercer révocation et transfert
La recette joue une délégation expirée, une personne changeant de filiale, un dossier transféré, une recherche globale et une assistance support. Le déploiement commence sur un périmètre borné. Si une ressource d’une autre filiale apparaît ou si une révocation dépasse le délai local accepté, le lot est arrêté et le chemin précédent restauré.
La revue finale produit la matrice signée, la liste des accès globaux résiduels, les scénarios automatisés et le runbook de révocation. En pratique, la décision n’est pas « tous les tests passent », mais « les chemins critiques refusent par défaut et le support explique les exceptions ». Si une dépendance empêche ce verdict, le périmètre est réduit plutôt que couvert par une permission temporaire sans échéance.
- Refuser par défaut et expliquer chaque autorisation.
- Séparer identité, capacité, ressource, périmètre et contexte.
- Tester pages, API, exports, recherche, jobs et support.
- Élargir seulement lorsque révocation, transfert et audit sont démontrés.
Guides complémentaires pour les produits multi-entités
Relier le modèle d’accès au socle produit
Pour décider ce qui reste commun et ce qui varie, le guide de mutualisation multi-filiales aide à séparer invariants, paramètres et extensions sans cacher les responsabilités.
Préparer l’organisation et les variations
La scalabilité organisationnelle qualifie les rôles au fil de la croissance, tandis que les variations locales évitent de transformer chaque exception d’accès en fork.
- Relire les appartenances et délégations avant chaque nouvelle entité.
- Tester une recherche, un export et un traitement asynchrone hors périmètre.
- Vérifier que le support diagnostique sans administrateur global permanent.
Conclusion : le périmètre est une règle métier
Les droits multi-filiales ne se résument pas à ajouter des rôles. Ils expriment qui porte une responsabilité sur quelle ressource, dans quelle entité, pour quelle action et pendant combien de temps. Le périmètre doit rester visible jusque dans les requêtes, les workers et les exports.
La priorité va aux refus par défaut, aux accès support bornés et aux scénarios de transfert. Un moteur sophistiqué peut attendre ; une route qui charge une ressource avant de vérifier son appartenance ou un export global non tracé doit être corrigé avant l’arrivée d’une nouvelle filiale.
Dawap peut auditer les chemins d’accès, formaliser les politiques et construire les tests de non-fuite dans le cadre d’un accompagnement en développement web sur mesure, afin que la croissance multi-filiales n’élargisse jamais silencieusement les données visibles.