Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Organisations, équipes et droits sur Forgejo

Par mis à jour le

Améliorer cette page

Sur Forgejo 15 LTS, les droits ne se donnent pas personne par personne : on crée une organisation, on y range des équipes, et chaque équipe reçoit un niveau de droit sur certains dépôts seulement. Une organisation est un espace qui regroupe des dépôts ; une équipe est un groupe de personnes de cet espace. Cette leçon s'adresse à l'administrateur qui vient d'installer sa forge et doit y accueillir plusieurs équipes. Elle part d'une organisation privée acme, y crée une équipe en lecture et une équipe en écriture, prouve le résultat avec git, puis verrouille la branche main.

Vous repartez avec trois preuves : les droits effectifs relevés, un push refusé à un lecteur, et un push direct refusé sur main. Les commandes ont été exécutées sur Forgejo 15.0.10, AlmaLinux 10.2, avec la pile de l'installation, et les sorties citées sont celles obtenues. Les gestes de gouvernance existent aussi dans l'interface web, dont les libellés sont donnés en repère ; seuls l'API et la ligne de commande sont détaillés et copiables.

  • Modéliser les droits d'une forge : organisation, équipes, dépôts, unités.
  • Créer une organisation et des équipes, et les rattacher à un dépôt précis.
  • Relever la permission effective d'un compte et la prouver par un git push.
  • Choisir la visibilité d'une organisation : private, limited ou public.
  • Protéger main et comprendre qui peut fusionner une pull request.
  • Connaître les valeurs par défaut qui exposent un dépôt sans qu'on l'ait demandé.

Il vous faut l'instance de la leçon Installer Forgejo LTS : Forgejo 15 derrière Caddy, le compte administrateur fjadmin, le nom forge.lab.test et la CA de Caddy que git doit connaître. La leçon Configuration initiale de Forgejo règle le reste de l'instance. Vous devez savoir lancer curl et git, et lire un JSON ; python3 sert à en extraire un champ.

Une API est l'interface qu'un programme appelle pour piloter la forge ; ici, on l'appelle avec curl et un jeton d'accès, une clé personnelle qui remplace le mot de passe. Son périmètre (le scope) limite ce qu'il permet de faire.

Les jetons du lab portent le périmètre all, accepté par la commande forgejo admin user generate-access-token --scopes, et qui n'a de sens que dans un lab jetable. La documentation Forgejo 15 recommande « the most restrictive access token possible » : des périmètres par domaine, en lecture (read:) ou en écriture (write:), comme organization, repository ou issue. Sur une vraie instance, on les restreint. Pour le principe qui guide toute la leçon, voir Moindre privilège.

Une organisation regroupe des dépôts, des équipes regroupent des personnes, et c'est l'équipe qui reçoit un niveau de droit sur un ou plusieurs dépôts. Un dépôt personnel appartient à un compte et ne se partage qu'au cas par cas ; un dépôt d'organisation se gouverne par ses équipes, ce qui évite de répéter le même droit sur chaque dépôt et pour chaque personne. Quand quelqu'un arrive, on l'ajoute à une équipe ; quand il part, on l'en retire.

Le schéma montre l'organisation acme que construit cette leçon. Suivez les flèches de gauche à droite : chaque personne, sauf carol, est dans une équipe, et chaque équipe atteint des dépôts. Owners est créée seule avec l'organisation et couvre tous les dépôts ; lecteurs et ecrivains n'atteignent que app. La flèche pointillée est carol, qui n'est dans aucune équipe : elle ne voit ni app ni autre.

