← Back to list

Délégation de permissions dans un arbre organisationnel : comment garder un modèle lisible…

Article écrit conjointement avec Johan Girard

Florian Rossiaud · 2026-01-30 14:35 · 2 claps · 10.0 min read
#rebac #openfga #permission #delegation
Open on Medium ↗

Délégation de permissions dans un arbre organisationnel : comment garder un modèle lisible, auditable et évolutif

Article écrit conjointement avec Johan Girard

Introduction

Pendant longtemps, la gestion des permissions dans les plateformes de cybersécurité s’est appuyée sur des modèles d’autorisation simples, souvent basés sur le RBAC. Adaptés à des environnements peu complexes, ces modèles d’autorisation à rôle fixe, atteignent rapidement leurs limites dès qu’il s’agit de gérer plusieurs organisations hiérarchisées avec des mécanismes de délégation.

À ce stade, le problème n’est plus seulement de savoir qui a accès à quoi, mais de déléguer des permissions sans perdre le contrôle. Comment permettre à une organisation parente de confier une partie de sa sécurité à un partenaire, tout en conservant des règles claires, auditées et compréhensibles ?

Chez Stormshield, cette question est devenue centrale. Nous avons rapidement constaté que diffuser cette complexité dans le code applicatif rendait les services difficiles à maintenir, à faire évoluer et à tester. Conserver des services simples et découplés du modèle d’autorisation s’est donc imposé comme un principe structurant, tout en permettant l’expression de règles fines et évolutives d’autorisation.

Dans cet article, nous expliquons comment nous avons abordé ce défi en nous appuyant sur le modèle ReBAC (Relationship-Based Access Control). Contrairement aux modèles à rôle fixe, ReBAC définit les permissions à partir des relations entre entités. Cette approche est mise en œuvre à l’aide d’OpenFGA, un moteur d’autorisation capable de raisonner sur ces relations. Nous verrons comment construire un modèle multi-organisations et mettre en place une délégation de permissions à la fois simple, sécurisée et maintenable, sans sacrifier la lisibilité.

Étape 1 : Clarifier le problème avant de le modéliser

Avant de se lancer dans la modélisation OpenFGA, une étape est incontournable : définir précisément ce que le modèle d’autorisation doit permettre d’exprimer. L’objectif n’est pas de partir d’un schéma technique abstrait, mais bien de formaliser les besoins métiers réels auxquels le système doit répondre.

Dans notre cas, le modèle d’autorisation cible doit satisfaire trois exigences fondamentales:

  1. La première est la capacité à représenter un environnement multi-organisations. Chaque organisation dispose de ses propres utilisateurs et permissions, sans dépendre d’un contexte global ou d’une autorité centralisée. L’isolation logique entre organisations est donc un prérequis.
  2. La deuxième concerne la délégation explicite de permissions. Une organisation doit pouvoir déléguer volontairement une partie de ses permissions, par exemple read ou write, à une autre organisation. Cette délégation doit être maîtrisée, intentionnelle et compréhensible, tant pour les équipes que pour les audits.
  3. Enfin, cette délégation doit être transitive mais strictement bornée. Une organisation ne peut transmettre que les permissions qu’elle possède effectivement, qu’elles soient natives ou issues d’une délégation. Aucune élévation implicite de privilèges ne doit être possible.

Pour structurer cette démarche, nous nous appuyons sur la méthodologie recommandée par OpenFGA :

  1. Identifier les cas d’usage métiers concrets,
  2. Déduire les permissions exprimées à un niveau fonctionnel,
  3. Traduire ces permissions dans le modèle d’autorisation OpenFGA,
  4. Valider le modèle d’autorisation à l’aide de tests de permissions.

Cette approche permet de construire un modèle d’autorisation lisible, testable et durable, tout en garantissant son alignement avec les besoins fonctionnels réels.

Cas d’usage fonctionnels

Nous considérons trois organisations, A, B et C, chacune disposant de ses propres utilisateurs. À l’état initial, chaque utilisateur possède uniquement des permissions explicites sur son organisation d’appartenance :

  • User A dispose des droits read et write sur l’Organization A,
  • User B dispose du droit read sur l’Organization B,
  • User C dispose des droits read et write sur l’Organization C.

À partir de cette base, nous introduisons des relations de délégation contrôlées entre organisations parentes et enfants.

L’Organization B délègue explicitement sa permission de read à l’Organization A. Les membres de l’Organization A peuvent alors consulter les ressources de l’Organization B, sans pour autant disposer de permissions de write.

