1// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package auth
16
17import (
18	"context"
19	"fmt"
20	"os"
21	"reflect"
22	"strings"
23	"sync"
24	"testing"
25	"time"
26
27	"github.com/coreos/etcd/auth/authpb"
28	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
29	"github.com/coreos/etcd/mvcc/backend"
30
31	"golang.org/x/crypto/bcrypt"
32	"google.golang.org/grpc/metadata"
33)
34
35func init() { BcryptCost = bcrypt.MinCost }
36
37func dummyIndexWaiter(index uint64) <-chan struct{} {
38	ch := make(chan struct{})
39	go func() {
40		ch <- struct{}{}
41	}()
42	return ch
43}
44
45// TestNewAuthStoreRevision ensures newly auth store
46// keeps the old revision when there are no changes.
47func TestNewAuthStoreRevision(t *testing.T) {
48	b, tPath := backend.NewDefaultTmpBackend()
49	defer os.Remove(tPath)
50
51	tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter)
52	if err != nil {
53		t.Fatal(err)
54	}
55	as := NewAuthStore(b, tp)
56	err = enableAuthAndCreateRoot(as)
57	if err != nil {
58		t.Fatal(err)
59	}
60	old := as.Revision()
61	b.Close()
62	as.Close()
63
64	// no changes to commit
65	b2 := backend.NewDefaultBackend(tPath)
66	as = NewAuthStore(b2, tp)
67	new := as.Revision()
68	b2.Close()
69	as.Close()
70
71	if old != new {
72		t.Fatalf("expected revision %d, got %d", old, new)
73	}
74}
75
76func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testing.T)) {
77	b, tPath := backend.NewDefaultTmpBackend()
78
79	tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter)
80	if err != nil {
81		t.Fatal(err)
82	}
83	as := NewAuthStore(b, tp)
84	err = enableAuthAndCreateRoot(as)
85	if err != nil {
86		t.Fatal(err)
87	}
88
89	// adds a new role
90	_, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test"})
91	if err != nil {
92		t.Fatal(err)
93	}
94
95	ua := &pb.AuthUserAddRequest{Name: "foo", Password: "bar"}
96	_, err = as.UserAdd(ua) // add a non-existing user
97	if err != nil {
98		t.Fatal(err)
99	}
100
101	tearDown := func(t *testing.T) {
102		b.Close()
103		os.Remove(tPath)
104		as.Close()
105	}
106	return as, tearDown
107}
108
109func enableAuthAndCreateRoot(as *authStore) error {
110	_, err := as.UserAdd(&pb.AuthUserAddRequest{Name: "root", Password: "root"})
111	if err != nil {
112		return err
113	}
114
115	_, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: "root"})
116	if err != nil {
117		return err
118	}
119
120	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "root", Role: "root"})
121	if err != nil {
122		return err
123	}
124
125	return as.AuthEnable()
126}
127
128func TestUserAdd(t *testing.T) {
129	as, tearDown := setupAuthStore(t)
130	defer tearDown(t)
131
132	ua := &pb.AuthUserAddRequest{Name: "foo"}
133	_, err := as.UserAdd(ua) // add an existing user
134	if err == nil {
135		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
136	}
137	if err != ErrUserAlreadyExist {
138		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
139	}
140
141	ua = &pb.AuthUserAddRequest{Name: ""}
142	_, err = as.UserAdd(ua) // add a user with empty name
143	if err != ErrUserEmpty {
144		t.Fatal(err)
145	}
146}
147
148func TestCheckPassword(t *testing.T) {
149	as, tearDown := setupAuthStore(t)
150	defer tearDown(t)
151
152	// auth a non-existing user
153	_, err := as.CheckPassword("foo-test", "bar")
154	if err == nil {
155		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
156	}
157	if err != ErrAuthFailed {
158		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
159	}
160
161	// auth an existing user with correct password
162	_, err = as.CheckPassword("foo", "bar")
163	if err != nil {
164		t.Fatal(err)
165	}
166
167	// auth an existing user but with wrong password
168	_, err = as.CheckPassword("foo", "")
169	if err == nil {
170		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
171	}
172	if err != ErrAuthFailed {
173		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
174	}
175}
176
177func TestUserDelete(t *testing.T) {
178	as, tearDown := setupAuthStore(t)
179	defer tearDown(t)
180
181	// delete an existing user
182	ud := &pb.AuthUserDeleteRequest{Name: "foo"}
183	_, err := as.UserDelete(ud)
184	if err != nil {
185		t.Fatal(err)
186	}
187
188	// delete a non-existing user
189	_, err = as.UserDelete(ud)
190	if err == nil {
191		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
192	}
193	if err != ErrUserNotFound {
194		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
195	}
196}
197
198func TestUserChangePassword(t *testing.T) {
199	as, tearDown := setupAuthStore(t)
200	defer tearDown(t)
201
202	ctx1 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
203	_, err := as.Authenticate(ctx1, "foo", "bar")
204	if err != nil {
205		t.Fatal(err)
206	}
207
208	_, err = as.UserChangePassword(&pb.AuthUserChangePasswordRequest{Name: "foo", Password: "baz"})
209	if err != nil {
210		t.Fatal(err)
211	}
212
213	ctx2 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
214	_, err = as.Authenticate(ctx2, "foo", "baz")
215	if err != nil {
216		t.Fatal(err)
217	}
218
219	// change a non-existing user
220	_, err = as.UserChangePassword(&pb.AuthUserChangePasswordRequest{Name: "foo-test", Password: "bar"})
221	if err == nil {
222		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
223	}
224	if err != ErrUserNotFound {
225		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
226	}
227}
228
229func TestRoleAdd(t *testing.T) {
230	as, tearDown := setupAuthStore(t)
231	defer tearDown(t)
232
233	// adds a new role
234	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
235	if err != nil {
236		t.Fatal(err)
237	}
238}
239
240func TestUserGrant(t *testing.T) {
241	as, tearDown := setupAuthStore(t)
242	defer tearDown(t)
243
244	// grants a role to the user
245	_, err := as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
246	if err != nil {
247		t.Fatal(err)
248	}
249
250	// grants a role to a non-existing user
251	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo-test", Role: "role-test"})
252	if err == nil {
253		t.Errorf("expected %v, got %v", ErrUserNotFound, err)
254	}
255	if err != ErrUserNotFound {
256		t.Errorf("expected %v, got %v", ErrUserNotFound, err)
257	}
258}
259
260func TestGetUser(t *testing.T) {
261	as, tearDown := setupAuthStore(t)
262	defer tearDown(t)
263
264	_, err := as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
265	if err != nil {
266		t.Fatal(err)
267	}
268
269	u, err := as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
270	if err != nil {
271		t.Fatal(err)
272	}
273	if u == nil {
274		t.Fatal("expect user not nil, got nil")
275	}
276	expected := []string{"role-test"}
277	if !reflect.DeepEqual(expected, u.Roles) {
278		t.Errorf("expected %v, got %v", expected, u.Roles)
279	}
280}
281
282func TestListUsers(t *testing.T) {
283	as, tearDown := setupAuthStore(t)
284	defer tearDown(t)
285
286	ua := &pb.AuthUserAddRequest{Name: "user1", Password: "pwd1"}
287	_, err := as.UserAdd(ua) // add a non-existing user
288	if err != nil {
289		t.Fatal(err)
290	}
291
292	ul, err := as.UserList(&pb.AuthUserListRequest{})
293	if err != nil {
294		t.Fatal(err)
295	}
296	if !contains(ul.Users, "root") {
297		t.Errorf("expected %v in %v", "root", ul.Users)
298	}
299	if !contains(ul.Users, "user1") {
300		t.Errorf("expected %v in %v", "user1", ul.Users)
301	}
302}
303
304func TestRoleGrantPermission(t *testing.T) {
305	as, tearDown := setupAuthStore(t)
306	defer tearDown(t)
307
308	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
309	if err != nil {
310		t.Fatal(err)
311	}
312
313	perm := &authpb.Permission{
314		PermType: authpb.WRITE,
315		Key:      []byte("Keys"),
316		RangeEnd: []byte("RangeEnd"),
317	}
318	_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
319		Name: "role-test-1",
320		Perm: perm,
321	})
322
323	if err != nil {
324		t.Error(err)
325	}
326
327	r, err := as.RoleGet(&pb.AuthRoleGetRequest{Role: "role-test-1"})
328	if err != nil {
329		t.Fatal(err)
330	}
331
332	if !reflect.DeepEqual(perm, r.Perm[0]) {
333		t.Errorf("expected %v, got %v", perm, r.Perm[0])
334	}
335}
336
337func TestRoleRevokePermission(t *testing.T) {
338	as, tearDown := setupAuthStore(t)
339	defer tearDown(t)
340
341	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
342	if err != nil {
343		t.Fatal(err)
344	}
345
346	perm := &authpb.Permission{
347		PermType: authpb.WRITE,
348		Key:      []byte("Keys"),
349		RangeEnd: []byte("RangeEnd"),
350	}
351	_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
352		Name: "role-test-1",
353		Perm: perm,
354	})
355
356	if err != nil {
357		t.Fatal(err)
358	}
359
360	_, err = as.RoleGet(&pb.AuthRoleGetRequest{Role: "role-test-1"})
361	if err != nil {
362		t.Fatal(err)
363	}
364
365	_, err = as.RoleRevokePermission(&pb.AuthRoleRevokePermissionRequest{
366		Role:     "role-test-1",
367		Key:      "Keys",
368		RangeEnd: "RangeEnd",
369	})
370	if err != nil {
371		t.Fatal(err)
372	}
373
374	var r *pb.AuthRoleGetResponse
375	r, err = as.RoleGet(&pb.AuthRoleGetRequest{Role: "role-test-1"})
376	if err != nil {
377		t.Fatal(err)
378	}
379	if len(r.Perm) != 0 {
380		t.Errorf("expected %v, got %v", 0, len(r.Perm))
381	}
382}
383
384func TestUserRevokePermission(t *testing.T) {
385	as, tearDown := setupAuthStore(t)
386	defer tearDown(t)
387
388	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
389	if err != nil {
390		t.Fatal(err)
391	}
392
393	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
394	if err != nil {
395		t.Fatal(err)
396	}
397
398	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test-1"})
399	if err != nil {
400		t.Fatal(err)
401	}
402
403	u, err := as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
404	if err != nil {
405		t.Fatal(err)
406	}
407
408	expected := []string{"role-test", "role-test-1"}
409	if !reflect.DeepEqual(expected, u.Roles) {
410		t.Fatalf("expected %v, got %v", expected, u.Roles)
411	}
412
413	_, err = as.UserRevokeRole(&pb.AuthUserRevokeRoleRequest{Name: "foo", Role: "role-test-1"})
414	if err != nil {
415		t.Fatal(err)
416	}
417
418	u, err = as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
419	if err != nil {
420		t.Fatal(err)
421	}
422
423	expected = []string{"role-test"}
424	if !reflect.DeepEqual(expected, u.Roles) {
425		t.Errorf("expected %v, got %v", expected, u.Roles)
426	}
427}
428
429func TestRoleDelete(t *testing.T) {
430	as, tearDown := setupAuthStore(t)
431	defer tearDown(t)
432
433	_, err := as.RoleDelete(&pb.AuthRoleDeleteRequest{Role: "role-test"})
434	if err != nil {
435		t.Fatal(err)
436	}
437	rl, err := as.RoleList(&pb.AuthRoleListRequest{})
438	if err != nil {
439		t.Fatal(err)
440	}
441	expected := []string{"root"}
442	if !reflect.DeepEqual(expected, rl.Roles) {
443		t.Errorf("expected %v, got %v", expected, rl.Roles)
444	}
445}
446
447func TestAuthInfoFromCtx(t *testing.T) {
448	as, tearDown := setupAuthStore(t)
449	defer tearDown(t)
450
451	ctx := context.Background()
452	ai, err := as.AuthInfoFromCtx(ctx)
453	if err != nil && ai != nil {
454		t.Errorf("expected (nil, nil), got (%v, %v)", ai, err)
455	}
456
457	// as if it came from RPC
458	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"tokens": "dummy"}))
459	ai, err = as.AuthInfoFromCtx(ctx)
460	if err != nil && ai != nil {
461		t.Errorf("expected (nil, nil), got (%v, %v)", ai, err)
462	}
463
464	ctx = context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
465	resp, err := as.Authenticate(ctx, "foo", "bar")
466	if err != nil {
467		t.Error(err)
468	}
469
470	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"token": "Invalid Token"}))
471	_, err = as.AuthInfoFromCtx(ctx)
472	if err != ErrInvalidAuthToken {
473		t.Errorf("expected %v, got %v", ErrInvalidAuthToken, err)
474	}
475
476	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"token": "Invalid.Token"}))
477	_, err = as.AuthInfoFromCtx(ctx)
478	if err != ErrInvalidAuthToken {
479		t.Errorf("expected %v, got %v", ErrInvalidAuthToken, err)
480	}
481
482	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"token": resp.Token}))
483	ai, err = as.AuthInfoFromCtx(ctx)
484	if err != nil {
485		t.Error(err)
486	}
487	if ai.Username != "foo" {
488		t.Errorf("expected %v, got %v", "foo", ai.Username)
489	}
490}
491
492func TestAuthDisable(t *testing.T) {
493	as, tearDown := setupAuthStore(t)
494	defer tearDown(t)
495
496	as.AuthDisable()
497	ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
498	_, err := as.Authenticate(ctx, "foo", "bar")
499	if err != ErrAuthNotEnabled {
500		t.Errorf("expected %v, got %v", ErrAuthNotEnabled, err)
501	}
502
503	// Disabling disabled auth to make sure it can return safely if store is already disabled.
504	as.AuthDisable()
505	_, err = as.Authenticate(ctx, "foo", "bar")
506	if err != ErrAuthNotEnabled {
507		t.Errorf("expected %v, got %v", ErrAuthNotEnabled, err)
508	}
509}
510
511// TestAuthRevisionRace ensures that access to authStore.revision is thread-safe.
512func TestAuthInfoFromCtxRace(t *testing.T) {
513	b, tPath := backend.NewDefaultTmpBackend()
514	defer os.Remove(tPath)
515
516	tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter)
517	if err != nil {
518		t.Fatal(err)
519	}
520	as := NewAuthStore(b, tp)
521	defer as.Close()
522
523	donec := make(chan struct{})
524	go func() {
525		defer close(donec)
526		ctx := metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"token": "test"}))
527		as.AuthInfoFromCtx(ctx)
528	}()
529	as.UserAdd(&pb.AuthUserAddRequest{Name: "test"})
530	<-donec
531}
532
533func TestIsAdminPermitted(t *testing.T) {
534	as, tearDown := setupAuthStore(t)
535	defer tearDown(t)
536
537	err := as.IsAdminPermitted(&AuthInfo{Username: "root", Revision: 1})
538	if err != nil {
539		t.Errorf("expected nil, got %v", err)
540	}
541
542	// invalid user
543	err = as.IsAdminPermitted(&AuthInfo{Username: "rooti", Revision: 1})
544	if err != ErrUserNotFound {
545		t.Errorf("expected %v, got %v", ErrUserNotFound, err)
546	}
547
548	// non-admin user
549	err = as.IsAdminPermitted(&AuthInfo{Username: "foo", Revision: 1})
550	if err != ErrPermissionDenied {
551		t.Errorf("expected %v, got %v", ErrPermissionDenied, err)
552	}
553
554	// disabled auth should return nil
555	as.AuthDisable()
556	err = as.IsAdminPermitted(&AuthInfo{Username: "root", Revision: 1})
557	if err != nil {
558		t.Errorf("expected nil, got %v", err)
559	}
560}
561
562func TestRecoverFromSnapshot(t *testing.T) {
563	as, _ := setupAuthStore(t)
564
565	ua := &pb.AuthUserAddRequest{Name: "foo"}
566	_, err := as.UserAdd(ua) // add an existing user
567	if err == nil {
568		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
569	}
570	if err != ErrUserAlreadyExist {
571		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
572	}
573
574	ua = &pb.AuthUserAddRequest{Name: ""}
575	_, err = as.UserAdd(ua) // add a user with empty name
576	if err != ErrUserEmpty {
577		t.Fatal(err)
578	}
579
580	as.Close()
581
582	tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter)
583	if err != nil {
584		t.Fatal(err)
585	}
586	as2 := NewAuthStore(as.be, tp)
587	defer func(a *authStore) {
588		a.Close()
589	}(as2)
590
591	if !as2.isAuthEnabled() {
592		t.Fatal("recovering authStore from existing backend failed")
593	}
594
595	ul, err := as.UserList(&pb.AuthUserListRequest{})
596	if err != nil {
597		t.Fatal(err)
598	}
599	if !contains(ul.Users, "root") {
600		t.Errorf("expected %v in %v", "root", ul.Users)
601	}
602}
603
604func contains(array []string, str string) bool {
605	for _, s := range array {
606		if s == str {
607			return true
608		}
609	}
610	return false
611}
612
613func TestHammerSimpleAuthenticate(t *testing.T) {
614	// set TTL values low to try to trigger races
615	oldTTL, oldTTLRes := simpleTokenTTL, simpleTokenTTLResolution
616	defer func() {
617		simpleTokenTTL = oldTTL
618		simpleTokenTTLResolution = oldTTLRes
619	}()
620	simpleTokenTTL = 10 * time.Millisecond
621	simpleTokenTTLResolution = simpleTokenTTL
622	users := make(map[string]struct{})
623
624	as, tearDown := setupAuthStore(t)
625	defer tearDown(t)
626
627	// create lots of users
628	for i := 0; i < 50; i++ {
629		u := fmt.Sprintf("user-%d", i)
630		ua := &pb.AuthUserAddRequest{Name: u, Password: "123"}
631		if _, err := as.UserAdd(ua); err != nil {
632			t.Fatal(err)
633		}
634		users[u] = struct{}{}
635	}
636
637	// hammer on authenticate with lots of users
638	for i := 0; i < 10; i++ {
639		var wg sync.WaitGroup
640		wg.Add(len(users))
641		for u := range users {
642			go func(user string) {
643				defer wg.Done()
644				token := fmt.Sprintf("%s(%d)", user, i)
645				ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, token)
646				if _, err := as.Authenticate(ctx, user, "123"); err != nil {
647					t.Fatal(err)
648				}
649				if _, err := as.AuthInfoFromCtx(ctx); err != nil {
650					t.Fatal(err)
651				}
652			}(u)
653		}
654		time.Sleep(time.Millisecond)
655		wg.Wait()
656	}
657}
658
659// TestRolesOrder tests authpb.User.Roles is sorted
660func TestRolesOrder(t *testing.T) {
661	b, tPath := backend.NewDefaultTmpBackend()
662	defer os.Remove(tPath)
663
664	tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter)
665	if err != nil {
666		t.Fatal(err)
667	}
668	as := NewAuthStore(b, tp)
669	err = enableAuthAndCreateRoot(as)
670	if err != nil {
671		t.Fatal(err)
672	}
673
674	username := "user"
675	_, err = as.UserAdd(&pb.AuthUserAddRequest{username, "pass"})
676	if err != nil {
677		t.Fatal(err)
678	}
679
680	roles := []string{"role1", "role2", "abc", "xyz", "role3"}
681	for _, role := range roles {
682		_, err = as.RoleAdd(&pb.AuthRoleAddRequest{role})
683		if err != nil {
684			t.Fatal(err)
685		}
686
687		_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{username, role})
688		if err != nil {
689			t.Fatal(err)
690		}
691	}
692
693	user, err := as.UserGet(&pb.AuthUserGetRequest{username})
694	if err != nil {
695		t.Fatal(err)
696	}
697
698	for i := 1; i < len(user.Roles); i++ {
699		if strings.Compare(user.Roles[i-1], user.Roles[i]) != -1 {
700			t.Errorf("User.Roles isn't sorted (%s vs %s)", user.Roles[i-1], user.Roles[i])
701		}
702	}
703}
704
705func TestAuthInfoFromCtxWithRootSimple(t *testing.T) {
706	testAuthInfoFromCtxWithRoot(t, tokenTypeSimple)
707}
708
709func TestAuthInfoFromCtxWithRootJWT(t *testing.T) {
710	opts := testJWTOpts()
711	testAuthInfoFromCtxWithRoot(t, opts)
712}
713
714// testAuthInfoFromCtxWithRoot ensures "WithRoot" properly embeds token in the context.
715func testAuthInfoFromCtxWithRoot(t *testing.T, opts string) {
716	b, tPath := backend.NewDefaultTmpBackend()
717	defer os.Remove(tPath)
718
719	tp, err := NewTokenProvider(opts, dummyIndexWaiter)
720	if err != nil {
721		t.Fatal(err)
722	}
723	as := NewAuthStore(b, tp)
724	defer as.Close()
725
726	if err = enableAuthAndCreateRoot(as); err != nil {
727		t.Fatal(err)
728	}
729
730	ctx := context.Background()
731	ctx = as.WithRoot(ctx)
732
733	ai, aerr := as.AuthInfoFromCtx(ctx)
734	if aerr != nil {
735		t.Error(err)
736	}
737	if ai == nil {
738		t.Error("expected non-nil *AuthInfo")
739	}
740	if ai.Username != "root" {
741		t.Errorf("expected user name 'root', got %+v", ai)
742	}
743}
744