Organisation privée acme : fjadmin est dans l'équipe Owners (owner, tous les dépôts), alice dans lecteurs (read sur le code, les tickets et les pull requests), bob dans ecrivains (write sur les mêmes unités). Owners atteint les dépôts app et autre, lecteurs et ecrivains n'atteignent que app. carol, sans équipe, reçoit un 404 sur les deux dépôts.Organisation acme (privée)404 sur les deuxfjadminalicebobcarolhors équipeOwnersowner, tous les dépôtslecteursread : code, tickets, PRecrivainswrite : code, tickets, PRDépôt appDépôt autre
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Une équipe porte deux réglages qu'il faut distinguer. Son niveau (read, write, admin ou owner, détaillés ci-dessous) fixe ce qu'elle peut faire de façon générale. Ses unités, fournies par le champ units_map, précisent le niveau pour chaque fonction du dépôt : repo.code pour le code, repo.issues pour les tickets, repo.pulls pour les pull requests, ces demandes de fusion d'une branche dans une autre. Enfin includes_all_repositories vaut false pour lecteurs et ecrivains, donc une équipe ne voit que les dépôts qu'on lui rattache un par un. Le modèle plus général, avec RBAC et ses alternatives, est détaillé dans RBAC, ABAC et politiques d'autorisation.

Quels niveaux de droit une équipe peut-elle donner ?

Section intitulée « Quels niveaux de droit une équipe peut-elle donner ? »

Le schéma de l'API Forgejo 15 connaît cinq niveaux pour une équipe : none, read, write, admin et owner. À la création d'une équipe, l'API n'accepte que read, write ou admin ; owner est réservé à l'équipe Owners, que la forge crée seule. Cette leçon met en jeu read, write et owner, et observe none chez un compte sans équipe. Le niveau admin (« Administrator access » dans l'interface) n'est pas utilisé ici : avant de l'attribuer, relevez la permission effective qu'il donne, avec la méthode de la section de preuve plus bas. Le tableau relie chaque niveau à ce que l'on constate avec git.

NiveauQui, dans cette leçonCe que vous constatez
owneréquipe Owners (créée seule, tous les dépôts)gère l'organisation ; ne contourne pas la protection de main
writebob, équipe ecrivainsgit push sur main accepté tant que main n'est pas protégée
readalice, équipe lecteursclone possible, git push refusé (pre-receive hook declined)
nonecarol, aucune équipedépôt introuvable : repository ... not found

Le niveau read donne le clone et la lecture, jamais l'écriture. Quant au niveau none, il ne dit pas « accès refusé » : la forge répond comme si le dépôt n'existait pas, ce que la section de preuve montre.

Comment préparer les comptes et le shell de travail ?

Section intitulée « Comment préparer les comptes et le shell de travail ? »

On crée trois comptes en ligne de commande, on leur génère un jeton, puis on définit quatre petites fonctions pour ne pas répéter les en-têtes. Les trois comptes jouent trois rôles : alice lit, bob écrit, carol est extérieure. La commande forgejo admin user create est la même qu'en installation.

Fenêtre de terminal
D=forge.lab.test
ATK=$(sudo docker exec -u git forgejo forgejo admin user generate-access-token --username fjadmin --token-name "t-$$-$RANDOM" --scopes all --raw)
for u in alice bob carol; do
sudo docker exec -u git forgejo forgejo admin user create --username $u --email $u@example.test --password "$(openssl rand -hex 12)" --must-change-password=false 2>&1 | tail -1
done
T_alice=$(sudo docker exec -u git forgejo forgejo admin user generate-access-token --username alice --token-name "t-$RANDOM" --scopes all --raw)
T_bob=$(sudo docker exec -u git forgejo forgejo admin user generate-access-token --username bob --token-name "t-$RANDOM" --scopes all --raw)
T_carol=$(sudo docker exec -u git forgejo forgejo admin user generate-access-token --username carol --token-name "t-$RANDOM" --scopes all --raw)

La sortie attendue est New user 'alice' has been successfully created!, puis la même ligne pour bob et carol. Les mots de passe sont générés et jamais affichés ; tous les jetons restent dans des variables, jamais dans un fichier. Il reste à préparer git et les fonctions d'appel.

Fenêtre de terminal
W=$(mktemp -d); cd "$W"
sudo docker cp forgejo-proxy:/data/caddy/pki/authorities/local/root.crt "$W/root.crt"; sudo chown "$USER" "$W/root.crt"
export GIT_SSL_CAINFO="$W/root.crt" GIT_TERMINAL_PROMPT=0
api() { tk=$1; shift; curl -sk -H "Authorization: token $tk" -H 'Content-Type: application/json' "$@"; }
J() { python3 -c 'import sys,json;d=json.load(sys.stdin);print(eval(sys.argv[1]))' "$1"; }
code() { tk=$1; shift; curl -sk -o /dev/null -w '%{http_code}' -H "Authorization: token $tk" "$@"; }
gt() { tk=$1; shift; git -c credential.helper= -c "http.extraHeader=Authorization: token $tk" "$@"; }