De la même manière, l’Organization C délègue à l’Organization B les permissions read et write. Les membres de l’Organization B peuvent ainsi lire et modifier les ressources de l’Organization C.

Nous pouvons représenter ces interactions avec le schéma suivant :

Schéma des délégation de permissions dans un arbre organisationnel

Schéma des délégation de permissions dans un arbre organisationnel

Ce mécanisme crée une chaîne de délégation transitive :

  • L’Organization A hérite indirectement d’une permission de read sur l’Organization C, transmise via l’Organization B,
  • En revanche, aucune permission de write n’est propagée à l’Organization A sur l’Orgazisation C, cette permission n’ayant jamais été déléguée par l’Organization B.

Ce scénario met en évidence un principe clé du modèle d’autorisation : la délégation est transitive, mais strictement limitée aux permissions explicitement déléguées à chaque niveau.

Les permissions effectives d’un utilisateur sur une organisation donnée résultent donc de la combinaison suivante :

  • Ses permissions locales au sein de son organisation,
  • Et l’ensemble des délégations définies entre organisations.

Ce cas d’usage servira de référence pour formaliser les permissions de haut niveau, avant leur traduction progressive dans le modèle d’autorisation OpenFGA.

Traduction en permissions haut niveau

À partir des cas d’usage précédents, l’objectif est désormais d’exprimer une règle claire : dans quelles conditions un utilisateur peut-il lire ou écrire sur une organisation donnée ?

Nous définissons d’abord les conditions associées aux permissions d’écriture :

  • Un utilisateur dispose des permissions de read et write sur son organisation s’il possède la permission write sur sa propre organisation,
  • Un utilisateur dispose des permissions de read et write sur une autre organisation, si toutes les organisations intermédiaires ont explicitement délégué la permission write à leur organisation parente jusqu’à son organisation.

Nous définissons ensuite les conditions associées aux permissions de lecture :

  • Un utilisateur ne dispose d’aucun droit sur une organisation si aucun lien de délégation transitif ne peut être établi,
  • Un utilisateur dispose des permissions de read sur son organisation s’il possède la permission de read ou de write sur sa propre organisation,
  • Un utilisateur ne dispose que des permissions de read sur une autre organisation, si une des organisations intermédiaires a explicitement délégué la permission read à son organisation parente.

Ces règles constituent la base conceptuelle que nous allons maintenant traduire dans le modèle d’autorisation OpenFGA.

Matrice de permissions

Afin de vérifier que les règles définies produisent bien les effets attendus, nous pouvons les appliquer au scénario présenté précédemment et en déduire les permissions effectives de chaque utilisateur.

La matrice ci-dessous synthétise ces résultats en combinant :

  • Les permissions locales des utilisateurs sur leur organisation,
  • Les délégations explicites définies entre organisations,
  • Et leur propagation transitive, strictement encadrée.

Matrice des permissions cibles

Matrice des permissions cibles

Cette matrice servira de référence fonctionnelle pour la suite de l’article : le modèle d’autorisation final devra reproduire fidèlement ce comportement.

Elle reprend les couleurs du schéma représentant notre exemple.

Étape 2 : Définir l’appartenance d’un utilisateur à son organisation

Avant d’introduire la délégation entre organisations, nous commençons volontairement par le socle le plus simple : l’appartenance d’un utilisateur à une organisation.

Dans ce premier modèle d’autorisation, une organisation ne définit qu’une seule relation, member, qui référence explicitement les utilisateurs appartenant à cette organisation. Aucune notion de hiérarchie, de permission ou de délégation n’est encore prise en compte.

Ce choix est volontaire. Il permet de poser une base minimale, facile à comprendre et à valider, sur laquelle viendront s’appuyer toutes les extensions futures du modèle.

model
  schema 1.1

type user

type organization
  relations
    define member: [user]

Nous pouvons alors définir simplement les membres et les organisations de notre exemple à l’aide des tuples suivants (pseudo-code OpenFGA) :

write([
  {
    "user": "user:UserA",
    "relation": "member",
    "object": "organization:A"
  },
  {
    "user": "user:UserB",
    "relation": "member",
    "object": "organization:B"
  },
  {
    "user": "user:UserC",
    "relation": "member",
    "object": "organization:C"
  }
]);

Ce modèle d’autorisation minimal couvre déjà un premier cas d’usage fonctionnel : vérifier qu’un utilisateur appartient bien à une organisation. Côté application, il suffit alors d’interroger le moteur d’autorisation avec une requête de type check sur la relation member :

