Comprendre le modèle de permissions RBAC de gitrust

Ce que vous allez comprendre

  • Analyser comment les 4 rôles (Reader, Developer, Maintainer, Owner) s'appliquent à un dépôt.
  • Évaluer la différence entre accès direct et accès via équipe, et comment le rôle effectif est calculé.
  • Comparer le modèle gitrust avec les ACL Unix (user/group/other).

Le problème concret

Vous avez un dépôt privé alice/myproject. Alice souhaite que : - Bob puisse lire le code (pas pusher). - L'équipe backend-devs puisse pusher. - Charlie, membre de backend-devs, soit quand même restreint en lecture seule.

Sans un modèle de permissions précis, impossible de savoir quel accès Charlie obtient réellement.

L'analogie

Le modèle gitrust ressemble aux ACL Unix, mais à deux dimensions.

Sur Unix, chaque fichier a trois niveaux : user (propriétaire), group, other. Le niveau effectif est le premier qui correspond, dans cet ordre.

Gitrust fonctionne de manière similaire, avec deux axes :

Axe Unix Axe gitrust
user accès individuel direct (resource_shares)
group accès via équipe (team_repository_access)
other accès public (resources.is_public)

La différence clé : gitrust prend le maximum entre l'accès individuel et l'accès équipe, pas le premier correspondant.

Le modèle

Les 4 rôles et leur hiérarchie

graph LR
    R[Reader] --> D[Developer]
    D --> M[Maintainer]
    M --> O[Owner]
Rôle Permissions cumulatives
Reader Clone, browse (lecture seule)
Developer Reader + push sur branches non protégées
Maintainer Developer + gestion dépôt (settings, collaborateurs, labels, protection de branches)
Owner Maintainer + suppression, transfert

La hiérarchie est strictement cumulative : un Maintainer a toutes les permissions d'un Developer, qui a toutes celles d'un Reader.

Sources d'accès

Un utilisateur peut accéder à un dépôt par trois chemins indépendants :

flowchart TD
    User[Utilisateur]

    subgraph "Accès individuel"
        Owner["Owner (resources.owner_id)"]
        Share["resource_shares
(read / write / admin)"] Public["Accès public
(resources.is_public = true)"] end subgraph "Accès équipe" TeamMember["team_members
(l'utilisateur est membre)"] TeamAccess["team_repository_access
(permission de l'équipe sur le dépôt)"] end Effective["Rôle effectif
= max(individuel, équipe)"] User --> Owner User --> Share User --> Public User --> TeamMember TeamMember --> TeamAccess Owner -->|"owner (full)"| Effective Share -->|"read/write/admin"| Effective Public -->|"read only"| Effective TeamAccess -->|"read/write/admin"| Effective

Calcul du rôle effectif

Le rôle effectif d'un utilisateur sur un dépôt est le maximum de tous ses accès :

rôle_effectif = max(
    owner_directement ? owner : none,
    resource_shares.permission_level,
    is_public ? read : none,
    max(team_repository_access.permission pour chaque équipe dont il est membre)
)

Exemple — réponse au problème concret :

Utilisateur Accès individuel Accès via équipe Rôle effectif
Alice owner owner
Bob read (partagé directement) read
Charlie read (partagé directement) write (via backend-devs) write (max)
Dave (membre backend-devs) write write

Charlie obtient write car le max(read, write) = write. Pour restreindre Charlie en lecture seule malgré son appartenance à backend-devs, il faudrait le retirer de l'équipe ou utiliser une protection de branche.

Arbre de décision

flowchart TD
    Q1{L'utilisateur\nest-il owner ?}
    Q1 -->|Oui| Full[Accès owner\n(toutes permissions)]
    Q1 -->|Non| Q2{resource_shares\nexiste pour cet utilisateur ?}
    Q2 -->|Oui| Q3{Dépôt public ?}
    Q2 -->|Non| Q3
    Q3 -->|Oui| Q4[Accès public = read]
    Q3 -->|Non| Q4b[Pas d'accès public]
    Q4 --> Q5{Membre d'une équipe\navec accès au dépôt ?}
    Q4b --> Q5
    Q5 -->|Oui| Calc["Rôle = max(share, public, team)"]
    Q5 -->|Non| Calc2["Rôle = max(share, public)"]
    Calc --> Result[Rôle effectif final]
    Calc2 --> Result

Mapping vers les niveaux ResourceService

ResourceService (rustwarden-core) utilise les niveaux "read", "write", "admin". Le mapping gitrust est :

Niveau ResourceService Rôle gitrust
"read" Reader
"write" Developer
"admin" Maintainer
owner (ownership directe) Owner

Alternatives et compromis

Pourquoi pas un modèle RBAC pur avec des rôles nommés par dépôt ? Gitea/Forgejo utilisent des rôles nommés (reporter, developer, maintainer). Gitrust utilise des niveaux numériques (read < write < admin) héritables, ce qui simplifie le calcul du maximum mais rend impossible l'attribution de permissions non hiérarchiques (ex : « peut créer des issues mais pas pusher »).

Pourquoi pas des ACL par fichier ? La granularité fichier (comme dans Perforce) crée une complexité de maintenance considérable pour des équipes de 3-20 personnes. Les protections de branches couvrent 90 % des besoins de contrôle granulaire.

Vérifier votre compréhension

  1. Alice est owner de myrepo. Elle partage le dépôt avec Bob en read. L'équipe ops a accès en admin. Bob rejoint ops. Quel est le rôle effectif de Bob ?

  2. Un visiteur anonyme accède à un dépôt dont resources.is_public = true. Peut-il pusher ? Pourquoi ?

Pour aller plus loin