api appelle l'API avec un jeton donné, J extrait un champ du JSON reçu, code ne renvoie que le code HTTP, et gt passe le jeton à git en en-tête. L'option -k de curl, qui ne vérifie pas le certificat, n'est acceptable que parce que la CA du lab est locale. Le piège : un git pull ou un git fetch lancé sans passer par gt échoue avec could not read Username ... terminal prompts disabled, car GIT_TERMINAL_PROMPT=0 interdit toute question. Passez toujours par gt.

Une organisation se crée par POST /api/v1/orgs avec un nom et une visibilité, et il faut demander private explicitement. Sans le champ visibility, vous obtenez une organisation public : c'est la valeur par défaut, pas un choix de prudence. Créez deux organisations pour le voir.

Fenêtre de terminal
api $ATK -X POST https://$D/api/v1/orgs -d '{"username":"acme","visibility":"private"}' | J 'd["username"], d["visibility"]'
api $ATK -X POST https://$D/api/v1/orgs -d '{"username":"pardefaut"}' | J 'd["username"], d["visibility"]'

La sortie attendue est ('acme', 'private') puis ('pardefaut', 'public'). Dans l'interface, la même création passe par l'entrée « New organization » du menu « + » de la barre du haut. La visibilité se choisit entre « Public », « Limited (visible only to signed-in users) » et « Private (visible only to organization members) ». Ces libellés viennent du fichier de traduction de Forgejo 15 : vérifiez-les sur votre version. Le piège est la visibilité oubliée : une organisation créée « pour voir » reste publique, et la section sur la visibilité plus bas détaille ce que chaque valeur montre à un extérieur.

Une équipe a été créée sans que vous l'ayez demandée. Listez-la.

Fenêtre de terminal
api $ATK https://$D/api/v1/orgs/pardefaut/teams | J '[(t["name"],t["permission"],t["includes_all_repositories"]) for t in d]'

La sortie est [('Owners', 'owner', True)] : l'équipe Owners a le niveau owner et couvre tous les dépôts, présents et futurs. Gardez-la petite : c'est l'équipe de ceux qui administrent l'organisation, pas celle des développeurs.

Comment créer des équipes lecteurs et ecrivains ?

Section intitulée « Comment créer des équipes lecteurs et ecrivains ? »

On crée chaque équipe avec POST /api/v1/orgs/acme/teams, en fixant le niveau, les unités et includes_all_repositories à false. C'est ce dernier réglage qui applique le moindre privilège : une équipe sans dépôt rattaché ne voit rien. On crée d'abord les deux équipes, on relit leur identifiant, puis on y place alice et bob.

Fenêtre de terminal
api $ATK -X POST https://$D/api/v1/orgs/acme/teams -d '{"name":"lecteurs","description":"lecture","permission":"read","includes_all_repositories":false,"can_create_org_repo":false,"units_map":{"repo.code":"read","repo.issues":"read","repo.pulls":"read"}}' > t1.json
api $ATK -X POST https://$D/api/v1/orgs/acme/teams -d '{"name":"ecrivains","description":"ecriture","permission":"write","includes_all_repositories":false,"can_create_org_repo":false,"units_map":{"repo.code":"write","repo.issues":"write","repo.pulls":"write"}}' > t2.json
T1=$(J 'd["id"]' < t1.json); T2=$(J 'd["id"]' < t2.json)
api $ATK -X PUT https://$D/api/v1/teams/$T1/members/alice -o /dev/null -w 'alice -> lecteurs %{http_code}\n'
api $ATK -X PUT https://$D/api/v1/teams/$T2/members/bob -o /dev/null -w 'bob -> ecrivains %{http_code}\n'

