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.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- 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,limitedoupublic. - Protéger
mainet 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é.
Quels sont les prérequis ?
Section intitulée « Quels sont les prérequis ? »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.
Comment Forgejo organise-t-il les droits ?
Section intitulée « Comment Forgejo organise-t-il les droits ? »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.
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.
| Niveau | Qui, dans cette leçon | Ce 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 |
write | bob, équipe ecrivains | git push sur main accepté tant que main n'est pas protégée |
read | alice, équipe lecteurs | clone possible, git push refusé (pre-receive hook declined) |
none | carol, aucune équipe | dé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.
D=forge.lab.testATK=$(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 -1doneT_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.
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=0api() { 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.
Comment créer une organisation privée ?
Section intitulée « Comment créer une organisation privée ? »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.
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.
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.
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.jsonapi $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.jsonT1=$(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.
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"; doneapi $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.
Comment prouver les droits effectifs ?
Section intitulée « Comment prouver les droits effectifs ? »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.
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)'; doneLa 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.
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 )donePour 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.
gt $T_carol clone -q https://$D/acme/app.git c_carol 2>&1 | tail -1gt $T_alice clone -q https://$D/acme/autre.git c_x 2>&1 | tail -1Les 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.
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 acme | Organisation | Dépôt privé app | Dépôt public vitrine |
|---|---|---|---|
private | 404 | 404 | 404 |
limited | 200 | 404 | 200 |
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é.
Comment protéger la branche main ?
Section intitulée « Comment protéger la branche main ? »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.
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.
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).
gt $T_bob clone -q https://$D/acme/app.git cb; cd cbgit config user.email bob@example.test; git config user.name bobgit checkout -q -b feature; echo feat > feat.txt; git add .; git commit -qm featgt $T_bob push -q origin feature 2>&1 | tail -3PR=$(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.
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-200api $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-120api $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.
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 option | Valeur observée | Pour l'éviter |
|---|---|---|
| Dépôt d'organisation | private: False, branche main, 7 unités actives, fusion merge | passer "private":true à la création |
| Dépôt d'utilisateur | private: False | passer "private":true (par l'API, DEFAULT_PRIVATE n'agit pas) |
| Organisation | visibilité public | passer "visibility":"private" |
| Utilisateur | visibilité public | à régler sur chaque compte |
| Équipes d'une organisation neuve | Owners seule, niveau owner, tous les dépôts | créer ses propres équipes |
Protection de main | aucune | POST .../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ôme | Cause | Correction |
|---|---|---|
! [remote rejected] main -> main (pre-receive hook declined) pour un compte qui clone sans problème | le compte n'a que read sur le dépôt | relever collaborators/{user}/permission, passer l'équipe en write si c'est voulu |
remote: Forgejo: Not allowed to push to protected branch main | règle de protection : enable_push à false | pousser une branche, ouvrir une pull request |
fatal: repository '.../acme/app.git/' not found | le 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 approvals | aucune approbation d'un compte en écriture | faire approuver par un ayant droit ; l'avis d'un lecteur ne compte pas |
could not read Username ... terminal prompts disabled | un git pull ou git fetch lancé sans jeton | passer par gt <jeton> pull, ou configurer un identifiant |
Appels de l'anonyme en 403 | REQUIRE_SIGNIN_VIEW impose la connexion | comportement voulu ; se connecter pour consulter |
Un dépôt public de acme reste invisible | l'organisation est private, ce qui prime | passer 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.
Que vérifier soi-même avant de généraliser ?
Section intitulée « Que vérifier soi-même avant de généraliser ? »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.
À retenir
Section intitulée « À retenir »- 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
Ownersest créée seule avec l'organisation, au niveauowneret sur tous les dépôts ; une équipe créée avecincludes_all_repositoriesàfalsene 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,writeounone. - Un lecteur clone mais son push est refusé par
pre-receive hook declined; un non-membre reçoitrepository 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
privatereste 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çoit403. - Avec
enable_pushàfalse, le push direct surmainest 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.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »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
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
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Brancher Forgejo sur un fournisseur OIDC et imposer le 2FA
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications