1package vault
2
3import (
4	"bytes"
5	"context"
6	"errors"
7	"fmt"
8	"reflect"
9	"sort"
10	"strings"
11	"sync"
12	"sync/atomic"
13	"testing"
14	"time"
15
16	log "github.com/hashicorp/go-hclog"
17	uuid "github.com/hashicorp/go-uuid"
18	"github.com/hashicorp/vault/helper/namespace"
19	"github.com/hashicorp/vault/sdk/framework"
20	"github.com/hashicorp/vault/sdk/helper/logging"
21	"github.com/hashicorp/vault/sdk/logical"
22	"github.com/hashicorp/vault/sdk/physical"
23	"github.com/hashicorp/vault/sdk/physical/inmem"
24)
25
26var (
27	testImagePull sync.Once
28)
29
30// mockExpiration returns a mock expiration manager
31func mockExpiration(t testing.TB) *ExpirationManager {
32	c, _, _ := TestCoreUnsealed(t)
33	return c.expiration
34}
35
36func mockBackendExpiration(t testing.TB, backend physical.Backend) (*Core, *ExpirationManager) {
37	c, _, _ := TestCoreUnsealedBackend(t, backend)
38	return c, c.expiration
39}
40
41func TestExpiration_Tidy(t *testing.T) {
42	var err error
43
44	// We use this later for tidy testing where we need to check the output
45	logOut := new(bytes.Buffer)
46	logger := log.New(&log.LoggerOptions{
47		Output: logOut,
48	})
49
50	testCore := TestCore(t)
51	testCore.baseLogger = logger
52	testCore.logger = logger.Named("core")
53	testCoreUnsealed(t, testCore)
54
55	exp := testCore.expiration
56
57	if err := exp.Restore(nil); err != nil {
58		t.Fatal(err)
59	}
60
61	// Set up a count function to calculate number of leases
62	count := 0
63	countFunc := func(leaseID string) {
64		count++
65	}
66
67	// Scan the storage with the count func set
68	if err = logical.ScanView(namespace.RootContext(nil), exp.idView, countFunc); err != nil {
69		t.Fatal(err)
70	}
71
72	// Check that there are no leases to begin with
73	if count != 0 {
74		t.Fatalf("bad: lease count; expected:0 actual:%d", count)
75	}
76
77	// Create a lease entry without a client token in it
78	le := &leaseEntry{
79		LeaseID:   "lease/with/no/client/token",
80		Path:      "foo/bar",
81		namespace: namespace.RootNamespace,
82	}
83
84	// Persist the invalid lease entry
85	if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil {
86		t.Fatalf("error persisting entry: %v", err)
87	}
88
89	count = 0
90	if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil {
91		t.Fatal(err)
92	}
93
94	// Check that the storage was successful and that the count of leases is
95	// now 1
96	if count != 1 {
97		t.Fatalf("bad: lease count; expected:1 actual:%d", count)
98	}
99
100	// Run the tidy operation
101	err = exp.Tidy(namespace.RootContext(nil))
102	if err != nil {
103		t.Fatal(err)
104	}
105
106	count = 0
107	if err := logical.ScanView(context.Background(), exp.idView, countFunc); err != nil {
108		t.Fatal(err)
109	}
110
111	// Post the tidy operation, the invalid lease entry should have been gone
112	if count != 0 {
113		t.Fatalf("bad: lease count; expected:0 actual:%d", count)
114	}
115
116	// Set a revoked/invalid token in the lease entry
117	le.ClientToken = "invalidtoken"
118
119	// Persist the invalid lease entry
120	if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil {
121		t.Fatalf("error persisting entry: %v", err)
122	}
123
124	count = 0
125	if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil {
126		t.Fatal(err)
127	}
128
129	// Check that the storage was successful and that the count of leases is
130	// now 1
131	if count != 1 {
132		t.Fatalf("bad: lease count; expected:1 actual:%d", count)
133	}
134
135	// Run the tidy operation
136	err = exp.Tidy(namespace.RootContext(nil))
137	if err != nil {
138		t.Fatal(err)
139	}
140
141	count = 0
142	if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil {
143		t.Fatal(err)
144	}
145
146	// Post the tidy operation, the invalid lease entry should have been gone
147	if count != 0 {
148		t.Fatalf("bad: lease count; expected:0 actual:%d", count)
149	}
150
151	// Attach an invalid token with 2 leases
152	if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil {
153		t.Fatalf("error persisting entry: %v", err)
154	}
155
156	le.LeaseID = "another/invalid/lease"
157	if err = exp.persistEntry(context.Background(), le); err != nil {
158		t.Fatalf("error persisting entry: %v", err)
159	}
160
161	// Run the tidy operation
162	err = exp.Tidy(namespace.RootContext(nil))
163	if err != nil {
164		t.Fatal(err)
165	}
166
167	count = 0
168	if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil {
169		t.Fatal(err)
170	}
171
172	// Post the tidy operation, the invalid lease entry should have been gone
173	if count != 0 {
174		t.Fatalf("bad: lease count; expected:0 actual:%d", count)
175	}
176
177	for i := 0; i < 1000; i++ {
178		req := &logical.Request{
179			Operation:   logical.ReadOperation,
180			Path:        "invalid/lease/" + fmt.Sprintf("%d", i+1),
181			ClientToken: "invalidtoken",
182		}
183		req.SetTokenEntry(&logical.TokenEntry{ID: "invalidtoken", NamespaceID: "root"})
184		resp := &logical.Response{
185			Secret: &logical.Secret{
186				LeaseOptions: logical.LeaseOptions{
187					TTL: 100 * time.Millisecond,
188				},
189			},
190			Data: map[string]interface{}{
191				"test_key": "test_value",
192			},
193		}
194		_, err := exp.Register(namespace.RootContext(nil), req, resp)
195		if err != nil {
196			t.Fatalf("err: %v", err)
197		}
198	}
199
200	count = 0
201	if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil {
202		t.Fatal(err)
203	}
204
205	// Check that there are 1000 leases now
206	if count != 1000 {
207		t.Fatalf("bad: lease count; expected:1000 actual:%d", count)
208	}
209
210	errCh1 := make(chan error)
211	errCh2 := make(chan error)
212
213	// Initiate tidy of the above 1000 invalid leases in quick succession. Only
214	// one tidy operation can be in flight at any time. One of these requests
215	// should error out.
216	go func() {
217		errCh1 <- exp.Tidy(namespace.RootContext(nil))
218	}()
219
220	go func() {
221		errCh2 <- exp.Tidy(namespace.RootContext(nil))
222	}()
223
224	var err1, err2 error
225
226	for i := 0; i < 2; i++ {
227		select {
228		case err1 = <-errCh1:
229		case err2 = <-errCh2:
230		}
231	}
232
233	if err1 != nil || err2 != nil {
234		t.Fatalf("got an error: err1: %v; err2: %v", err1, err2)
235	}
236	if !strings.Contains(logOut.String(), "tidy operation on leases is already in progress") {
237		t.Fatalf("expected to see a warning saying operation in progress, output is %s", logOut.String())
238	}
239
240	root, err := exp.tokenStore.rootToken(context.Background())
241	if err != nil {
242		t.Fatal(err)
243	}
244	le.ClientToken = root.ID
245
246	// Attach a valid token with the leases
247	if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil {
248		t.Fatalf("error persisting entry: %v", err)
249	}
250
251	// Run the tidy operation
252	err = exp.Tidy(namespace.RootContext(nil))
253	if err != nil {
254		t.Fatal(err)
255	}
256
257	count = 0
258	if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil {
259		t.Fatal(err)
260	}
261
262	// Post the tidy operation, the valid lease entry should not get affected
263	if count != 1 {
264		t.Fatalf("bad: lease count; expected:1 actual:%d", count)
265	}
266}
267
268// To avoid pulling in deps for all users of the package, don't leave these
269// uncommented in the public tree
270/*
271func BenchmarkExpiration_Restore_Etcd(b *testing.B) {
272	addr := os.Getenv("PHYSICAL_BACKEND_BENCHMARK_ADDR")
273	randPath := fmt.Sprintf("vault-%d/", time.Now().Unix())
274
275	logger := logging.NewVaultLogger(log.Trace)
276	physicalBackend, err := physEtcd.NewEtcdBackend(map[string]string{
277		"address":      addr,
278		"path":         randPath,
279		"max_parallel": "256",
280	}, logger)
281	if err != nil {
282		b.Fatalf("err: %s", err)
283	}
284
285	benchmarkExpirationBackend(b, physicalBackend, 10000) // 10,000 leases
286}
287
288func BenchmarkExpiration_Restore_Consul(b *testing.B) {
289	addr := os.Getenv("PHYSICAL_BACKEND_BENCHMARK_ADDR")
290	randPath := fmt.Sprintf("vault-%d/", time.Now().Unix())
291
292	logger := logging.NewVaultLogger(log.Trace)
293	physicalBackend, err := physConsul.NewConsulBackend(map[string]string{
294		"address":      addr,
295		"path":         randPath,
296		"max_parallel": "256",
297	}, logger)
298	if err != nil {
299		b.Fatalf("err: %s", err)
300	}
301
302	benchmarkExpirationBackend(b, physicalBackend, 10000) // 10,000 leases
303}
304*/
305
306func BenchmarkExpiration_Restore_InMem(b *testing.B) {
307	logger := logging.NewVaultLogger(log.Trace)
308	inm, err := inmem.NewInmem(nil, logger)
309	if err != nil {
310		b.Fatal(err)
311	}
312	benchmarkExpirationBackend(b, inm, 100000) // 100,000 Leases
313}
314
315func benchmarkExpirationBackend(b *testing.B, physicalBackend physical.Backend, numLeases int) {
316	c, _, _ := TestCoreUnsealedBackend(b, physicalBackend)
317	exp := c.expiration
318	noop := &NoopBackend{}
319	view := NewBarrierView(c.barrier, "logical/")
320	meUUID, err := uuid.GenerateUUID()
321	if err != nil {
322		b.Fatal(err)
323	}
324	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
325	if err != nil {
326		b.Fatal(err)
327	}
328
329	// Register fake leases
330	for i := 0; i < numLeases; i++ {
331		pathUUID, err := uuid.GenerateUUID()
332		if err != nil {
333			b.Fatal(err)
334		}
335
336		req := &logical.Request{
337			Operation:   logical.ReadOperation,
338			Path:        "prod/aws/" + pathUUID,
339			ClientToken: "root",
340		}
341		resp := &logical.Response{
342			Secret: &logical.Secret{
343				LeaseOptions: logical.LeaseOptions{
344					TTL: 400 * time.Second,
345				},
346			},
347			Data: map[string]interface{}{
348				"access_key": "xyz",
349				"secret_key": "abcd",
350			},
351		}
352		_, err = exp.Register(context.Background(), req, resp)
353		if err != nil {
354			b.Fatalf("err: %v", err)
355		}
356	}
357
358	// Stop everything
359	err = exp.Stop()
360	if err != nil {
361		b.Fatalf("err: %v", err)
362	}
363
364	b.ResetTimer()
365	for i := 0; i < b.N; i++ {
366		err = exp.Restore(nil)
367		// Restore
368		if err != nil {
369			b.Fatalf("err: %v", err)
370		}
371	}
372	b.StopTimer()
373}
374
375func TestExpiration_Restore(t *testing.T) {
376	exp := mockExpiration(t)
377	noop := &NoopBackend{}
378	_, barrier, _ := mockBarrier(t)
379	view := NewBarrierView(barrier, "logical/")
380	meUUID, err := uuid.GenerateUUID()
381	if err != nil {
382		t.Fatal(err)
383	}
384	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
385	if err != nil {
386		t.Fatal(err)
387	}
388
389	paths := []string{
390		"prod/aws/foo",
391		"prod/aws/sub/bar",
392		"prod/aws/zip",
393	}
394	for _, path := range paths {
395		req := &logical.Request{
396			Operation:   logical.ReadOperation,
397			Path:        path,
398			ClientToken: "foobar",
399		}
400		req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
401		resp := &logical.Response{
402			Secret: &logical.Secret{
403				LeaseOptions: logical.LeaseOptions{
404					TTL: 20 * time.Millisecond,
405				},
406			},
407			Data: map[string]interface{}{
408				"access_key": "xyz",
409				"secret_key": "abcd",
410			},
411		}
412		_, err := exp.Register(namespace.RootContext(nil), req, resp)
413		if err != nil {
414			t.Fatalf("err: %v", err)
415		}
416	}
417
418	// Stop everything
419	err = exp.Stop()
420	if err != nil {
421		t.Fatalf("err: %v", err)
422	}
423
424	// Restore
425	err = exp.Restore(nil)
426	if err != nil {
427		t.Fatalf("err: %v", err)
428	}
429
430	// Ensure all are reaped
431	start := time.Now()
432	for time.Now().Sub(start) < time.Second {
433		noop.Lock()
434		less := len(noop.Requests) < 3
435		noop.Unlock()
436
437		if less {
438			time.Sleep(5 * time.Millisecond)
439			continue
440		}
441		break
442	}
443	for _, req := range noop.Requests {
444		if req.Operation != logical.RevokeOperation {
445			t.Fatalf("Bad: %v", req)
446		}
447	}
448}
449
450func TestExpiration_Register(t *testing.T) {
451	exp := mockExpiration(t)
452	req := &logical.Request{
453		Operation:   logical.ReadOperation,
454		Path:        "prod/aws/foo",
455		ClientToken: "foobar",
456	}
457	req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
458	resp := &logical.Response{
459		Secret: &logical.Secret{
460			LeaseOptions: logical.LeaseOptions{
461				TTL: time.Hour,
462			},
463		},
464		Data: map[string]interface{}{
465			"access_key": "xyz",
466			"secret_key": "abcd",
467		},
468	}
469
470	id, err := exp.Register(namespace.RootContext(nil), req, resp)
471	if err != nil {
472		t.Fatalf("err: %v", err)
473	}
474
475	if !strings.HasPrefix(id, req.Path) {
476		t.Fatalf("bad: %s", id)
477	}
478
479	if len(id) <= len(req.Path) {
480		t.Fatalf("bad: %s", id)
481	}
482}
483
484func TestExpiration_RegisterAuth(t *testing.T) {
485	exp := mockExpiration(t)
486	root, err := exp.tokenStore.rootToken(context.Background())
487	if err != nil {
488		t.Fatalf("err: %v", err)
489	}
490
491	auth := &logical.Auth{
492		ClientToken: root.ID,
493		LeaseOptions: logical.LeaseOptions{
494			TTL: time.Hour,
495		},
496	}
497
498	te := &logical.TokenEntry{
499		Path:        "auth/github/login",
500		NamespaceID: namespace.RootNamespaceID,
501	}
502	err = exp.RegisterAuth(namespace.RootContext(nil), te, auth)
503	if err != nil {
504		t.Fatalf("err: %v", err)
505	}
506
507	te = &logical.TokenEntry{
508		Path:        "auth/github/../login",
509		NamespaceID: namespace.RootNamespaceID,
510	}
511	err = exp.RegisterAuth(namespace.RootContext(nil), te, auth)
512	if err == nil {
513		t.Fatal("expected error")
514	}
515}
516
517func TestExpiration_RegisterAuth_NoLease(t *testing.T) {
518	exp := mockExpiration(t)
519	root, err := exp.tokenStore.rootToken(context.Background())
520	if err != nil {
521		t.Fatalf("err: %v", err)
522	}
523
524	auth := &logical.Auth{
525		ClientToken: root.ID,
526	}
527
528	te := &logical.TokenEntry{
529		ID:          root.ID,
530		Path:        "auth/github/login",
531		NamespaceID: namespace.RootNamespaceID,
532	}
533	err = exp.RegisterAuth(namespace.RootContext(nil), te, auth)
534	if err != nil {
535		t.Fatalf("err: %v", err)
536	}
537
538	// Should not be able to renew, no expiration
539	te = &logical.TokenEntry{
540		ID:          root.ID,
541		Path:        "auth/github/login",
542		NamespaceID: namespace.RootNamespaceID,
543	}
544	resp, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0)
545	if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) {
546		t.Fatalf("bad: err:%v resp:%#v", err, resp)
547	}
548	if resp == nil {
549		t.Fatal("expected a response")
550	}
551
552	// Wait and check token is not invalidated
553	time.Sleep(20 * time.Millisecond)
554
555	// Verify token does not get revoked
556	out, err := exp.tokenStore.Lookup(namespace.RootContext(nil), root.ID)
557	if err != nil {
558		t.Fatalf("err: %v", err)
559	}
560	if out == nil {
561		t.Fatalf("missing token")
562	}
563}
564
565func TestExpiration_Revoke(t *testing.T) {
566	exp := mockExpiration(t)
567	noop := &NoopBackend{}
568	_, barrier, _ := mockBarrier(t)
569	view := NewBarrierView(barrier, "logical/")
570	meUUID, err := uuid.GenerateUUID()
571	if err != nil {
572		t.Fatal(err)
573	}
574	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
575	if err != nil {
576		t.Fatal(err)
577	}
578
579	req := &logical.Request{
580		Operation:   logical.ReadOperation,
581		Path:        "prod/aws/foo",
582		ClientToken: "foobar",
583	}
584	req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
585	resp := &logical.Response{
586		Secret: &logical.Secret{
587			LeaseOptions: logical.LeaseOptions{
588				TTL: time.Hour,
589			},
590		},
591		Data: map[string]interface{}{
592			"access_key": "xyz",
593			"secret_key": "abcd",
594		},
595	}
596
597	id, err := exp.Register(namespace.RootContext(nil), req, resp)
598	if err != nil {
599		t.Fatalf("err: %v", err)
600	}
601
602	if err := exp.Revoke(namespace.RootContext(nil), id); err != nil {
603		t.Fatalf("err: %v", err)
604	}
605
606	req = noop.Requests[0]
607	if req.Operation != logical.RevokeOperation {
608		t.Fatalf("Bad: %v", req)
609	}
610}
611
612func TestExpiration_RevokeOnExpire(t *testing.T) {
613	exp := mockExpiration(t)
614	noop := &NoopBackend{}
615	_, barrier, _ := mockBarrier(t)
616	view := NewBarrierView(barrier, "logical/")
617	meUUID, err := uuid.GenerateUUID()
618	if err != nil {
619		t.Fatal(err)
620	}
621	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
622	if err != nil {
623		t.Fatal(err)
624	}
625
626	req := &logical.Request{
627		Operation:   logical.ReadOperation,
628		Path:        "prod/aws/foo",
629		ClientToken: "foobar",
630	}
631	req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
632	resp := &logical.Response{
633		Secret: &logical.Secret{
634			LeaseOptions: logical.LeaseOptions{
635				TTL: 20 * time.Millisecond,
636			},
637		},
638		Data: map[string]interface{}{
639			"access_key": "xyz",
640			"secret_key": "abcd",
641		},
642	}
643
644	_, err = exp.Register(namespace.RootContext(nil), req, resp)
645	if err != nil {
646		t.Fatalf("err: %v", err)
647	}
648
649	start := time.Now()
650	for time.Now().Sub(start) < time.Second {
651		req = nil
652
653		noop.Lock()
654		if len(noop.Requests) > 0 {
655			req = noop.Requests[0]
656		}
657		noop.Unlock()
658		if req == nil {
659			time.Sleep(5 * time.Millisecond)
660			continue
661		}
662		if req.Operation != logical.RevokeOperation {
663			t.Fatalf("Bad: %v", req)
664		}
665
666		break
667	}
668}
669
670func TestExpiration_RevokePrefix(t *testing.T) {
671	exp := mockExpiration(t)
672	noop := &NoopBackend{}
673	_, barrier, _ := mockBarrier(t)
674	view := NewBarrierView(barrier, "logical/")
675	meUUID, err := uuid.GenerateUUID()
676	if err != nil {
677		t.Fatal(err)
678	}
679	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
680	if err != nil {
681		t.Fatal(err)
682	}
683
684	paths := []string{
685		"prod/aws/foo",
686		"prod/aws/sub/bar",
687		"prod/aws/zip",
688	}
689	for _, path := range paths {
690		req := &logical.Request{
691			Operation:   logical.ReadOperation,
692			Path:        path,
693			ClientToken: "foobar",
694		}
695		req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
696		resp := &logical.Response{
697			Secret: &logical.Secret{
698				LeaseOptions: logical.LeaseOptions{
699					TTL: 20 * time.Millisecond,
700				},
701			},
702			Data: map[string]interface{}{
703				"access_key": "xyz",
704				"secret_key": "abcd",
705			},
706		}
707		_, err := exp.Register(namespace.RootContext(nil), req, resp)
708		if err != nil {
709			t.Fatalf("err: %v", err)
710		}
711	}
712
713	// Should nuke all the keys
714	if err := exp.RevokePrefix(namespace.RootContext(nil), "prod/aws/", true); err != nil {
715		t.Fatalf("err: %v", err)
716	}
717
718	if len(noop.Requests) != 3 {
719		t.Fatalf("Bad: %v", noop.Requests)
720	}
721	for _, req := range noop.Requests {
722		if req.Operation != logical.RevokeOperation {
723			t.Fatalf("Bad: %v", req)
724		}
725	}
726
727	expect := []string{
728		"foo",
729		"sub/bar",
730		"zip",
731	}
732	sort.Strings(noop.Paths)
733	sort.Strings(expect)
734	if !reflect.DeepEqual(noop.Paths, expect) {
735		t.Fatalf("bad: %v", noop.Paths)
736	}
737}
738
739func TestExpiration_RevokeByToken(t *testing.T) {
740	exp := mockExpiration(t)
741	noop := &NoopBackend{}
742	_, barrier, _ := mockBarrier(t)
743	view := NewBarrierView(barrier, "logical/")
744	meUUID, err := uuid.GenerateUUID()
745	if err != nil {
746		t.Fatal(err)
747	}
748	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
749	if err != nil {
750		t.Fatal(err)
751	}
752
753	paths := []string{
754		"prod/aws/foo",
755		"prod/aws/sub/bar",
756		"prod/aws/zip",
757	}
758	for _, path := range paths {
759		req := &logical.Request{
760			Operation:   logical.ReadOperation,
761			Path:        path,
762			ClientToken: "foobarbaz",
763		}
764		req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"})
765		resp := &logical.Response{
766			Secret: &logical.Secret{
767				LeaseOptions: logical.LeaseOptions{
768					TTL: 20 * time.Millisecond,
769				},
770			},
771			Data: map[string]interface{}{
772				"access_key": "xyz",
773				"secret_key": "abcd",
774			},
775		}
776		_, err := exp.Register(namespace.RootContext(nil), req, resp)
777		if err != nil {
778			t.Fatalf("err: %v", err)
779		}
780	}
781
782	// Should nuke all the keys
783	te := &logical.TokenEntry{
784		ID:          "foobarbaz",
785		NamespaceID: namespace.RootNamespaceID,
786	}
787	if err := exp.RevokeByToken(namespace.RootContext(nil), te); err != nil {
788		t.Fatalf("err: %v", err)
789	}
790
791	time.Sleep(300 * time.Millisecond)
792
793	noop.Lock()
794	defer noop.Unlock()
795
796	if len(noop.Requests) != 3 {
797		t.Fatalf("Bad: %v", noop.Requests)
798	}
799	for _, req := range noop.Requests {
800		if req.Operation != logical.RevokeOperation {
801			t.Fatalf("Bad: %v", req)
802		}
803	}
804
805	expect := []string{
806		"foo",
807		"sub/bar",
808		"zip",
809	}
810	sort.Strings(noop.Paths)
811	sort.Strings(expect)
812	if !reflect.DeepEqual(noop.Paths, expect) {
813		t.Fatalf("bad: %v", noop.Paths)
814	}
815}
816
817func TestExpiration_RevokeByToken_Blocking(t *testing.T) {
818	exp := mockExpiration(t)
819	noop := &NoopBackend{}
820	// Request handle with a timeout context that simulates blocking lease revocation.
821	noop.RequestHandler = func(ctx context.Context, req *logical.Request) (*logical.Response, error) {
822		ctx, cancel := context.WithTimeout(ctx, 200*time.Millisecond)
823		defer cancel()
824
825		select {
826		case <-ctx.Done():
827			return noop.Response, nil
828		}
829	}
830
831	_, barrier, _ := mockBarrier(t)
832	view := NewBarrierView(barrier, "logical/")
833	meUUID, err := uuid.GenerateUUID()
834	if err != nil {
835		t.Fatal(err)
836	}
837	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
838	if err != nil {
839		t.Fatal(err)
840	}
841
842	paths := []string{
843		"prod/aws/foo",
844		"prod/aws/sub/bar",
845		"prod/aws/zip",
846	}
847	for _, path := range paths {
848		req := &logical.Request{
849			Operation:   logical.ReadOperation,
850			Path:        path,
851			ClientToken: "foobarbaz",
852		}
853		req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"})
854		resp := &logical.Response{
855			Secret: &logical.Secret{
856				LeaseOptions: logical.LeaseOptions{
857					TTL: 1 * time.Minute,
858				},
859			},
860			Data: map[string]interface{}{
861				"access_key": "xyz",
862				"secret_key": "abcd",
863			},
864		}
865		_, err := exp.Register(namespace.RootContext(nil), req, resp)
866		if err != nil {
867			t.Fatalf("err: %v", err)
868		}
869	}
870
871	// Should nuke all the keys
872	te := &logical.TokenEntry{
873		ID:          "foobarbaz",
874		NamespaceID: namespace.RootNamespaceID,
875	}
876	if err := exp.RevokeByToken(namespace.RootContext(nil), te); err != nil {
877		t.Fatalf("err: %v", err)
878	}
879
880	// Lock and check that no requests has gone through yet
881	noop.Lock()
882	if len(noop.Requests) != 0 {
883		t.Fatalf("Bad: %v", noop.Requests)
884	}
885	noop.Unlock()
886
887	// Wait for a bit for timeouts to trigger and pending revocations to go
888	// through and then we relock
889	time.Sleep(300 * time.Millisecond)
890
891	noop.Lock()
892	defer noop.Unlock()
893
894	// Now make sure that all requests have gone through
895	if len(noop.Requests) != 3 {
896		t.Fatalf("Bad: %v", noop.Requests)
897	}
898	for _, req := range noop.Requests {
899		if req.Operation != logical.RevokeOperation {
900			t.Fatalf("Bad: %v", req)
901		}
902	}
903
904	expect := []string{
905		"foo",
906		"sub/bar",
907		"zip",
908	}
909	sort.Strings(noop.Paths)
910	sort.Strings(expect)
911	if !reflect.DeepEqual(noop.Paths, expect) {
912		t.Fatalf("bad: %v", noop.Paths)
913	}
914}
915
916func TestExpiration_RenewToken(t *testing.T) {
917	exp := mockExpiration(t)
918	root, err := exp.tokenStore.rootToken(context.Background())
919	if err != nil {
920		t.Fatalf("err: %v", err)
921	}
922
923	// Register a token
924	auth := &logical.Auth{
925		ClientToken: root.ID,
926		LeaseOptions: logical.LeaseOptions{
927			TTL:       time.Hour,
928			Renewable: true,
929		},
930	}
931
932	te := &logical.TokenEntry{
933		ID:          root.ID,
934		Path:        "auth/token/login",
935		NamespaceID: namespace.RootNamespaceID,
936	}
937	err = exp.RegisterAuth(namespace.RootContext(nil), te, auth)
938	if err != nil {
939		t.Fatalf("err: %v", err)
940	}
941
942	// Renew the token
943	te = &logical.TokenEntry{
944		ID:          root.ID,
945		Path:        "auth/token/login",
946		NamespaceID: namespace.RootNamespaceID,
947	}
948	out, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0)
949	if err != nil {
950		t.Fatalf("err: %v", err)
951	}
952
953	if auth.ClientToken != out.Auth.ClientToken {
954		t.Fatalf("bad: %#v", out)
955	}
956}
957
958func TestExpiration_RenewToken_period(t *testing.T) {
959	exp := mockExpiration(t)
960	root := &logical.TokenEntry{
961		Policies:     []string{"root"},
962		Path:         "auth/token/root",
963		DisplayName:  "root",
964		CreationTime: time.Now().Unix(),
965		Period:       time.Minute,
966		NamespaceID:  namespace.RootNamespaceID,
967	}
968	if err := exp.tokenStore.create(namespace.RootContext(nil), root); err != nil {
969		t.Fatalf("err: %v", err)
970	}
971
972	// Register a token
973	auth := &logical.Auth{
974		ClientToken: root.ID,
975		LeaseOptions: logical.LeaseOptions{
976			TTL:       time.Hour,
977			Renewable: true,
978		},
979		Period: time.Minute,
980	}
981	te := &logical.TokenEntry{
982		ID:          root.ID,
983		Path:        "auth/token/login",
984		NamespaceID: namespace.RootNamespaceID,
985	}
986	err := exp.RegisterAuth(namespace.RootContext(nil), te, auth)
987	if err != nil {
988		t.Fatalf("err: %v", err)
989	}
990
991	// Renew the token
992	te = &logical.TokenEntry{
993		ID:          root.ID,
994		Path:        "auth/token/login",
995		NamespaceID: namespace.RootNamespaceID,
996	}
997	out, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0)
998	if err != nil {
999		t.Fatalf("err: %v", err)
1000	}
1001
1002	if auth.ClientToken != out.Auth.ClientToken {
1003		t.Fatalf("bad: %#v", out)
1004	}
1005
1006	if out.Auth.TTL > time.Minute {
1007		t.Fatalf("expected TTL to be less than 1 minute, got: %s", out.Auth.TTL)
1008	}
1009}
1010
1011func TestExpiration_RenewToken_period_backend(t *testing.T) {
1012	exp := mockExpiration(t)
1013	root, err := exp.tokenStore.rootToken(context.Background())
1014	if err != nil {
1015		t.Fatalf("err: %v", err)
1016	}
1017
1018	// Mount a noop backend
1019	noop := &NoopBackend{
1020		Response: &logical.Response{
1021			Auth: &logical.Auth{
1022				LeaseOptions: logical.LeaseOptions{
1023					TTL:       10 * time.Second,
1024					Renewable: true,
1025				},
1026				Period: 5 * time.Second,
1027			},
1028		},
1029		DefaultLeaseTTL: 5 * time.Second,
1030		MaxLeaseTTL:     5 * time.Second,
1031	}
1032
1033	_, barrier, _ := mockBarrier(t)
1034	view := NewBarrierView(barrier, credentialBarrierPrefix)
1035	meUUID, err := uuid.GenerateUUID()
1036	if err != nil {
1037		t.Fatal(err)
1038	}
1039	err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1040	if err != nil {
1041		t.Fatal(err)
1042	}
1043
1044	// Register a token
1045	auth := &logical.Auth{
1046		ClientToken: root.ID,
1047		LeaseOptions: logical.LeaseOptions{
1048			TTL:       10 * time.Second,
1049			Renewable: true,
1050		},
1051		Period: 5 * time.Second,
1052	}
1053	te := &logical.TokenEntry{
1054		ID:          root.ID,
1055		Path:        "auth/foo/login",
1056		NamespaceID: namespace.RootNamespaceID,
1057	}
1058
1059	err = exp.RegisterAuth(namespace.RootContext(nil), te, auth)
1060	if err != nil {
1061		t.Fatalf("err: %v", err)
1062	}
1063
1064	// Wait 3 seconds
1065	time.Sleep(3 * time.Second)
1066	te = &logical.TokenEntry{
1067		ID:          root.ID,
1068		Path:        "auth/foo/login",
1069		NamespaceID: namespace.RootNamespaceID,
1070	}
1071	resp, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0)
1072	if err != nil {
1073		t.Fatalf("err: %v", err)
1074	}
1075	if resp == nil {
1076		t.Fatal("expected a response")
1077	}
1078	if resp.Auth.TTL == 0 || resp.Auth.TTL > 5*time.Second {
1079		t.Fatalf("expected TTL to be greater than zero and less than or equal to period, got: %s", resp.Auth.TTL)
1080	}
1081
1082	// Wait another 3 seconds. If period works correctly, this should not fail
1083	time.Sleep(3 * time.Second)
1084	resp, err = exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0)
1085	if err != nil {
1086		t.Fatalf("err: %v", err)
1087	}
1088	if resp == nil {
1089		t.Fatal("expected a response")
1090	}
1091	if resp.Auth.TTL < 4*time.Second || resp.Auth.TTL > 5*time.Second {
1092		t.Fatalf("expected TTL to be around period's value, got: %s", resp.Auth.TTL)
1093	}
1094}
1095
1096func TestExpiration_RenewToken_NotRenewable(t *testing.T) {
1097	exp := mockExpiration(t)
1098	root, err := exp.tokenStore.rootToken(context.Background())
1099	if err != nil {
1100		t.Fatalf("err: %v", err)
1101	}
1102
1103	// Register a token
1104	auth := &logical.Auth{
1105		ClientToken: root.ID,
1106		LeaseOptions: logical.LeaseOptions{
1107			TTL:       time.Hour,
1108			Renewable: false,
1109		},
1110	}
1111	te := &logical.TokenEntry{
1112		ID:          root.ID,
1113		Path:        "auth/foo/login",
1114		NamespaceID: namespace.RootNamespaceID,
1115	}
1116	err = exp.RegisterAuth(namespace.RootContext(nil), te, auth)
1117	if err != nil {
1118		t.Fatalf("err: %v", err)
1119	}
1120
1121	// Attempt to renew the token
1122	te = &logical.TokenEntry{
1123		ID:          root.ID,
1124		Path:        "auth/github/login",
1125		NamespaceID: namespace.RootNamespaceID,
1126	}
1127	resp, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0)
1128	if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "invalid lease ID")) {
1129		t.Fatalf("bad: err:%v resp:%#v", err, resp)
1130	}
1131	if resp == nil {
1132		t.Fatal("expected a response")
1133	}
1134
1135}
1136
1137func TestExpiration_Renew(t *testing.T) {
1138	exp := mockExpiration(t)
1139	noop := &NoopBackend{}
1140	_, barrier, _ := mockBarrier(t)
1141	view := NewBarrierView(barrier, "logical/")
1142	meUUID, err := uuid.GenerateUUID()
1143	if err != nil {
1144		t.Fatal(err)
1145	}
1146	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1147	if err != nil {
1148		t.Fatal(err)
1149	}
1150
1151	req := &logical.Request{
1152		Operation:   logical.ReadOperation,
1153		Path:        "prod/aws/foo",
1154		ClientToken: "foobar",
1155	}
1156	req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
1157	resp := &logical.Response{
1158		Secret: &logical.Secret{
1159			LeaseOptions: logical.LeaseOptions{
1160				TTL:       20 * time.Millisecond,
1161				Renewable: true,
1162			},
1163		},
1164		Data: map[string]interface{}{
1165			"access_key": "xyz",
1166			"secret_key": "abcd",
1167		},
1168	}
1169
1170	id, err := exp.Register(namespace.RootContext(nil), req, resp)
1171	if err != nil {
1172		t.Fatalf("err: %v", err)
1173	}
1174
1175	noop.Response = &logical.Response{
1176		Secret: &logical.Secret{
1177			LeaseOptions: logical.LeaseOptions{
1178				TTL: 20 * time.Millisecond,
1179			},
1180		},
1181		Data: map[string]interface{}{
1182			"access_key": "123",
1183			"secret_key": "abcd",
1184		},
1185	}
1186
1187	out, err := exp.Renew(namespace.RootContext(nil), id, 0)
1188	if err != nil {
1189		t.Fatalf("err: %v", err)
1190	}
1191
1192	noop.Lock()
1193	defer noop.Unlock()
1194
1195	if !reflect.DeepEqual(out, noop.Response) {
1196		t.Fatalf("Bad: %#v", out)
1197	}
1198
1199	if len(noop.Requests) != 1 {
1200		t.Fatalf("Bad: %#v", noop.Requests)
1201	}
1202	req = noop.Requests[0]
1203	if req.Operation != logical.RenewOperation {
1204		t.Fatalf("Bad: %v", req)
1205	}
1206}
1207
1208func TestExpiration_Renew_NotRenewable(t *testing.T) {
1209	exp := mockExpiration(t)
1210	noop := &NoopBackend{}
1211	_, barrier, _ := mockBarrier(t)
1212	view := NewBarrierView(barrier, "logical/")
1213	meUUID, err := uuid.GenerateUUID()
1214	if err != nil {
1215		t.Fatal(err)
1216	}
1217	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1218	if err != nil {
1219		t.Fatal(err)
1220	}
1221
1222	req := &logical.Request{
1223		Operation:   logical.ReadOperation,
1224		Path:        "prod/aws/foo",
1225		ClientToken: "foobar",
1226	}
1227	req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
1228	resp := &logical.Response{
1229		Secret: &logical.Secret{
1230			LeaseOptions: logical.LeaseOptions{
1231				TTL:       20 * time.Millisecond,
1232				Renewable: false,
1233			},
1234		},
1235		Data: map[string]interface{}{
1236			"access_key": "xyz",
1237			"secret_key": "abcd",
1238		},
1239	}
1240
1241	id, err := exp.Register(namespace.RootContext(nil), req, resp)
1242	if err != nil {
1243		t.Fatalf("err: %v", err)
1244	}
1245
1246	_, err = exp.Renew(namespace.RootContext(nil), id, 0)
1247	if err.Error() != "lease is not renewable" {
1248		t.Fatalf("err: %v", err)
1249	}
1250
1251	noop.Lock()
1252	defer noop.Unlock()
1253
1254	if len(noop.Requests) != 0 {
1255		t.Fatalf("Bad: %#v", noop.Requests)
1256	}
1257}
1258
1259func TestExpiration_Renew_RevokeOnExpire(t *testing.T) {
1260	exp := mockExpiration(t)
1261	noop := &NoopBackend{}
1262	_, barrier, _ := mockBarrier(t)
1263	view := NewBarrierView(barrier, "logical/")
1264	meUUID, err := uuid.GenerateUUID()
1265	if err != nil {
1266		t.Fatal(err)
1267	}
1268	err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1269	if err != nil {
1270		t.Fatal(err)
1271	}
1272
1273	req := &logical.Request{
1274		Operation:   logical.ReadOperation,
1275		Path:        "prod/aws/foo",
1276		ClientToken: "foobar",
1277	}
1278	req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
1279	resp := &logical.Response{
1280		Secret: &logical.Secret{
1281			LeaseOptions: logical.LeaseOptions{
1282				TTL:       20 * time.Millisecond,
1283				Renewable: true,
1284			},
1285		},
1286		Data: map[string]interface{}{
1287			"access_key": "xyz",
1288			"secret_key": "abcd",
1289		},
1290	}
1291
1292	id, err := exp.Register(namespace.RootContext(nil), req, resp)
1293	if err != nil {
1294		t.Fatalf("err: %v", err)
1295	}
1296
1297	noop.Response = &logical.Response{
1298		Secret: &logical.Secret{
1299			LeaseOptions: logical.LeaseOptions{
1300				TTL: 20 * time.Millisecond,
1301			},
1302		},
1303		Data: map[string]interface{}{
1304			"access_key": "123",
1305			"secret_key": "abcd",
1306		},
1307	}
1308
1309	_, err = exp.Renew(namespace.RootContext(nil), id, 0)
1310	if err != nil {
1311		t.Fatalf("err: %v", err)
1312	}
1313
1314	start := time.Now()
1315	for time.Now().Sub(start) < time.Second {
1316		req = nil
1317
1318		noop.Lock()
1319		if len(noop.Requests) >= 2 {
1320			req = noop.Requests[1]
1321		}
1322		noop.Unlock()
1323
1324		if req == nil {
1325			time.Sleep(5 * time.Millisecond)
1326			continue
1327		}
1328		if req.Operation != logical.RevokeOperation {
1329			t.Fatalf("Bad: %v", req)
1330		}
1331		break
1332	}
1333}
1334
1335func TestExpiration_revokeEntry(t *testing.T) {
1336	exp := mockExpiration(t)
1337
1338	noop := &NoopBackend{}
1339	_, barrier, _ := mockBarrier(t)
1340	view := NewBarrierView(barrier, "logical/")
1341	meUUID, err := uuid.GenerateUUID()
1342	if err != nil {
1343		t.Fatal(err)
1344	}
1345	err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1346	if err != nil {
1347		t.Fatal(err)
1348	}
1349
1350	le := &leaseEntry{
1351		LeaseID: "foo/bar/1234",
1352		Path:    "foo/bar",
1353		Data: map[string]interface{}{
1354			"testing": true,
1355		},
1356		Secret: &logical.Secret{
1357			LeaseOptions: logical.LeaseOptions{
1358				TTL: time.Minute,
1359			},
1360		},
1361		IssueTime:  time.Now(),
1362		ExpireTime: time.Now(),
1363		namespace:  namespace.RootNamespace,
1364	}
1365
1366	err = exp.revokeEntry(namespace.RootContext(nil), le)
1367	if err != nil {
1368		t.Fatalf("err: %v", err)
1369	}
1370
1371	noop.Lock()
1372	defer noop.Unlock()
1373
1374	req := noop.Requests[0]
1375	if req.Operation != logical.RevokeOperation {
1376		t.Fatalf("bad: operation; req: %#v", req)
1377	}
1378	if !reflect.DeepEqual(req.Data, le.Data) {
1379		t.Fatalf("bad: data; req: %#v\n le: %#v\n", req, le)
1380	}
1381}
1382
1383func TestExpiration_revokeEntry_token(t *testing.T) {
1384	exp := mockExpiration(t)
1385	root, err := exp.tokenStore.rootToken(context.Background())
1386	if err != nil {
1387		t.Fatalf("err: %v", err)
1388	}
1389
1390	// N.B.: Vault doesn't allow both a secret and auth to be returned, but the
1391	// reason for both is that auth needs to be included in order to use the
1392	// token store as it's the only mounted backend, *but* RegisterAuth doesn't
1393	// actually create the index by token, only Register (for a Secret) does.
1394	// So without the Secret we don't do anything when removing the index which
1395	// (at the time of writing) now fails because a bug causing every token
1396	// expiration to do an extra delete to a non-existent key has been fixed,
1397	// and this test relies on this nonstandard behavior.
1398	le := &leaseEntry{
1399		LeaseID: "foo/bar/1234",
1400		Auth: &logical.Auth{
1401			ClientToken: root.ID,
1402			LeaseOptions: logical.LeaseOptions{
1403				TTL: time.Minute,
1404			},
1405		},
1406		Secret: &logical.Secret{
1407			LeaseOptions: logical.LeaseOptions{
1408				TTL: time.Minute,
1409			},
1410		},
1411		ClientToken: root.ID,
1412		Path:        "foo/bar",
1413		IssueTime:   time.Now(),
1414		ExpireTime:  time.Now(),
1415		namespace:   namespace.RootNamespace,
1416	}
1417
1418	if err := exp.persistEntry(namespace.RootContext(nil), le); err != nil {
1419		t.Fatalf("error persisting entry: %v", err)
1420	}
1421	if err := exp.createIndexByToken(namespace.RootContext(nil), le, le.ClientToken); err != nil {
1422		t.Fatalf("error creating secondary index: %v", err)
1423	}
1424	exp.updatePending(le, le.Secret.LeaseTotal())
1425
1426	indexEntry, err := exp.indexByToken(namespace.RootContext(nil), le)
1427	if err != nil {
1428		t.Fatalf("err: %v", err)
1429	}
1430	if indexEntry == nil {
1431		t.Fatalf("err: should have found a secondary index entry")
1432	}
1433
1434	err = exp.revokeEntry(namespace.RootContext(nil), le)
1435	if err != nil {
1436		t.Fatalf("err: %v", err)
1437	}
1438
1439	time.Sleep(300 * time.Millisecond)
1440
1441	out, err := exp.tokenStore.Lookup(namespace.RootContext(nil), le.ClientToken)
1442	if err != nil {
1443		t.Fatalf("err: %v", err)
1444	}
1445	if out != nil {
1446		t.Fatalf("bad: %v", out)
1447	}
1448
1449	indexEntry, err = exp.indexByToken(namespace.RootContext(nil), le)
1450	if err != nil {
1451		t.Fatalf("err: %v", err)
1452	}
1453	if indexEntry != nil {
1454		t.Fatalf("err: should not have found a secondary index entry")
1455	}
1456}
1457
1458func TestExpiration_renewEntry(t *testing.T) {
1459	exp := mockExpiration(t)
1460
1461	noop := &NoopBackend{
1462		Response: &logical.Response{
1463			Secret: &logical.Secret{
1464				LeaseOptions: logical.LeaseOptions{
1465					Renewable: true,
1466					TTL:       time.Hour,
1467				},
1468			},
1469			Data: map[string]interface{}{
1470				"testing": false,
1471			},
1472		},
1473	}
1474	_, barrier, _ := mockBarrier(t)
1475	view := NewBarrierView(barrier, "logical/")
1476	meUUID, err := uuid.GenerateUUID()
1477	if err != nil {
1478		t.Fatal(err)
1479	}
1480	err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1481	if err != nil {
1482		t.Fatal(err)
1483	}
1484
1485	le := &leaseEntry{
1486		LeaseID: "foo/bar/1234",
1487		Path:    "foo/bar",
1488		Data: map[string]interface{}{
1489			"testing": true,
1490		},
1491		Secret: &logical.Secret{
1492			LeaseOptions: logical.LeaseOptions{
1493				TTL: time.Minute,
1494			},
1495		},
1496		IssueTime:  time.Now(),
1497		ExpireTime: time.Now(),
1498		namespace:  namespace.RootNamespace,
1499	}
1500
1501	resp, err := exp.renewEntry(namespace.RootContext(nil), le, 0)
1502	if err != nil {
1503		t.Fatalf("err: %v", err)
1504	}
1505
1506	noop.Lock()
1507	defer noop.Unlock()
1508
1509	if !reflect.DeepEqual(resp, noop.Response) {
1510		t.Fatalf("bad: %#v", resp)
1511	}
1512
1513	req := noop.Requests[0]
1514	if req.Operation != logical.RenewOperation {
1515		t.Fatalf("Bad: %v", req)
1516	}
1517	if !reflect.DeepEqual(req.Data, le.Data) {
1518		t.Fatalf("Bad: %v", req)
1519	}
1520}
1521
1522func TestExpiration_revokeEntry_rejected(t *testing.T) {
1523	core, _, _ := TestCoreUnsealed(t)
1524	exp := core.expiration
1525
1526	rejected := new(uint32)
1527
1528	noop := &NoopBackend{
1529		RequestHandler: func(ctx context.Context, req *logical.Request) (*logical.Response, error) {
1530			if req.Operation == logical.RevokeOperation {
1531				if atomic.CompareAndSwapUint32(rejected, 0, 1) {
1532					t.Logf("denying revocation")
1533					return nil, errors.New("nope")
1534				}
1535				t.Logf("allowing revocation")
1536			}
1537			return nil, nil
1538		},
1539	}
1540	_, barrier, _ := mockBarrier(t)
1541	view := NewBarrierView(barrier, "logical/")
1542	meUUID, err := uuid.GenerateUUID()
1543	if err != nil {
1544		t.Fatal(err)
1545	}
1546	err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1547	if err != nil {
1548		t.Fatal(err)
1549	}
1550
1551	le := &leaseEntry{
1552		LeaseID: "foo/bar/1234",
1553		Path:    "foo/bar",
1554		Data: map[string]interface{}{
1555			"testing": true,
1556		},
1557		Secret: &logical.Secret{
1558			LeaseOptions: logical.LeaseOptions{
1559				TTL: time.Minute,
1560			},
1561		},
1562		IssueTime:  time.Now(),
1563		ExpireTime: time.Now().Add(time.Minute),
1564		namespace:  namespace.RootNamespace,
1565	}
1566
1567	err = exp.persistEntry(namespace.RootContext(nil), le)
1568	if err != nil {
1569		t.Fatal(err)
1570	}
1571
1572	err = exp.LazyRevoke(namespace.RootContext(nil), le.LeaseID)
1573	if err != nil {
1574		t.Fatal(err)
1575	}
1576
1577	// Give time to let the request be handled
1578	time.Sleep(1 * time.Second)
1579
1580	if atomic.LoadUint32(rejected) != 1 {
1581		t.Fatal("unexpected val for rejected")
1582	}
1583
1584	err = exp.Stop()
1585	if err != nil {
1586		t.Fatal(err)
1587	}
1588
1589	err = core.setupExpiration(expireLeaseStrategyRevoke)
1590	if err != nil {
1591		t.Fatal(err)
1592	}
1593	exp = core.expiration
1594
1595	for {
1596		if !exp.inRestoreMode() {
1597			break
1598		}
1599		time.Sleep(100 * time.Millisecond)
1600	}
1601
1602	// Now let the revocation actually process
1603	time.Sleep(1 * time.Second)
1604
1605	le, err = exp.FetchLeaseTimes(namespace.RootContext(nil), le.LeaseID)
1606	if err != nil {
1607		t.Fatal(err)
1608	}
1609	if le != nil {
1610		t.Fatal("lease entry not nil")
1611	}
1612}
1613
1614func TestExpiration_renewAuthEntry(t *testing.T) {
1615	exp := mockExpiration(t)
1616
1617	noop := &NoopBackend{
1618		Response: &logical.Response{
1619			Auth: &logical.Auth{
1620				LeaseOptions: logical.LeaseOptions{
1621					Renewable: true,
1622					TTL:       time.Hour,
1623				},
1624			},
1625		},
1626	}
1627	_, barrier, _ := mockBarrier(t)
1628	view := NewBarrierView(barrier, "auth/")
1629	meUUID, err := uuid.GenerateUUID()
1630	if err != nil {
1631		t.Fatal(err)
1632	}
1633	err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view)
1634	if err != nil {
1635		t.Fatal(err)
1636	}
1637
1638	le := &leaseEntry{
1639		LeaseID: "auth/foo/1234",
1640		Path:    "auth/foo/login",
1641		Auth: &logical.Auth{
1642			LeaseOptions: logical.LeaseOptions{
1643				Renewable: true,
1644				TTL:       time.Minute,
1645			},
1646			InternalData: map[string]interface{}{
1647				"MySecret": "secret",
1648			},
1649		},
1650		IssueTime:  time.Now(),
1651		ExpireTime: time.Now().Add(time.Minute),
1652		namespace:  namespace.RootNamespace,
1653	}
1654
1655	resp, err := exp.renewAuthEntry(namespace.RootContext(nil), &logical.Request{}, le, 0)
1656	if err != nil {
1657		t.Fatalf("err: %v", err)
1658	}
1659
1660	noop.Lock()
1661	defer noop.Unlock()
1662
1663	if !reflect.DeepEqual(resp, noop.Response) {
1664		t.Fatalf("bad: %#v", resp)
1665	}
1666
1667	req := noop.Requests[0]
1668	if req.Operation != logical.RenewOperation {
1669		t.Fatalf("Bad: %v", req)
1670	}
1671	if req.Path != "login" {
1672		t.Fatalf("Bad: %v", req)
1673	}
1674	if req.Auth.InternalData["MySecret"] != "secret" {
1675		t.Fatalf("Bad: %v", req)
1676	}
1677}
1678
1679func TestExpiration_PersistLoadDelete(t *testing.T) {
1680	exp := mockExpiration(t)
1681	lastTime := time.Now()
1682	le := &leaseEntry{
1683		LeaseID: "foo/bar/1234",
1684		Path:    "foo/bar",
1685		Data: map[string]interface{}{
1686			"testing": true,
1687		},
1688		Secret: &logical.Secret{
1689			LeaseOptions: logical.LeaseOptions{
1690				TTL: time.Minute,
1691			},
1692		},
1693		IssueTime:       lastTime,
1694		ExpireTime:      lastTime,
1695		LastRenewalTime: lastTime,
1696		namespace:       namespace.RootNamespace,
1697	}
1698	if err := exp.persistEntry(namespace.RootContext(nil), le); err != nil {
1699		t.Fatalf("err: %v", err)
1700	}
1701
1702	out, err := exp.loadEntry(namespace.RootContext(nil), "foo/bar/1234")
1703	if err != nil {
1704		t.Fatalf("err: %v", err)
1705	}
1706	if !le.LastRenewalTime.Equal(out.LastRenewalTime) ||
1707		!le.IssueTime.Equal(out.IssueTime) ||
1708		!le.ExpireTime.Equal(out.ExpireTime) {
1709		t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", le, out)
1710	}
1711	le.LastRenewalTime = out.LastRenewalTime
1712	le.IssueTime = out.IssueTime
1713	le.ExpireTime = out.ExpireTime
1714	if !reflect.DeepEqual(out, le) {
1715		t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", le, out)
1716	}
1717
1718	err = exp.deleteEntry(namespace.RootContext(nil), le)
1719	if err != nil {
1720		t.Fatalf("err: %v", err)
1721	}
1722
1723	out, err = exp.loadEntry(namespace.RootContext(nil), "foo/bar/1234")
1724	if err != nil {
1725		t.Fatalf("err: %v", err)
1726	}
1727	if out != nil {
1728		t.Fatalf("out: %#v", out)
1729	}
1730}
1731
1732func TestLeaseEntry(t *testing.T) {
1733	le := &leaseEntry{
1734		LeaseID: "foo/bar/1234",
1735		Path:    "foo/bar",
1736		Data: map[string]interface{}{
1737			"testing": true,
1738		},
1739		Secret: &logical.Secret{
1740			LeaseOptions: logical.LeaseOptions{
1741				TTL:       time.Minute,
1742				Renewable: true,
1743			},
1744		},
1745		IssueTime:  time.Now(),
1746		ExpireTime: time.Now(),
1747	}
1748
1749	enc, err := le.encode()
1750	if err != nil {
1751		t.Fatalf("err: %v", err)
1752	}
1753
1754	out, err := decodeLeaseEntry(enc)
1755	if err != nil {
1756		t.Fatalf("err: %v", err)
1757	}
1758
1759	if !reflect.DeepEqual(out.Data, le.Data) {
1760		t.Fatalf("got: %#v, expect %#v", out, le)
1761	}
1762
1763	// Test renewability
1764	le.ExpireTime = time.Time{}
1765	if r, _ := le.renewable(); r {
1766		t.Fatal("lease with zero expire time is not renewable")
1767	}
1768	le.ExpireTime = time.Now().Add(-1 * time.Hour)
1769	if r, _ := le.renewable(); r {
1770		t.Fatal("lease with expire time in the past is not renewable")
1771	}
1772	le.ExpireTime = time.Now().Add(1 * time.Hour)
1773	if r, err := le.renewable(); !r {
1774		t.Fatalf("lease with future expire time is renewable, err: %v", err)
1775	}
1776	le.Secret.LeaseOptions.Renewable = false
1777	if r, _ := le.renewable(); r {
1778		t.Fatal("secret is set to not be renewable but returns as renewable")
1779	}
1780	le.Secret = nil
1781	le.Auth = &logical.Auth{
1782		LeaseOptions: logical.LeaseOptions{
1783			Renewable: true,
1784		},
1785	}
1786	if r, err := le.renewable(); !r {
1787		t.Fatalf("auth is renewable but is set to not be, err: %v", err)
1788	}
1789	le.Auth.LeaseOptions.Renewable = false
1790	if r, _ := le.renewable(); r {
1791		t.Fatal("auth is set to not be renewable but returns as renewable")
1792	}
1793}
1794
1795func TestExpiration_RevokeForce(t *testing.T) {
1796	core, _, root := TestCoreUnsealed(t)
1797
1798	core.logicalBackends["badrenew"] = badRenewFactory
1799	me := &MountEntry{
1800		Table:    mountTableType,
1801		Path:     "badrenew/",
1802		Type:     "badrenew",
1803		Accessor: "badrenewaccessor",
1804	}
1805
1806	err := core.mount(namespace.RootContext(nil), me)
1807	if err != nil {
1808		t.Fatal(err)
1809	}
1810
1811	req := &logical.Request{
1812		Operation:   logical.ReadOperation,
1813		Path:        "badrenew/creds",
1814		ClientToken: root,
1815	}
1816	req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
1817
1818	resp, err := core.HandleRequest(namespace.RootContext(nil), req)
1819	if err != nil {
1820		t.Fatal(err)
1821	}
1822	if resp == nil {
1823		t.Fatal("response was nil")
1824	}
1825	if resp.Secret == nil {
1826		t.Fatalf("response secret was nil, response was %#v", *resp)
1827	}
1828
1829	req.Operation = logical.UpdateOperation
1830	req.Path = "sys/revoke-prefix/badrenew/creds"
1831
1832	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
1833	if err == nil {
1834		t.Fatal("expected error")
1835	}
1836
1837	req.Path = "sys/revoke-force/badrenew/creds"
1838	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
1839	if err != nil {
1840		t.Fatalf("got error: %s", err)
1841	}
1842}
1843
1844func TestExpiration_RevokeForceSingle(t *testing.T) {
1845	core, _, root := TestCoreUnsealed(t)
1846
1847	core.logicalBackends["badrenew"] = badRenewFactory
1848	me := &MountEntry{
1849		Table:    mountTableType,
1850		Path:     "badrenew/",
1851		Type:     "badrenew",
1852		Accessor: "badrenewaccessor",
1853	}
1854
1855	err := core.mount(namespace.RootContext(nil), me)
1856	if err != nil {
1857		t.Fatal(err)
1858	}
1859
1860	req := &logical.Request{
1861		Operation:   logical.ReadOperation,
1862		Path:        "badrenew/creds",
1863		ClientToken: root,
1864	}
1865	req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
1866
1867	resp, err := core.HandleRequest(namespace.RootContext(nil), req)
1868	if err != nil {
1869		t.Fatal(err)
1870	}
1871	if resp == nil {
1872		t.Fatal("response was nil")
1873	}
1874	if resp.Secret == nil {
1875		t.Fatalf("response secret was nil, response was %#v", *resp)
1876	}
1877	leaseID := resp.Secret.LeaseID
1878
1879	req.Operation = logical.UpdateOperation
1880	req.Path = "sys/leases/lookup"
1881	req.Data = map[string]interface{}{"lease_id": leaseID}
1882	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
1883	if err != nil {
1884		t.Fatal(err)
1885	}
1886	if resp == nil {
1887		t.Fatal("nil response")
1888	}
1889	if resp.Data["id"].(string) != leaseID {
1890		t.Fatalf("expected id %q, got %q", leaseID, resp.Data["id"].(string))
1891	}
1892
1893	req.Path = "sys/revoke-prefix/" + leaseID
1894
1895	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
1896	if err == nil {
1897		t.Fatal("expected error")
1898	}
1899
1900	req.Path = "sys/revoke-force/" + leaseID
1901	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
1902	if err != nil {
1903		t.Fatalf("got error: %s", err)
1904	}
1905
1906	req.Path = "sys/leases/lookup"
1907	req.Data = map[string]interface{}{"lease_id": leaseID}
1908	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
1909	if err == nil {
1910		t.Fatal("expected error")
1911	}
1912	if !strings.Contains(err.Error(), "invalid request") {
1913		t.Fatalf("bad error: %v", err)
1914	}
1915}
1916
1917func badRenewFactory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
1918	be := &framework.Backend{
1919		Paths: []*framework.Path{
1920			{
1921				Pattern: "creds",
1922				Callbacks: map[logical.Operation]framework.OperationFunc{
1923					logical.ReadOperation: func(context.Context, *logical.Request, *framework.FieldData) (*logical.Response, error) {
1924						resp := &logical.Response{
1925							Secret: &logical.Secret{
1926								InternalData: map[string]interface{}{
1927									"secret_type": "badRenewBackend",
1928								},
1929							},
1930						}
1931						resp.Secret.TTL = time.Second * 30
1932						return resp, nil
1933					},
1934				},
1935			},
1936		},
1937
1938		Secrets: []*framework.Secret{
1939			&framework.Secret{
1940				Type: "badRenewBackend",
1941				Revoke: func(context.Context, *logical.Request, *framework.FieldData) (*logical.Response, error) {
1942					return nil, fmt.Errorf("always errors")
1943				},
1944			},
1945		},
1946		BackendType: logical.TypeLogical,
1947	}
1948
1949	err := be.Setup(namespace.RootContext(nil), conf)
1950	if err != nil {
1951		return nil, err
1952	}
1953
1954	return be, nil
1955}
1956