Les réponses des deux créations sont gardées dans t1.json et t2.json, d'où l'on ne lit que l'identifiant id. Les deux ajouts de membre affichent alice -> lecteurs 204 et bob -> ecrivains 204 : 204 est le code d'un succès sans contenu. Dans l'interface, on retrouve ces réglages sur la page de l'organisation, onglet « Teams », avec le bouton « New team » : nom, permission (« Read », « Write », « Administrator access » ou « Custom access » pour fixer chaque unité), dépôts accessibles et unités.

Le piège : oublier "includes_all_repositories":false ferait voir à l'équipe tous les dépôts de l'organisation, y compris ceux de demain. Autre précision de l'interface : pour une unité, l'option « No access » n'a d'effet que sur les dépôts privés.

Il faut maintenant créer deux dépôts privés et n'en rattacher qu'un aux équipes.

Fenêtre de terminal
api $ATK -X POST https://$D/api/v1/orgs/acme/repos -d '{"name":"app","auto_init":true,"default_branch":"main","private":true}' | J 'd["full_name"], "private",d["private"]'
api $ATK -X POST https://$D/api/v1/orgs/acme/repos -d '{"name":"autre","auto_init":true,"default_branch":"main","private":true}' | J 'd["full_name"]'
for t in $T1 $T2; do api $ATK -X PUT https://$D/api/v1/teams/$t/repos/acme/app -o /dev/null -w "equipe $t -> acme/app %{http_code}\n"; done
api $ATK https://$D/api/v1/orgs/acme/teams | J '[(t["name"],t["permission"],t["includes_all_repositories"]) for t in d]'
api $ATK https://$D/api/v1/teams/$T1/repos | J '[r["full_name"] for r in d]'

Les rattachements répondent 204. La liste des équipes affiche ('ecrivains', 'write', False), ('lecteurs', 'read', False), ('Owners', 'owner', True), et les dépôts de lecteurs se résument à ['acme/app'] : l'équipe ne voit pas autre. Cette relecture donne déjà un premier indice, avant même de pousser du code.

Deux preuves se complètent : l'API donne la permission calculée par la forge, et git montre ce qu'un compte peut réellement faire. L'API se lit sans rien modifier, ce qui en fait le bon outil d'audit : pour un dépôt et un compte, collaborators/{user}/permission renvoie le niveau effectif.

Fenêtre de terminal
for u in alice bob carol; do echo -n "$u: "; api $ATK https://$D/api/v1/repos/acme/app/collaborators/$u/permission | J 'd.get("permission",d)'; done

La sortie est alice: read, bob: write et carol: none. Voyons si git confirme. On clone avec chaque jeton, on ajoute un fichier et on pousse sur main.

Fenêtre de terminal
for u in alice bob; do
eval "tk=\$T_$u"; gt $tk clone -q https://$D/acme/app.git c_$u 2>&1 | tail -1
( cd c_$u && git config user.email $u@example.test && git config user.name $u && echo $u > $u.txt && git add . && git commit -qm "ajout $u" && echo "-- push $u sur main :" && gt $tk push origin main 2>&1 | cut -c1-200 | tail -6 )
done

Pour alice, le push se termine par ! [remote rejected] main -> main (pre-receive hook declined) : le clone était permis, l'écriture non. Le message ne dit pas « droits insuffisants », il faut le savoir pour le reconnaître. Pour bob, la sortie ressemble à 5b40044..ca4f123 main -> main (les empreintes diffèrent chez vous) : le push est accepté. Restent deux cas qui ne doivent rien apprendre : carol, qui n'est dans aucune équipe, et alice, qui lit app mais n'a aucun droit sur autre.

Fenêtre de terminal
gt $T_carol clone -q https://$D/acme/app.git c_carol 2>&1 | tail -1
gt $T_alice clone -q https://$D/acme/autre.git c_x 2>&1 | tail -1

Les deux commandes renvoient fatal: repository '.../acme/app.git/' not found (et .../acme/autre.git/ pour la seconde). Ce message est identique à celui d'un dépôt qui n'existe pas : un compte sans droit ne peut pas deviner qu'un dépôt privé existe. Retenez-le pour le dépannage : un « not found » peut cacher un droit manquant, et c'est aussi la preuve que le rattachement dépôt par dépôt isole bien autre de l'équipe lecteurs.