check(
  user = "user:UserA",
  relation = "member",
  object = "organization:A"
);

Reply: true

Et, à l’inverse, si l’utilisateur n’appartient pas à l’organisation ciblée :

check(
  user = "user:UserA",
  relation = "member",
  object = "organization:B"
);

Reply: false

Étape 3 : Créer les permissions d’un utilisateur au sein de son organisation

Une fois la notion de membre établie, nous pouvons introduire une première couche de permissions applicatives : read et write.

Pour cela, nous ajoutons deux relations à l’objet organization :

  • writer : représente la permission write sur l’organisation
  • reader : représente la permission read sur l’organisation

Afin d’éviter de dupliquer inutilement les tuples, reader est définie comme une relation dérivée : un utilisateur est reader s’il est member de l’organisation, ou s’il est writer.

model
  schema 1.1

type user

type organization
  relations
    define member: [user]

    define writer: [user]
    define reader: member or writer

Les tuples à ajouter pour notre exemple sont les suivants (pseudo-code OpenFGA) :

write([
  {
    "user": "user:UserA",
    "relation": "writer",
    "object": "organization:A"
  },
  {
    "user": "user:UserB",
    "relation": "reader",
    "object": "organization:B"
  },
  {
    "user": "user:UserC",
    "relation": "writer",
    "object": "organization:C"
  }
]);

Avec ce modèle d’autorisation, on peut déduire la matrice de permissions suivante :

Matrice de permission d’une organisation sans délégation

Matrice de permission d’une organisation sans délégation

Ici aussi, on peut interroger le moteur d’autorisation avec une requête de type check sur les permissions reader ou writer :

check(
  user = "user:UserA",
  relation = "reader",
  object = "organization:A"
);

Reply: true

check(
  user = "user:UserB",
  relation = "writer",
  object = "organization:C"
);

Reply: false

Cette base, volontairement simple, nous servira de point d’appui pour introduire la délégation dans les étapes suivantes.

Étape 4 : Implémenter une première délégation inter-organisations

Nous pouvons maintenant introduire une délégation inter-organisations volontairement simple : une organisation peut déléguer une permission de read ou write à une autre organisation.

Dans OpenFGA, cela se modélise naturellement en :

  • ajoutant des relations entre organisation pour représenter la délégation,
  • et en étendant la définition des permissions afin d’intégrer ces relations.

Sur le même principe que précédemment, afin de limiter le nombre de tuples, une délégation en write induit implicitement une délégation en read.

On obtient ainsi un premier modèle d’autorisation fonctionnel, mais limité : un seul niveau de délégation est pris en charge.

Dans ce cas d’usage, les permissions ne peuvent être déléguées que d’une organisation enfant vers une organisation parente. Cela permet de valider rapidement les premiers cas d’usage, tout en gardant un modèle lisible et maîtrisé, avant d’introduire la transitivité complète dans les étapes suivantes.

model
  schema 1.1

type user

type organization
  relations
    define member: [user]

    define writer: [user] or member from delegated_write
    define reader: member or writer or member from delegated_read

    define delegated_write: [organization]
    define delegated_read: [organization]

Les tuples de notre exemple à ajouter sont les suivants :

write([
  {
    "user": "organization:A",
    "relation": "delegated_read",
    "object": "organization:B"
  },
  {
    "user": "organization:B",
    "relation": "delegated_write",
    "object": "organization:C"
  }
]);

Avec ce modèle d’autorisation, nous couvrons désormais un périmètre plus large de la matrice de permissions.

Matrice de permission d’une première délégation simple

Matrice de permission d’une première délégation simple

Cette approche naïve permet d’illustrer simplement le mécanisme de délégation inter-organisations limité à un seul niveau de profondeur.

Étape 5 : Ajouter la délégation transitive au modèle d’autorisation

À ce stade, les organisations peuvent déjà décider de déléguer des permissions de read et/ou write.

L’enjeu de cette étape n’est donc plus d’introduire de nouvelles permissions, mais de calculer correctement leur héritage le long de la hiérarchie des organisations.

L’objectif est de permettre une propagation transitive maîtrisée des permissions déléguées, sans toucher à la logique applicative existante. Ainsi l’application continue de vérifier uniquement les relations writer et reader.

Pour y parvenir, nous distinguons explicitement :

  • les permissions directes, définies localement sur une organisation,
  • les permissions héritées, obtenues via les délégations inter-organisations.

