1package approle
2
3import (
4	"context"
5	"fmt"
6	"sync"
7	"testing"
8	"time"
9
10	"github.com/hashicorp/vault/sdk/logical"
11)
12
13func TestAppRole_TidyDanglingAccessors_Normal(t *testing.T) {
14	var resp *logical.Response
15	var err error
16	b, storage := createBackendWithStorage(t)
17
18	// Create a role
19	createRole(t, b, storage, "role1", "a,b,c")
20
21	// Create a secret-id
22	roleSecretIDReq := &logical.Request{
23		Operation: logical.UpdateOperation,
24		Path:      "role/role1/secret-id",
25		Storage:   storage,
26	}
27	resp, err = b.HandleRequest(context.Background(), roleSecretIDReq)
28	if err != nil || (resp != nil && resp.IsError()) {
29		t.Fatalf("err:%v resp:%#v", err, resp)
30	}
31
32	accessorHashes, err := storage.List(context.Background(), "accessor/")
33	if err != nil {
34		t.Fatal(err)
35	}
36	if len(accessorHashes) != 1 {
37		t.Fatalf("bad: len(accessorHashes); expect 1, got %d", len(accessorHashes))
38	}
39
40	entry1, err := logical.StorageEntryJSON(
41		"accessor/invalid1",
42		&secretIDAccessorStorageEntry{
43			SecretIDHMAC: "samplesecretidhmac",
44		},
45	)
46	err = storage.Put(context.Background(), entry1)
47	if err != nil {
48		t.Fatal(err)
49	}
50
51	entry2, err := logical.StorageEntryJSON(
52		"accessor/invalid2",
53		&secretIDAccessorStorageEntry{
54			SecretIDHMAC: "samplesecretidhmac2",
55		},
56	)
57	err = storage.Put(context.Background(), entry2)
58	if err != nil {
59		t.Fatal(err)
60	}
61
62	accessorHashes, err = storage.List(context.Background(), "accessor/")
63	if err != nil {
64		t.Fatal(err)
65	}
66	if len(accessorHashes) != 3 {
67		t.Fatalf("bad: len(accessorHashes); expect 3, got %d", len(accessorHashes))
68	}
69
70	_, err = b.tidySecretID(context.Background(), &logical.Request{
71		Storage: storage,
72	})
73	if err != nil {
74		t.Fatal(err)
75	}
76
77	// It runs async so we give it a bit of time to run
78	time.Sleep(10 * time.Second)
79
80	accessorHashes, err = storage.List(context.Background(), "accessor/")
81	if err != nil {
82		t.Fatal(err)
83	}
84	if len(accessorHashes) != 1 {
85		t.Fatalf("bad: len(accessorHashes); expect 1, got %d", len(accessorHashes))
86	}
87}
88
89func TestAppRole_TidyDanglingAccessors_RaceTest(t *testing.T) {
90	var resp *logical.Response
91	var err error
92	b, storage := createBackendWithStorage(t)
93
94	b.testTidyDelay = 300 * time.Millisecond
95
96	// Create a role
97	createRole(t, b, storage, "role1", "a,b,c")
98
99	// Create an initial entry
100	roleSecretIDReq := &logical.Request{
101		Operation: logical.UpdateOperation,
102		Path:      "role/role1/secret-id",
103		Storage:   storage,
104	}
105	resp, err = b.HandleRequest(context.Background(), roleSecretIDReq)
106	if err != nil || (resp != nil && resp.IsError()) {
107		t.Fatalf("err:%v resp:%#v", err, resp)
108	}
109	count := 1
110
111	wg := &sync.WaitGroup{}
112	now := time.Now()
113	started := false
114	for {
115		if time.Now().Sub(now) > 700*time.Millisecond {
116			break
117		}
118		if time.Now().Sub(now) > 100*time.Millisecond && !started {
119			started = true
120			_, err = b.tidySecretID(context.Background(), &logical.Request{
121				Storage: storage,
122			})
123			if err != nil {
124				t.Fatal(err)
125			}
126		}
127		wg.Add(1)
128		go func() {
129			defer wg.Done()
130			roleSecretIDReq := &logical.Request{
131				Operation: logical.UpdateOperation,
132				Path:      "role/role1/secret-id",
133				Storage:   storage,
134			}
135			resp, err := b.HandleRequest(context.Background(), roleSecretIDReq)
136			if err != nil || (resp != nil && resp.IsError()) {
137				t.Fatalf("err:%v resp:%#v", err, resp)
138			}
139		}()
140		count++
141	}
142
143	t.Logf("wrote %d entries", count)
144
145	wg.Wait()
146	// Let tidy finish
147	time.Sleep(1 * time.Second)
148
149	// Run tidy again
150	_, err = b.tidySecretID(context.Background(), &logical.Request{
151		Storage: storage,
152	})
153	if err != nil {
154		t.Fatal(err)
155	}
156	time.Sleep(2 * time.Second)
157
158	accessorHashes, err := storage.List(context.Background(), "accessor/")
159	if err != nil {
160		t.Fatal(err)
161	}
162	if len(accessorHashes) != count {
163		t.Fatalf("bad: len(accessorHashes); expect %d, got %d", count, len(accessorHashes))
164	}
165
166	roleHMACs, err := storage.List(context.Background(), secretIDPrefix)
167	if err != nil {
168		t.Fatal(err)
169	}
170	secretIDs, err := storage.List(context.Background(), fmt.Sprintf("%s%s", secretIDPrefix, roleHMACs[0]))
171	if err != nil {
172		t.Fatal(err)
173	}
174	if len(secretIDs) != count {
175		t.Fatalf("bad: len(secretIDs); expect %d, got %d", count, len(secretIDs))
176	}
177}
178