Qui voit l'organisation : private, limited ou public ?

Section intitulée « Qui voit l'organisation : private, limited ou public ? »

La visibilité d'une organisation décide ce qu'un compte extérieur, qui n'est dans aucune équipe, peut voir : tout (public), l'organisation sans ses dépôts privés (limited), ou rien (private). Elle se change à tout moment avec PATCH /api/v1/orgs/acme. Mesurons-le avec carol, extérieure, après avoir créé un dépôt public vitrine dans acme.

Fenêtre de terminal
api $ATK -X POST https://$D/api/v1/orgs/acme/repos -d '{"name":"vitrine","auto_init":true,"default_branch":"main","private":false}' | J 'd["full_name"], "private",d["private"]'
echo "private : org=$(code $T_carol https://$D/api/v1/orgs/acme) repo=$(code $T_carol https://$D/api/v1/repos/acme/app)"
api $ATK -X PATCH https://$D/api/v1/orgs/acme -d '{"visibility":"limited"}' | J 'd["visibility"]'
echo "limited : org=$(code $T_carol https://$D/api/v1/orgs/acme) app=$(code $T_carol https://$D/api/v1/repos/acme/app) vitrine=$(code $T_carol https://$D/api/v1/repos/acme/vitrine)"
echo -n "carol voit les membres : "; api $T_carol https://$D/api/v1/orgs/acme/members | J 'len(d) if isinstance(d,list) else d'
api $ATK -X PATCH https://$D/api/v1/orgs/acme -d '{"visibility":"private"}' | J 'd["visibility"]'
echo "retour private : org=$(code $T_carol https://$D/api/v1/orgs/acme) vitrine=$(code $T_carol https://$D/api/v1/repos/acme/vitrine)"

Voici ce que carol obtient, en codes HTTP. La ligne private regroupe la mesure faite au départ et celle du retour à private.

Visibilité de acmeOrganisationDépôt privé appDépôt public vitrine
private404404404
limited200404200

Deux enseignements. Passer en limited rend l'organisation et ses dépôts publics visibles aux comptes connectés, mais pas ses dépôts privés ni la liste de ses membres : la commande affiche carol voit les membres : 0. Et un dépôt public d'une organisation private n'est pas visible d'un extérieur : la visibilité de l'organisation l'emporte. Le niveau public, lui, n'a pas été mesuré vu d'un non-membre, hormis le fait qu'il s'applique sans option à la création : si vous en avez besoin, relevez ses effets avec la même méthode.

