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 | } | |