~bigbes/sr-ht-spec

ref: 6f4efc2b70da87a9dc49ce079a609b9777ee63ed sr-ht-spec/db/token_test.go -rw-r--r-- 3.5 KiB
6f4efc2b — bigbes feat(graph): the read-only GraphQL schema at /query 27 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package db

import (
	"bytes"
	"context"
	"errors"
	"testing"
)

func TestAgentTokenLifecycle(t *testing.T) {
	s, pool, cleanup := newTestStore(t)
	defer cleanup()
	ctx := context.Background()

	token, err := GenerateToken()
	if err != nil {
		t.Fatalf("generate token: %v", err)
	}
	hash := HashToken(token)

	created, err := s.CreateAgentToken(ctx, "spec-writer", hash)
	if err != nil {
		t.Fatalf("create token: %v", err)
	}
	if created.ID == 0 || !created.Active() {
		t.Fatalf("unexpected created token: %+v", created)
	}

	// The plaintext must never have reached the database.
	var found int
	if err := pool.QueryRowContext(ctx,
		`SELECT count(*) FROM agent_token WHERE encode(token_hash, 'escape') LIKE '%' || $1 || '%'`,
		token).Scan(&found); err != nil {
		t.Fatalf("scan for plaintext: %v", err)
	}
	if found != 0 {
		t.Fatal("the token plaintext is recoverable from the row")
	}

	if _, err := s.CreateAgentToken(ctx, "duplicate", hash); !errors.Is(err, ErrTokenExists) {
		t.Fatalf("duplicate token = %v, want ErrTokenExists", err)
	}

	byHash, err := s.AgentTokenByHash(ctx, hash)
	if err != nil {
		t.Fatalf("lookup by hash: %v", err)
	}
	if byHash.ID != created.ID || byHash.Name != "spec-writer" || !bytes.Equal(byHash.Hash, hash) {
		t.Fatalf("unexpected lookup: %+v", byHash)
	}
	if _, err := s.AgentTokenByHash(ctx, HashToken("nope")); !errors.Is(err, ErrNotFound) {
		t.Fatalf("unknown hash = %v, want ErrNotFound", err)
	}

	auth, err := s.AuthenticateAgentToken(ctx, token)
	if err != nil {
		t.Fatalf("authenticate: %v", err)
	}
	if auth.ID != created.ID {
		t.Fatalf("authenticated the wrong token: %+v", auth)
	}
	if _, err := s.AuthenticateAgentToken(ctx, token+"x"); !errors.Is(err, ErrNotFound) {
		t.Fatalf("wrong token = %v, want ErrNotFound", err)
	}
	if _, err := s.AuthenticateAgentToken(ctx, ""); !errors.Is(err, ErrNotFound) {
		t.Fatalf("empty token = %v, want ErrNotFound", err)
	}

	// Revocation is a stamp, so the row stays for the audit trail.
	if err := s.RevokeAgentToken(ctx, created.ID); err != nil {
		t.Fatalf("revoke: %v", err)
	}
	if _, err := s.AuthenticateAgentToken(ctx, token); !errors.Is(err, ErrTokenRevoked) {
		t.Fatalf("revoked token = %v, want ErrTokenRevoked", err)
	}
	revoked, err := s.AgentTokenByHash(ctx, hash)
	if err != nil {
		t.Fatalf("lookup revoked: %v", err)
	}
	if revoked.Active() || revoked.Revoked == nil {
		t.Fatalf("revocation did not stick: %+v", revoked)
	}
	first := *revoked.Revoked

	// Re-revoking keeps the original timestamp rather than moving it.
	if err := s.RevokeAgentToken(ctx, created.ID); err != nil {
		t.Fatalf("re-revoke: %v", err)
	}
	again, err := s.AgentTokenByHash(ctx, hash)
	if err != nil {
		t.Fatalf("lookup revoked: %v", err)
	}
	if !again.Revoked.Equal(first) {
		t.Fatalf("re-revoking moved the timestamp: %v -> %v", first, *again.Revoked)
	}

	if err := s.RevokeAgentToken(ctx, 99999); !errors.Is(err, ErrNotFound) {
		t.Fatalf("revoking a missing token = %v, want ErrNotFound", err)
	}

	second, err := GenerateToken()
	if err != nil {
		t.Fatalf("generate token: %v", err)
	}
	if _, err := s.CreateAgentToken(ctx, "notes-writer", HashToken(second)); err != nil {
		t.Fatalf("create second token: %v", err)
	}
	list, err := s.ListAgentTokens(ctx)
	if err != nil {
		t.Fatalf("list tokens: %v", err)
	}
	if len(list) != 2 || list[0].Name != "notes-writer" {
		t.Fatalf("unexpected token list (newest first): %+v", list)
	}
	if _, err := s.AuthenticateAgentToken(ctx, second); err != nil {
		t.Fatalf("authenticate second token: %v", err)
	}
}