Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Answer whoami --role from the repository's rules

eec0e60344bf5686fc278ba5a8415dc0777ba9e2 · Keenan Nemetz · 6d ago · parent f422c24f

Answer whoami --role from the repository's rules

A role is per repository, so whoami cannot print one truth without knowing which repository is meant. Keep the default answer name-only, for the scripts that call it, and make the role an explicit query on the remote's repository.

The server's vole-whoami verb now accepts a repository path and returns the effective role after the same private-repository rule as a fetch. A run credential may ask only about its own repository. Update the CLI help and the contributor and account docs to point at the new query.

server/integration_test.go +64−1

@@ -452,11 +452,18 @@ func (f *fixture) get(path string) (int, string) {
452 452 // cli runs the built binary in dir as the owner. A null global git config means
453 453 // a developer's own identity cannot change what a test sees.
454 454 func (f *fixture) cli(dir string, args ...string) (string, string, error) {
455 f.t.Helper()
456 return f.cliWithKey(f.keyPath, dir, args...)
457 }
458
459 // cliWithKey is cli for another account. The key is what the instance resolves,
460 // so a test can ask a different role the same question.
461 func (f *fixture) cliWithKey(key, dir string, args ...string) (string, string, error) {
455 462 f.t.Helper()
456 463 cmd := exec.Command(binPath, args...)
457 464 cmd.Dir = dir
458 465 cmd.Env = append(os.Environ(),
459 "GIT_SSH_COMMAND=ssh -i "+f.keyPath+" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR",
466 "GIT_SSH_COMMAND=ssh -i "+key+" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR",
460 467 "GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_SYSTEM=/dev/null",
461 468 "USER=keenan")
462 469 var out, errb bytes.Buffer
@@ -1131,6 +1138,62 @@ func TestWhoAmIAnswersFromTheInstance(t *testing.T) {
1131 1138 }
1132 1139 }
1133 1140
1141 // TestWhoAmIRoleAnswersFromTheInstance checks the explicit role query. The role
1142 // is per repository, so the answer comes from the ACL behind the remote, not from
1143 // the account's instance role alone. A private repository the key cannot read
1144 // gives the same answer as a clone: not found.
1145 func TestWhoAmIRoleAnswersFromTheInstance(t *testing.T) {
1146 t.Parallel()
1147 f := newFixture(t)
1148 work := workRepo(t, map[string]string{"readme.md": "hi\n"})
1149 if _, errs, err := f.git(work, "push", f.sshURL(f.repo), "main:refs/heads/main"); err != nil {
1150 t.Fatalf("push main: %s", errs)
1151 }
1152 clone := filepath.Join(t.TempDir(), "clone")
1153 if _, errs, err := f.git("", "clone", "-q", f.sshURL(f.repo), clone); err != nil {
1154 t.Fatalf("clone: %s", errs)
1155 }
1156
1157 out, errs, err := f.cli(clone, "whoami", "--role")
1158 if err != nil {
1159 t.Fatalf("whoami --role: %v\n%s", err, errs)
1160 }
1161 if got := strings.TrimSpace(out); got != "admin" {
1162 t.Fatalf("whoami --role = %q, want admin for the repository owner", got)
1163 }
1164
1165 writerKey := f.addWriter(t)
1166 out, errs, err = f.cliWithKey(writerKey, clone, "whoami", "--role")
1167 if err != nil {
1168 t.Fatalf("writer whoami --role: %v\n%s", err, errs)
1169 }
1170 if got := strings.TrimSpace(out); got != "maintain" {
1171 t.Fatalf("writer whoami --role = %q, want maintain", got)
1172 }
1173
1174 // A stranger on a private repository learns nothing about it, just like a
1175 // fetch that the read rule refuses.
1176 pub, priv := keypair(t, filepath.Join(f.cfg.DataDir, "id_stranger"))
1177 if _, err := f.srv.Identity.Create("stranger", pub, "test"); err != nil {
1178 t.Fatal(err)
1179 }
1180 if _, errs, err := f.cliWithKey(priv, clone, "whoami", "--role"); err == nil {
1181 t.Fatalf("a stranger read a private repository's role: %s", errs)
1182 } else if !strings.Contains(errs, "not found") {
1183 t.Fatalf("the refusal does not hide the private repository: %s", errs)
1184 }
1185
1186 // Published, the repository's default role is the account's role.
1187 f.publish(f.repo)
1188 out, errs, err = f.cliWithKey(priv, clone, "whoami", "--role")
1189 if err != nil {
1190 t.Fatalf("stranger whoami --role on a public repository: %v\n%s", err, errs)
1191 }
1192 if got := strings.TrimSpace(out); got != "read" {
1193 t.Fatalf("stranger whoami --role = %q, want read after publishing", got)
1194 }
1195 }
1196
1134 1197 // TestReadCommandsNeedNoIdentity: `issue list` and `issue show` read the branch,
1135 1198 // so they must keep working where the instance cannot be reached at all.
1136 1199 func TestReadCommandsNeedNoIdentity(t *testing.T) {