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.

Create accounts with a key and find the instance config

c36d507302c0f7fb24af6eba390c4121d67318c3 · Keenan Nemetz · 16d ago · parent 3abbe749

Create accounts with a key and find the instance config

An account is now written with its first key in one transaction, so a name cannot be claimed without a credential. add-key remains for additional keys, and remote user create forwards the key on stdin.

Local commands now resolve the config from --config, VOLE_CONFIG, ./vole.toml, or ~/.vole/vole.toml before falling back to defaults. init prints --config in its next steps when the instance will not be discovered automatically.

server/admin_test.go +21−6

@@ -4,6 +4,7 @@ import (
4 4 "bytes"
5 5 "encoding/json"
6 6 "net"
7 "os"
7 8 "os/exec"
8 9 "path/filepath"
9 10 "strings"
@@ -59,15 +60,24 @@ func TestRemoteAdminManagesUsersAndRepos(t *testing.T) {
59 60 }
60 61
61 62 out, errOut, err := f.adminSSH(f.keyPath, nil, "user", "create", "alice")
63 if err == nil {
64 t.Fatalf("user create without a key succeeded: %s%s", out, errOut)
65 }
66
67 pub, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_alice"))
68 out, errOut, err = f.adminSSH(f.keyPath, []byte(pub), "user", "create", "alice")
62 69 if err != nil {
63 70 t.Fatalf("user create: %v\nstdout: %s\nstderr: %s", err, out, errOut)
64 71 }
65 72 if !strings.Contains(out, "alice") {
66 73 t.Fatalf("user create did not name alice: %s", out)
67 74 }
75 if a, err := f.srv.Identity.Find("alice"); err != nil || len(a.Keys) != 1 {
76 t.Fatalf("user create did not attach the key: %+v, %v", a, err)
77 }
68 78
69 pub, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_alice"))
70 out, errOut, err = f.adminSSH(f.keyPath, []byte(pub), "user", "add-key", "alice")
79 second, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_alice_second"))
80 out, errOut, err = f.adminSSH(f.keyPath, []byte(second), "user", "add-key", "alice")
71 81 if err != nil {
72 82 t.Fatalf("user add-key: %v\nstdout: %s\nstderr: %s", err, out, errOut)
73 83 }
@@ -170,7 +180,11 @@ func TestAdminCLIForwardsToRemote(t *testing.T) {
170 180 // The client reads a key file and sends it on stdin; the server never sees
171 181 // the client's filesystem.
172 182 pub, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_cli_alice"))
173 cmd = exec.Command(binPath, "admin", "--remote", "ssh://git@"+f.sshAddr, "user", "create", "alice")
183 pubPath := filepath.Join(f.cfg.DataDir, "id_cli_alice.pub")
184 if err := os.WriteFile(pubPath, []byte(pub), 0o600); err != nil {
185 t.Fatal(err)
186 }
187 cmd = exec.Command(binPath, "admin", "--remote", "ssh://git@"+f.sshAddr, "user", "create", "alice", "--key", pubPath)
174 188 cmd.Env = append(cmd.Environ(),
175 189 "VOLE_SSH_COMMAND=ssh -i "+f.keyPath+" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR",
176 190 "GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_SYSTEM=/dev/null",
@@ -178,16 +192,17 @@ func TestAdminCLIForwardsToRemote(t *testing.T) {
178 192 if err := cmd.Run(); err != nil {
179 193 t.Fatalf("remote user create: %v", err)
180 194 }
195 second, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_cli_alice_second"))
181 196 cmd = exec.Command(binPath, "admin", "--remote", "ssh://git@"+f.sshAddr, "user", "add-key", "alice")
182 197 cmd.Env = append(cmd.Environ(),
183 198 "VOLE_SSH_COMMAND=ssh -i "+f.keyPath+" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR",
184 199 "GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_SYSTEM=/dev/null",
185 200 )
186 cmd.Stdin = strings.NewReader(pub)
201 cmd.Stdin = strings.NewReader(second)
187 202 if err := cmd.Run(); err != nil {
188 203 t.Fatalf("remote user add-key: %v", err)
189 204 }
190 if a, err := f.srv.Identity.Find("alice"); err != nil || len(a.Keys) != 1 {
191 t.Fatalf("alice's key did not land: %+v, %v", a, err)
205 if a, err := f.srv.Identity.Find("alice"); err != nil || len(a.Keys) != 2 {
206 t.Fatalf("alice's keys did not land: %+v, %v", a, err)
192 207 }
193 208 }