Les relations writer_inherited et reader_inherited expriment ces permissions héritées. Elles s’appuient à la fois sur la relation member et sur l’arbre de délégation entre organisations (delegate_read, delegate_write).

Cette approche permet d’introduire un héritage hiérarchique explicite, tout en conservant une interface de vérification inchangée côté application, qui continue simplement à interroger OpenFGA.

model
  schema 1.1

type user

type organization
  relations
    define member: [user]

    define writer: [user] or writer_inherited
    define reader: member or writer or reader_inherited

    define delegated_write: [organization]
    define delegated_read: [organization]

    define writer_inherited: member from delegated_write or writer_inherited from delegated_write
    define reader_inherited: member from delegated_read or reader_inherited from delegated_read or writer_inherited or reader_inherited from delegated_write

Mission accomplie.

Avec ce modèle d’autorisation, l’ensemble de la matrice de permissions est désormais correctement couvert :

Matrice des permissions cibles

Matrice des permissions cibles

Limitations

En chemin, nous avons rencontré des limitations structurantes, influençant notre modèle d’autorisation.

Premièrement, il n’est pas possible de brancher directement une délégation sur une relation de type #member (par exemple delegate_write: [organization#member]) sans perdre la capacité à représenter correctement l’arbre de délégation.

Deuxièmement, OpenFGA impose qu’une relation utilisée dans un from soit directe. Une relation calculée (par exemple define delegate_read: [organization] or delegate_write) ne peut pas servir de base à une délégation, sous peine de provoquer l’erreur :

“… relation is referenced in at least one tupleset and thus must be a direct relation”

Ces contraintes nous ont donc conduits à séparer strictement les relations de délégation directes des relations calculées, garantissant à la fois la validité du modèle et sa lisibilité.

Conclusion

Dans cet article, nous avons montré comment adresser cette problématique en construisant progressivement un modèle d’autorisation basé sur ReBAC, implémenté avec OpenFGA. À partir de règles fonctionnelles explicites, nous avons abouti à un modèle d’autorisation capable d’exprimer des délégations inter-organisations transitives, tout en conservant une structure claire et un nombre de tuples maîtrisé.

Cette approche permet de concentrer la complexité dans le modèle d’autorisation lui-même, tout en maintenant des services applicatifs agnostiques des règles de délégation, sans compromettre la sécurité ni la maintenabilité.

Le modèle d’autorisation présenté constitue une base générique, pensée pour évoluer vers des besoins plus fins. Il peut notamment être étendu afin de restreindre, pour chaque user, les organisations qu’il est autorisé à gérer ou à superviser, sans remettre en cause les mécanismes de délégation existants.

Avec le recul, le choix d’OpenFGA est pertinent, tant pour son expressivité que pour sa testabilité et sa capacité à maîtriser le volume de données.

Enfin, l’ensemble des modèles d’autorisation et des tests de permissions associés est disponible dans un repository GitHub, afin de permettre leur réutilisation et leur adaptation à d’autres contextes.

Bonus: Séparer les délégations inter-organisations des permissions utilisateurs

Dans certains contextes, il peut être utile de rendre explicite la chaîne de délégation entre organisations, indépendamment des utilisateurs. Cela facilite le raisonnement sur les relations inter-organisations, l’audit et la réutilisation de ces règles dans d’autres mécanismes d’autorisation.

model
  schema 1.1

type user

type organization
  relations

    # ... Same model as before ...

    define can_write_from: delegated_write or can_write_from from delegated_write
    define can_read_from: can_write_from or delegated_read or can_read_from from delegated_read or can_read_from from delegated_write
    define can_access_from: can_write_from or can_read_from

Ce modèle d’autorisation introduit des relations calculées représentant les délégations transitives entre organisations. Il permet de séparer clairement la logique de délégation inter-organisations des permissions utilisateurs.


메타데이터
post_id
fa6434f705b7
slug
délégation-de-permissions-dans-un-arbre-organisationnel-comment-garder-un-modèle-lisible-fa6434f705b7
url
https://medium.com/@florian.rossiaud/d%C3%A9l%C3%A9gation-de-permissions-dans-un-arbre-organisationnel-comment-garder-un-mod%C3%A8le-lisible-fa6434f705b7
canonical_url
https://medium.com/@florian.rossiaud/d%C3%A9l%C3%A9gation-de-permissions-dans-un-arbre-organisationnel-comment-garder-un-mod%C3%A8le-lisible-fa6434f705b7
author_url
https://medium.com/@florian.rossiaud
status
ok
fetched_at
2026-07-13 06:23:13