internal/store/tokens_test.go
115 lines · 3365 bytes
6 symbols in this file
1package store
2
3import (
4 "slices"
5 "testing"
6 "time"
7)
8
9func tokenID(t *testing.T, s *Store, hash string) int64 {
10 t.Helper()
11 _, tok, err := s.APITokenUser(hash)
12 if err != nil {
13 t.Fatal(err)
14 }
15 return tok.ID
16}
17
18// parent made child, child made grandchild and a key; the key belongs
19// to another account, as admin user create --key makes one.
20func tokenChain(t *testing.T) (*Store, int64, *[]Revoked) {
21 t.Helper()
22 s, uid, got := revokeFixture(t)
23 bob, err := s.CreateUser("bob", false)
24 if err != nil {
25 t.Fatal(err)
26 }
27 if err := s.CreateAPIToken(uid, "parent", "h-parent", "full", nil, 0); err != nil {
28 t.Fatal(err)
29 }
30 if err := s.CreateAPIToken(uid, "child", "h-child", "full", nil, tokenID(t, s, "h-parent")); err != nil {
31 t.Fatal(err)
32 }
33 child := tokenID(t, s, "h-child")
34 if err := s.CreateAPIToken(uid, "grandchild", "h-grand", "read", nil, child); err != nil {
35 t.Fatal(err)
36 }
37 if err := s.AddSSHKeyFrom(bob, "SHA256:k", "ssh-ed25519", []byte("k"), "full", "", KeyOrigin{CreatedByToken: child}); err != nil {
38 t.Fatal(err)
39 }
40 return s, uid, got
41}
42
43func TestTokenRecordsItsCreator(t *testing.T) {
44 s, uid, _ := tokenChain(t)
45 toks, err := s.ListAPITokens(uid)
46 if err != nil {
47 t.Fatal(err)
48 }
49 by := map[string]string{}
50 for _, tk := range toks {
51 by[tk.Name] = tk.CreatedBy
52 }
53 if by["parent"] != "" || by["child"] != "parent" || by["grandchild"] != "child" {
54 t.Fatalf("created by: %v", by)
55 }
56 bob, _ := s.UserByUsername("bob")
57 keys, err := s.ListSSHKeys(bob.ID)
58 if err != nil || len(keys) != 1 || keys[0].CreatedBy != "child" {
59 t.Fatalf("key created by: %+v %v", keys, err)
60 }
61}
62
63func TestRevokeAPITokenListsWhatItCreated(t *testing.T) {
64 s, uid, got := tokenChain(t)
65 c, err := s.RevokeAPIToken(uid, "parent", false)
66 if err != nil {
67 t.Fatal(err)
68 }
69 if !slices.Equal(c.Tokens, []string{"child", "grandchild"}) || !slices.Equal(c.Keys, []string{"SHA256:k"}) {
70 t.Fatalf("created = %+v", c)
71 }
72 // Listed, not removed; the link to the revoked parent is gone.
73 toks, _ := s.ListAPITokens(uid)
74 if len(toks) != 2 || toks[0].Name != "child" || toks[0].CreatedBy != "" {
75 t.Fatalf("tokens after revoke: %+v", toks)
76 }
77 if _, err := s.SSHKeyByFingerprint("SHA256:k"); err != nil {
78 t.Fatalf("the key went: %v", err)
79 }
80 if len(*got) != 0 {
81 t.Fatalf("announced %+v with nothing revoked but the token", *got)
82 }
83}
84
85func TestRevokeAPITokenWithCreated(t *testing.T) {
86 s, uid, got := tokenChain(t)
87 k, _ := s.SSHKeyByFingerprint("SHA256:k")
88 if _, err := s.RevokeAPIToken(uid, "parent", true); err != nil {
89 t.Fatal(err)
90 }
91 if toks, _ := s.ListAPITokens(uid); len(toks) != 0 {
92 t.Fatalf("tokens left: %+v", toks)
93 }
94 if _, err := s.SSHKeyByFingerprint("SHA256:k"); err != ErrNotFound {
95 t.Fatalf("key left: %v", err)
96 }
97 if len(*got) != 1 || !slices.Equal((*got)[0].KeyIDs, []int64{k.ID}) {
98 t.Fatalf("announced %+v", *got)
99 }
100 if _, err := s.RevokeAPIToken(uid, "parent", true); err != ErrNotFound {
101 t.Fatalf("second revoke: %v", err)
102 }
103}
104
105func TestAPITokenUserCarriesExpiry(t *testing.T) {
106 s, uid, _ := revokeFixture(t)
107 exp := time.Now().Add(time.Hour)
108 if err := s.CreateAPIToken(uid, "brief", "h-brief", "full", &exp, 0); err != nil {
109 t.Fatal(err)
110 }
111 _, tok, err := s.APITokenUser("h-brief")
112 if err != nil || tok.ExpiresAt == nil || tok.Name != "brief" || tok.ID == 0 {
113 t.Fatalf("token %+v %v", tok, err)
114 }
115}