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.

Let instance admins read every repository

381d6aac38ab785d879fa82a6b8397ce6f543bab · Keenan Nemetz · 14d ago · parent 074ae160

Let instance admins read every repository

An instance administrator can already rewrite any repository's ACL, so hiding its content only hides what they govern. Raise them to at least read in the web and SSH paths, preserving any higher explicit role, and enforce the read rule on SSH upload-pack, where any authenticated key could previously clone a private repository by name. Anonymous HTTP clone still answers 404 because it carries no identity.

Group the admin surface into Repositories and Users tabs with a shared header, and teach Page.Section to mark the active tab.

server/ssh/sshserver_test.go +36

@@ -257,6 +257,42 @@ func TestPushAndCloneOverSSH(t *testing.T) {
257 257 }
258 258 }
259 259
260 // TestInstanceAdminCanCloneWithoutACLEntry proves the read gate still applies to
261 // ordinary accounts, and that instance administration is the one overlay: they
262 // can clone a repository whose rules they can already rewrite.
263 func TestInstanceAdminCanCloneWithoutACLEntry(t *testing.T) {
264 t.Parallel()
265 h := newHarness(t)
266 work := workRepo(t)
267 if _, errs, err := h.git(work, h.keyPath, "push", h.url(), "main:refs/heads/main"); err != nil {
268 t.Fatalf("push: %v\n%s", err, errs)
269 }
270
271 opKey, opPub := keypair(t, filepath.Join(h.dir, "operator"))
272 if _, err := h.ident.Create("operator", opPub, "test"); err != nil {
273 t.Fatal(err)
274 }
275
276 if _, _, err := h.git("", opKey, "clone", "-q", h.url(), filepath.Join(t.TempDir(), "denied")); err == nil {
277 t.Fatal("an account with no role cloned a private repository")
278 }
279
280 if _, err := h.ident.SetAdmin("operator", true, "test"); err != nil {
281 t.Fatal(err)
282 }
283 dst := filepath.Join(t.TempDir(), "allowed")
284 if _, errs, err := h.git("", opKey, "clone", "-q", h.url(), dst); err != nil {
285 t.Fatalf("instance admin clone: %v\n%s", err, errs)
286 }
287 got, err := os.ReadFile(filepath.Join(dst, "readme.md"))
288 if err != nil {
289 t.Fatal(err)
290 }
291 if string(got) != "# hi\n" {
292 t.Fatalf("cloned content = %q", got)
293 }
294 }
295
260 296 // TestReadOnlyUserCannotPush proves the hook sees the SSH identity: the
261 297 // rejection has to name the user, not just fail.
262 298 func TestReadOnlyUserCannotPush(t *testing.T) {