Un visiteur anonyme reçoit ici un 403. Cela vient de l'instance de la leçon, où le réglage REQUIRE_SIGNIN_VIEW, posé dans la leçon Configuration initiale de Forgejo, force à se connecter pour consulter une page ou utiliser l'API (défaut false d'après la feuille de référence). Selon la documentation, limited ne montre le contenu qu'aux utilisateurs connectés. L'effet d'une visibilité sur un visiteur non connecté, sans ce réglage, n'a pas été mesuré.

Une règle de protection de branche interdit le push direct sur main et exige une approbation avant la fusion d'une pull request. Une protection de branche est une règle rattachée au nom d'une branche : elle restreint qui peut y pousser et à quelles conditions une pull request peut y être fusionnée. Une approbation est l'avis favorable (review) qu'un compte laisse sur la pull request. C'est l'étape qui transforme les droits d'écriture en revue de code : sans elle, bob, qui a write, pousse directement. Sur Forgejo 15 LTS, la règle se pose dépôt par dépôt par POST /api/v1/repos/{owner}/{repo}/branch_protections.

Fenêtre de terminal
api $ATK -X POST https://$D/api/v1/repos/acme/app/branch_protections -d '{"branch_name":"main","rule_name":"main","enable_push":false,"required_approvals":1,"dismiss_stale_approvals":true,"block_on_rejected_reviews":true}' | J 'd.get("rule_name"),"enable_push",d.get("enable_push"),"approvals",d.get("required_approvals"),d.get("message","")'

La sortie est ('main', 'enable_push', False, 'approvals', 1, ''). Les quatre champs sont lisibles : enable_push à false interdit le push direct, required_approvals à 1 exige une approbation, dismiss_stale_approvals annule les approbations devenues périmées après un nouveau commit, et block_on_rejected_reviews bloque la fusion tant qu'un avis négatif subsiste. Dans l'interface, la même règle se crée dans Settings, page « Branches », par le bouton « Add new rule ». Le formulaire porte les cases « Disable push », « Required approvals », « Dismiss stale approvals » et « Block merge on rejected reviews » (libellés du fichier de traduction de Forgejo 15, à vérifier sur votre version).

Une case compte pour le test de fusion plus bas : « Restrict approvals to whitelisted users or teams ». Elle limite les avis comptés à une liste ; sans elle, l'avis de toute personne ayant le droit d'écriture compte.

Il faut maintenant vérifier que la règle tient pour bob et pour l'administrateur propriétaire. Le clone de bob est déjà à jour de son dernier push : on y ajoute un commit et on pousse, puis on refait le geste en fjadmin sur un clone neuf, toujours avec gt pour porter le jeton.

Fenêtre de terminal
cd c_bob && echo bob2 > bob2.txt && git add . && git commit -qm "direct apres protection" && gt $T_bob push origin main 2>&1 | cut -c1-220 | tail -6; cd ..
gt $ATK clone -q https://$D/acme/app.git c_adm && cd c_adm && git config user.email adm@example.test && git config user.name adm && echo adm > adm.txt && git add . && git commit -qm "admin direct" && gt $ATK push origin main 2>&1 | cut -c1-220 | tail -5; cd ..

Les deux pushes, celui de bob et celui de fjadmin, donnent remote: Forgejo: Not allowed to push to protected branch main suivi de ! [remote rejected] main -> main (pre-receive hook declined). Même le propriétaire est arrêté : avec enable_push à false, le push direct est refusé à tous, et il faut passer par une pull request. Forgejo 15 propose en outre l'option apply_to_admins (« Enforce this rule for repository admins »), à false par défaut. La requête ci-dessus ne la modifie pas : ces observations valent pour sa valeur par défaut, et il vous revient de tester son effet avant de compter sur la règle contre un administrateur de dépôt. Le guide Gouvernance du code source replace cette protection dans une démarche de chaîne d'approvisionnement.

Que se passe-t-il à la fusion d'une pull request ?

Section intitulée « Que se passe-t-il à la fusion d'une pull request ? »

La fusion est refusée tant qu'aucun ayant droit en écriture n'a approuvé : l'avis d'un lecteur est accepté par l'API mais ne compte pas. Un ayant droit est ici un compte qui a au moins write sur le dépôt. Jouons le flux complet : bob pousse une branche feature et ouvre la pull request (sur le cycle lui-même, voir Pull requests et code review).

Fenêtre de terminal
gt $T_bob clone -q https://$D/acme/app.git cb; cd cb
git config user.email bob@example.test; git config user.name bob
git checkout -q -b feature; echo feat > feat.txt; git add .; git commit -qm feat
gt $T_bob push -q origin feature 2>&1 | tail -3
PR=$(api $T_bob -X POST https://$D/api/v1/repos/acme/app/pulls -d '{"title":"feature","head":"feature","base":"main"}' | J 'd["number"]')
echo "PR #$PR"

La sortie inclut PR #1. On tente ensuite de fusionner à trois moments successifs : sans approbation, après l'approbation d'alice (lectrice), puis après celle de fjadmin.

Fenêtre de terminal
api $ATK -X POST https://$D/api/v1/repos/acme/app/pulls/$PR/merge -d '{"Do":"merge"}' -w ' http=%{http_code}'
api $T_alice -X POST https://$D/api/v1/repos/acme/app/pulls/$PR/reviews -d '{"event":"APPROVED"}' -w ' http=%{http_code}' | cut -c1-200
api $ATK -X POST https://$D/api/v1/repos/acme/app/pulls/$PR/merge -d '{"Do":"merge"}' -w ' http=%{http_code}'
api $ATK -X POST https://$D/api/v1/repos/acme/app/pulls/$PR/reviews -d '{"event":"APPROVED"}' -w ' http=%{http_code}' | cut -c1-120
api $ATK -X POST https://$D/api/v1/repos/acme/app/pulls/$PR/merge -d '{"Do":"merge"}' -w ' http=%{http_code}'
api $ATK https://$D/api/v1/repos/acme/app/pulls/$PR | J '"merged",d["merged"]'

Les résultats sont les suivants. Avant toute approbation, la fusion répond 405 avec not allowed to merge [reason: Does not have enough approvals]. L'approbation d'alice est acceptée (http=200), mais la fusion reste refusée pour la même raison : son avis ne compte pas, car elle n'a que read. Après l'approbation de fjadmin, la fusion répond http=200 et le dernier appel affiche ('merged', True). Le piège : voir une approbation « acceptée » ne prouve pas qu'elle compte, seule la fusion le dit.

Quelles sont les valeurs par défaut à connaître ?

Section intitulée « Quelles sont les valeurs par défaut à connaître ? »

Un dépôt créé sans aucune option est public, sur la branche main, avec toutes les unités activées et le style de fusion merge. Ces valeurs ne protègent pas : elles conviennent à une forge ouverte, pas à une forge où l'on veut un dépôt privé par défaut. Relevez-les en créant un dépôt x dans l'organisation pardefaut de tout à l'heure, puis un dépôt y pour fjadmin.

Fenêtre de terminal
api $ATK -X POST https://$D/api/v1/orgs/pardefaut/repos -d '{"name":"x"}' | J '{k:d[k] for k in ["private","default_branch","has_issues","has_wiki","has_pull_requests","has_projects","has_packages","has_actions","has_releases","default_merge_style"] if k in d}'
echo -n "depot utilisateur sans options : private = "; api $ATK -X POST https://$D/api/v1/user/repos -d '{"name":"y"}' | J 'd["private"]'
echo -n "visibilite par defaut d'un utilisateur : "; api $ATK https://$D/api/v1/users/alice | J 'd["visibility"]'

Le tableau résume ce qui est sorti, avec le geste qui corrige.

Objet créé sans optionValeur observéePour l'éviter
Dépôt d'organisationprivate: False, branche main, 7 unités actives, fusion mergepasser "private":true à la création
Dépôt d'utilisateurprivate: Falsepasser "private":true (par l'API, DEFAULT_PRIVATE n'agit pas)
Organisationvisibilité publicpasser "visibility":"private"
Utilisateurvisibilité publicà régler sur chaque compte
Équipes d'une organisation neuveOwners seule, niveau owner, tous les dépôtscréer ses propres équipes
Protection de mainaucunePOST .../branch_protections, dépôt par dépôt

Les sept unités actives sont les tickets, le wiki, les pull requests, les projets, les paquets, les actions et les versions (has_issues, has_wiki, has_pull_requests, has_projects, has_packages, has_actions, has_releases). Dans la pile d'installation, la section [repository] de app.ini ne contient que ROOT = /data/git/repositories : tout le reste vient des valeurs par défaut de la forge, décrites dans la feuille de référence de configuration. Le réglage DEFAULT_PRIVATE de cette section accepte last, private ou public, avec last par défaut : le dernier choix de visibilité fait par l'utilisateur. Il présélectionne la case du formulaire web ; le code de Forgejo 15 ne le lit pas pour un dépôt créé par l'API, qui reste public sans "private":true. Pour imposer le privé à tous, le réglage est FORCE_PRIVATE (non essayé ici : testez-le sur votre instance).

Le point à retenir pour un administrateur : il n'existe, en 15 LTS, aucun réglage de protection de branche au niveau de l'organisation. L'API ne propose que le chemin /repos/{owner}/{repo}/branch_protections (relevé dans le swagger.v1.json de l'instance, et retrouvé dans celui de la branche v15.0 du code source). Chaque nouveau dépôt doit donc recevoir sa règle, et la mention « valeur par défaut » ne s'applique pas à la protection.

Que faire quand un droit ne se comporte pas comme prévu ?

Section intitulée « Que faire quand un droit ne se comporte pas comme prévu ? »

La plupart des pannes de droits se lisent dans le message de git ou le code HTTP, à condition de savoir que la forge cache volontairement l'existence des dépôts. Le tableau relie chaque symptôme rencontré dans cette leçon à sa cause et à la correction.

SymptômeCauseCorrection
! [remote rejected] main -> main (pre-receive hook declined) pour un compte qui clone sans problèmele compte n'a que read sur le dépôtrelever collaborators/{user}/permission, passer l'équipe en write si c'est voulu
remote: Forgejo: Not allowed to push to protected branch mainrègle de protection : enable_push à falsepousser une branche, ouvrir une pull request
fatal: repository '.../acme/app.git/' not foundle compte n'est dans aucune équipe, ou le dépôt n'est pas rattaché à son équipe (includes_all_repositories à false)GET /teams/{id}/repos, puis PUT /teams/{id}/repos/acme/app
Fusion refusée : 405 ... Does not have enough approvalsaucune approbation d'un compte en écriturefaire approuver par un ayant droit ; l'avis d'un lecteur ne compte pas
could not read Username ... terminal prompts disabledun git pull ou git fetch lancé sans jetonpasser par gt <jeton> pull, ou configurer un identifiant
Appels de l'anonyme en 403REQUIRE_SIGNIN_VIEW impose la connexioncomportement voulu ; se connecter pour consulter
Un dépôt public de acme reste invisiblel'organisation est private, ce qui primepasser l'organisation en limited ou public si le besoin est réel

Le premier réflexe est de mesurer avant de modifier : relevez la permission effective, listez les dépôts de l'équipe, puis seulement ajustez. Les réglages des équipes se relisent à tout moment avec GET /api/v1/orgs/acme/teams.

Quatre situations n'ont pas été essayées dans cette leçon : mesurez-les sur votre instance avant de vous y fier. La première est une équipe avec includes_all_repositories à true : seule Owners a été observée ainsi, et GET /teams/{id}/repos montre ce qu'une autre équipe voit réellement. La deuxième est la liste blanche de push d'une règle de protection : testez un git push avec un compte listé puis avec un compte non listé.

La troisième est le droit différencié par unité (code en lecture, tickets en écriture) : les équipes de la leçon ont le même niveau partout, donc relisez leur units_map après toute variation. La dernière est celle des équipes alimentées par un annuaire ou un fournisseur d'identité : tant que vous ne savez pas comment l'annuaire crée ou remplit les équipes, n'appliquez pas cette procédure telle quelle.

  • Les droits se donnent par équipe, sur des dépôts rattachés, avec un niveau (read, write, admin, owner) et des unités (units_map).
  • L'équipe Owners est créée seule avec l'organisation, au niveau owner et sur tous les dépôts ; une équipe créée avec includes_all_repositories à false ne voit que les dépôts qu'on lui rattache.
  • La permission effective se relève par GET /repos/{owner}/{repo}/collaborators/{user}/permission : read, write ou none.
  • Un lecteur clone mais son push est refusé par pre-receive hook declined ; un non-membre reçoit repository not found, identique à un dépôt inexistant.
  • La visibilité d'une organisation prime sur celle de ses dépôts : un dépôt public d'une organisation private reste invisible d'un extérieur.
  • En limited, un compte connecté voit l'organisation et ses dépôts publics, mais ni ses dépôts privés ni ses membres ; sur une instance qui impose la connexion (REQUIRE_SIGNIN_VIEW), l'anonyme reçoit 403.
  • Avec enable_push à false, le push direct sur main est refusé à tous, administrateur propriétaire compris ; la fusion exige l'approbation d'un compte en écriture, et celle d'un lecteur est acceptée sans effet.
  • En 15 LTS, la protection de branche n'existe que par dépôt ; les valeurs par défaut (dépôt, organisation et utilisateur publics) demandent un "private":true (dépôt) ou un "visibility":"private" (organisation) explicite.

Six questions tirées sur huit : rattacher une équipe à ses dépôts, lire un refus de push, ce que voit un non-membre, la visibilité par défaut et la protection de main.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn