internal/control/runnerrepo_test.go
107 lines · 4214 bytes
1package control
2
3import (
4 "bytes"
5 "strings"
6 "testing"
7
8 "gitbay.org/gitbay/internal/config"
9 "gitbay.org/gitbay/internal/protocol"
10 "gitbay.org/gitbay/internal/store"
11)
12
13// Generated once with ssh-keygen -t ed25519; a valid authorized_keys line.
14const testRunnerPub = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAr2r82jFsCJwsEyrEf2wgKy9Dv45xYYici6Ii7NyCS runner@test\n"
15
16func repoRunnerCtx(t *testing.T, st *store.Store, uid int64, admin bool, stdin string) (*Ctx, *bytes.Buffer) {
17 t.Helper()
18 var out bytes.Buffer
19 return &Ctx{
20 User: store.User{ID: uid, Username: "alice", IsAdmin: admin},
21 Scope: "full",
22 Source: "SHA256:session",
23 Store: st,
24 Cfg: config.Config{Server: config.Server{SiteURL: "https://x.test"}},
25 Stdin: strings.NewReader(stdin),
26 Stdout: &out,
27 Stderr: &out,
28 JSON: true,
29 }, &out
30}
31
32// A fresh key is registered on the caller's account with scope runner and
33// attached; a second add is a no-op; list shows it; remove detaches and
34// leaves the key on the account.
35func TestRepoRunnerAddListRemove(t *testing.T) {
36 st, repo, uid := newQueueTestRepo(t)
37 c, out := repoRunnerCtx(t, st, uid, false, testRunnerPub)
38 if code := runRepoRunnerAdd(c, []string{repo.Path()}); code != protocol.ExitOK {
39 t.Fatalf("add: exit %d %s", code, out.String())
40 }
41 if !strings.Contains(out.String(), `"fingerprint":"SHA256:`) {
42 t.Fatalf("add output: %s", out.String())
43 }
44 keys, _ := st.ListSSHKeys(uid)
45 if len(keys) != 1 || keys[0].Scope != "runner" {
46 t.Fatalf("key not registered as runner: %+v", keys)
47 }
48 fp := keys[0].Fingerprint
49 c, out = repoRunnerCtx(t, st, uid, false, testRunnerPub)
50 if code := runRepoRunnerAdd(c, []string{repo.Path()}); code != protocol.ExitOK {
51 t.Fatalf("second add: exit %d %s", code, out.String())
52 }
53 c, out = repoRunnerCtx(t, st, uid, false, "")
54 if code := runRepoRunnerList(c, []string{repo.Path()}); code != protocol.ExitOK || strings.Count(out.String(), fp) != 1 {
55 t.Fatalf("list: exit %d %s", code, out.String())
56 }
57 c, out = repoRunnerCtx(t, st, uid, false, "")
58 if code := runRepoRunnerRemove(c, []string{repo.Path(), fp}); code != protocol.ExitOK {
59 t.Fatalf("remove: exit %d %s", code, out.String())
60 }
61 if ok, _ := st.RunnerAttached(keys[0].ID, repo.ID); ok {
62 t.Fatal("still attached after remove")
63 }
64 if keys, _ = st.ListSSHKeys(uid); len(keys) != 1 {
65 t.Fatal("remove dropped the key from the account")
66 }
67 c, out = repoRunnerCtx(t, st, uid, false, "")
68 if code := runRepoRunnerRemove(c, []string{repo.Path(), fp}); code != protocol.ExitNotFound {
69 t.Fatalf("remove twice: exit %d, want %d", code, protocol.ExitNotFound)
70 }
71}
72
73// A key that already exists with another scope is never promoted, and
74// another account's runner key is refused unless the caller is an admin.
75func TestRepoRunnerAddRefusesWrongKeys(t *testing.T) {
76 st, repo, uid := newQueueTestRepo(t)
77 c, _ := repoRunnerCtx(t, st, uid, false, testRunnerPub)
78 // Register the same key as a full key first.
79 if code := runKeysAdd(c, nil); code != protocol.ExitOK {
80 t.Fatal("keys add failed")
81 }
82 c, out := repoRunnerCtx(t, st, uid, false, testRunnerPub)
83 if code := runRepoRunnerAdd(c, []string{repo.Path()}); code != protocol.ExitDenied {
84 t.Fatalf("full key accepted as runner: exit %d %s", code, out.String())
85 }
86 keys, _ := st.ListSSHKeys(uid)
87 if keys[0].Scope != "full" {
88 t.Fatalf("scope changed to %s", keys[0].Scope)
89 }
90 // Someone else's runner key.
91 bob, _ := st.CreateUser("bob", false)
92 if err := st.AddSSHKey(bob, "SHA256:bobrunner", "ssh-ed25519", []byte("x"), "runner"); err != nil {
93 t.Fatal(err)
94 }
95 st.RemoveSSHKey(uid, keys[0].Fingerprint)
96 if err := st.AddSSHKey(bob, keys[0].Fingerprint, "ssh-ed25519", keys[0].Blob, "runner"); err != nil {
97 t.Fatal(err)
98 }
99 c, out = repoRunnerCtx(t, st, uid, false, testRunnerPub)
100 if code := runRepoRunnerAdd(c, []string{repo.Path()}); code != protocol.ExitDenied {
101 t.Fatalf("another account's key attached by a non-admin: exit %d %s", code, out.String())
102 }
103 c, out = repoRunnerCtx(t, st, uid, true, testRunnerPub)
104 if code := runRepoRunnerAdd(c, []string{repo.Path()}); code != protocol.ExitOK {
105 t.Fatalf("admin could not attach another account's runner key: exit %d %s", code, out.String())
106 }
107}