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	"go.etcd.io/etcd/auth/authpb"
28	"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
29	pb "go.etcd.io/etcd/etcdserver/etcdserverpb"
30	"go.etcd.io/etcd/mvcc/backend"
31
32	"go.uber.org/zap"
33	"golang.org/x/crypto/bcrypt"
34	"google.golang.org/grpc/metadata"
35)
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(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
52	if err != nil {
53		t.Fatal(err)
54	}
55	as := NewAuthStore(zap.NewExample(), b, nil, tp, bcrypt.MinCost)
56	err = enableAuthAndCreateRoot(as)
57	if err != nil {
58		t.Fatal(err)
59	}
60	old := as.Revision()
61	as.Close()
62	b.Close()
63
64	// no changes to commit
65	b2 := backend.NewDefaultBackend(tPath)
66	as = NewAuthStore(zap.NewExample(), b2, nil, tp, bcrypt.MinCost)
67	new := as.Revision()
68	as.Close()
69	b2.Close()
70
71	if old != new {
72		t.Fatalf("expected revision %d, got %d", old, new)
73	}
74}
75
76// TestNewAuthStoreBryptCost ensures that NewAuthStore uses default when given bcrypt-cost is invalid
77func TestNewAuthStoreBcryptCost(t *testing.T) {
78	b, tPath := backend.NewDefaultTmpBackend()
79	defer os.Remove(tPath)
80
81	tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
82	if err != nil {
83		t.Fatal(err)
84	}
85
86	invalidCosts := [2]int{bcrypt.MinCost - 1, bcrypt.MaxCost + 1}
87	for _, invalidCost := range invalidCosts {
88		as := NewAuthStore(zap.NewExample(), b, nil, tp, invalidCost)
89		if as.BcryptCost() != bcrypt.DefaultCost {
90			t.Fatalf("expected DefaultCost when bcryptcost is invalid")
91		}
92		as.Close()
93	}
94
95	b.Close()
96}
97
98func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testing.T)) {
99	b, tPath := backend.NewDefaultTmpBackend()
100
101	tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
102	if err != nil {
103		t.Fatal(err)
104	}
105	as := NewAuthStore(zap.NewExample(), b, nil, tp, bcrypt.MinCost)
106	err = enableAuthAndCreateRoot(as)
107	if err != nil {
108		t.Fatal(err)
109	}
110
111	// adds a new role
112	_, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test"})
113	if err != nil {
114		t.Fatal(err)
115	}
116
117	ua := &pb.AuthUserAddRequest{Name: "foo", Password: "bar", Options: &authpb.UserAddOptions{NoPassword: false}}
118	_, err = as.UserAdd(ua) // add a non-existing user
119	if err != nil {
120		t.Fatal(err)
121	}
122
123	tearDown := func(_ *testing.T) {
124		b.Close()
125		os.Remove(tPath)
126		as.Close()
127	}
128	return as, tearDown
129}
130
131func enableAuthAndCreateRoot(as *authStore) error {
132	_, err := as.UserAdd(&pb.AuthUserAddRequest{Name: "root", Password: "root", Options: &authpb.UserAddOptions{NoPassword: false}})
133	if err != nil {
134		return err
135	}
136
137	_, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: "root"})
138	if err != nil {
139		return err
140	}
141
142	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "root", Role: "root"})
143	if err != nil {
144		return err
145	}
146
147	return as.AuthEnable()
148}
149
150func TestUserAdd(t *testing.T) {
151	as, tearDown := setupAuthStore(t)
152	defer tearDown(t)
153
154	ua := &pb.AuthUserAddRequest{Name: "foo", Options: &authpb.UserAddOptions{NoPassword: false}}
155	_, err := as.UserAdd(ua) // add an existing user
156	if err == nil {
157		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
158	}
159	if err != ErrUserAlreadyExist {
160		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
161	}
162
163	ua = &pb.AuthUserAddRequest{Name: "", Options: &authpb.UserAddOptions{NoPassword: false}}
164	_, err = as.UserAdd(ua) // add a user with empty name
165	if err != ErrUserEmpty {
166		t.Fatal(err)
167	}
168}
169
170func TestRecover(t *testing.T) {
171	as, tearDown := setupAuthStore(t)
172	defer tearDown(t)
173
174	as.enabled = false
175	as.Recover(as.be)
176
177	if !as.IsAuthEnabled() {
178		t.Fatalf("expected auth enabled got disabled")
179	}
180}
181
182func TestCheckPassword(t *testing.T) {
183	as, tearDown := setupAuthStore(t)
184	defer tearDown(t)
185
186	// auth a non-existing user
187	_, err := as.CheckPassword("foo-test", "bar")
188	if err == nil {
189		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
190	}
191	if err != ErrAuthFailed {
192		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
193	}
194
195	// auth an existing user with correct password
196	_, err = as.CheckPassword("foo", "bar")
197	if err != nil {
198		t.Fatal(err)
199	}
200
201	// auth an existing user but with wrong password
202	_, err = as.CheckPassword("foo", "")
203	if err == nil {
204		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
205	}
206	if err != ErrAuthFailed {
207		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
208	}
209}
210
211func TestUserDelete(t *testing.T) {
212	as, tearDown := setupAuthStore(t)
213	defer tearDown(t)
214
215	// delete an existing user
216	ud := &pb.AuthUserDeleteRequest{Name: "foo"}
217	_, err := as.UserDelete(ud)
218	if err != nil {
219		t.Fatal(err)
220	}
221
222	// delete a non-existing user
223	_, err = as.UserDelete(ud)
224	if err == nil {
225		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
226	}
227	if err != ErrUserNotFound {
228		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
229	}
230}
231
232func TestUserChangePassword(t *testing.T) {
233	as, tearDown := setupAuthStore(t)
234	defer tearDown(t)
235
236	ctx1 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
237	_, err := as.Authenticate(ctx1, "foo", "bar")
238	if err != nil {
239		t.Fatal(err)
240	}
241
242	_, err = as.UserChangePassword(&pb.AuthUserChangePasswordRequest{Name: "foo", Password: "baz"})
243	if err != nil {
244		t.Fatal(err)
245	}
246
247	ctx2 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
248	_, err = as.Authenticate(ctx2, "foo", "baz")
249	if err != nil {
250		t.Fatal(err)
251	}
252
253	// change a non-existing user
254	_, err = as.UserChangePassword(&pb.AuthUserChangePasswordRequest{Name: "foo-test", Password: "bar"})
255	if err == nil {
256		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
257	}
258	if err != ErrUserNotFound {
259		t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
260	}
261}
262
263func TestRoleAdd(t *testing.T) {
264	as, tearDown := setupAuthStore(t)
265	defer tearDown(t)
266
267	// adds a new role
268	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
269	if err != nil {
270		t.Fatal(err)
271	}
272
273	// add a role with empty name
274	_, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: ""})
275	if err != ErrRoleEmpty {
276		t.Fatal(err)
277	}
278}
279
280func TestUserGrant(t *testing.T) {
281	as, tearDown := setupAuthStore(t)
282	defer tearDown(t)
283
284	// grants a role to the user
285	_, err := as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
286	if err != nil {
287		t.Fatal(err)
288	}
289
290	// grants a role to a non-existing user
291	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo-test", Role: "role-test"})
292	if err == nil {
293		t.Errorf("expected %v, got %v", ErrUserNotFound, err)
294	}
295	if err != ErrUserNotFound {
296		t.Errorf("expected %v, got %v", ErrUserNotFound, err)
297	}
298}
299
300func TestHasRole(t *testing.T) {
301	as, tearDown := setupAuthStore(t)
302	defer tearDown(t)
303
304	// grants a role to the user
305	_, err := as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
306	if err != nil {
307		t.Fatal(err)
308	}
309
310	// checks role reflects correctly
311	hr := as.HasRole("foo", "role-test")
312	if !hr {
313		t.Fatal("expected role granted, got false")
314	}
315
316	// checks non existent role
317	hr = as.HasRole("foo", "non-existent-role")
318	if hr {
319		t.Fatal("expected role not found, got true")
320	}
321
322	// checks non existent user
323	hr = as.HasRole("nouser", "role-test")
324	if hr {
325		t.Fatal("expected user not found got true")
326	}
327}
328
329func TestIsOpPermitted(t *testing.T) {
330	as, tearDown := setupAuthStore(t)
331	defer tearDown(t)
332
333	// add new role
334	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
335	if err != nil {
336		t.Fatal(err)
337	}
338
339	perm := &authpb.Permission{
340		PermType: authpb.WRITE,
341		Key:      []byte("Keys"),
342		RangeEnd: []byte("RangeEnd"),
343	}
344
345	_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
346		Name: "role-test-1",
347		Perm: perm,
348	})
349	if err != nil {
350		t.Fatal(err)
351	}
352
353	// grants a role to the user
354	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test-1"})
355	if err != nil {
356		t.Fatal(err)
357	}
358
359	// check permission reflected to user
360
361	err = as.isOpPermitted("foo", as.Revision(), perm.Key, perm.RangeEnd, perm.PermType)
362	if err != nil {
363		t.Fatal(err)
364	}
365}
366
367func TestGetUser(t *testing.T) {
368	as, tearDown := setupAuthStore(t)
369	defer tearDown(t)
370
371	_, err := as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
372	if err != nil {
373		t.Fatal(err)
374	}
375
376	u, err := as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
377	if err != nil {
378		t.Fatal(err)
379	}
380	if u == nil {
381		t.Fatal("expect user not nil, got nil")
382	}
383	expected := []string{"role-test"}
384	if !reflect.DeepEqual(expected, u.Roles) {
385		t.Errorf("expected %v, got %v", expected, u.Roles)
386	}
387
388	// check non existent user
389	_, err = as.UserGet(&pb.AuthUserGetRequest{Name: "nouser"})
390	if err == nil {
391		t.Errorf("expected %v, got %v", ErrUserNotFound, err)
392	}
393}
394
395func TestListUsers(t *testing.T) {
396	as, tearDown := setupAuthStore(t)
397	defer tearDown(t)
398
399	ua := &pb.AuthUserAddRequest{Name: "user1", Password: "pwd1", Options: &authpb.UserAddOptions{NoPassword: false}}
400	_, err := as.UserAdd(ua) // add a non-existing user
401	if err != nil {
402		t.Fatal(err)
403	}
404
405	ul, err := as.UserList(&pb.AuthUserListRequest{})
406	if err != nil {
407		t.Fatal(err)
408	}
409	if !contains(ul.Users, "root") {
410		t.Errorf("expected %v in %v", "root", ul.Users)
411	}
412	if !contains(ul.Users, "user1") {
413		t.Errorf("expected %v in %v", "user1", ul.Users)
414	}
415}
416
417func TestRoleGrantPermission(t *testing.T) {
418	as, tearDown := setupAuthStore(t)
419	defer tearDown(t)
420
421	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
422	if err != nil {
423		t.Fatal(err)
424	}
425
426	perm := &authpb.Permission{
427		PermType: authpb.WRITE,
428		Key:      []byte("Keys"),
429		RangeEnd: []byte("RangeEnd"),
430	}
431	_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
432		Name: "role-test-1",
433		Perm: perm,
434	})
435
436	if err != nil {
437		t.Error(err)
438	}
439
440	r, err := as.RoleGet(&pb.AuthRoleGetRequest{Role: "role-test-1"})
441	if err != nil {
442		t.Fatal(err)
443	}
444
445	if !reflect.DeepEqual(perm, r.Perm[0]) {
446		t.Errorf("expected %v, got %v", perm, r.Perm[0])
447	}
448}
449
450func TestRoleRevokePermission(t *testing.T) {
451	as, tearDown := setupAuthStore(t)
452	defer tearDown(t)
453
454	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
455	if err != nil {
456		t.Fatal(err)
457	}
458
459	perm := &authpb.Permission{
460		PermType: authpb.WRITE,
461		Key:      []byte("Keys"),
462		RangeEnd: []byte("RangeEnd"),
463	}
464	_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
465		Name: "role-test-1",
466		Perm: perm,
467	})
468
469	if err != nil {
470		t.Fatal(err)
471	}
472
473	_, err = as.RoleGet(&pb.AuthRoleGetRequest{Role: "role-test-1"})
474	if err != nil {
475		t.Fatal(err)
476	}
477
478	_, err = as.RoleRevokePermission(&pb.AuthRoleRevokePermissionRequest{
479		Role:     "role-test-1",
480		Key:      []byte("Keys"),
481		RangeEnd: []byte("RangeEnd"),
482	})
483	if err != nil {
484		t.Fatal(err)
485	}
486
487	var r *pb.AuthRoleGetResponse
488	r, err = as.RoleGet(&pb.AuthRoleGetRequest{Role: "role-test-1"})
489	if err != nil {
490		t.Fatal(err)
491	}
492	if len(r.Perm) != 0 {
493		t.Errorf("expected %v, got %v", 0, len(r.Perm))
494	}
495}
496
497func TestUserRevokePermission(t *testing.T) {
498	as, tearDown := setupAuthStore(t)
499	defer tearDown(t)
500
501	_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
502	if err != nil {
503		t.Fatal(err)
504	}
505
506	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
507	if err != nil {
508		t.Fatal(err)
509	}
510
511	_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test-1"})
512	if err != nil {
513		t.Fatal(err)
514	}
515
516	u, err := as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
517	if err != nil {
518		t.Fatal(err)
519	}
520
521	expected := []string{"role-test", "role-test-1"}
522	if !reflect.DeepEqual(expected, u.Roles) {
523		t.Fatalf("expected %v, got %v", expected, u.Roles)
524	}
525
526	_, err = as.UserRevokeRole(&pb.AuthUserRevokeRoleRequest{Name: "foo", Role: "role-test-1"})
527	if err != nil {
528		t.Fatal(err)
529	}
530
531	u, err = as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
532	if err != nil {
533		t.Fatal(err)
534	}
535
536	expected = []string{"role-test"}
537	if !reflect.DeepEqual(expected, u.Roles) {
538		t.Errorf("expected %v, got %v", expected, u.Roles)
539	}
540}
541
542func TestRoleDelete(t *testing.T) {
543	as, tearDown := setupAuthStore(t)
544	defer tearDown(t)
545
546	_, err := as.RoleDelete(&pb.AuthRoleDeleteRequest{Role: "role-test"})
547	if err != nil {
548		t.Fatal(err)
549	}
550	rl, err := as.RoleList(&pb.AuthRoleListRequest{})
551	if err != nil {
552		t.Fatal(err)
553	}
554	expected := []string{"root"}
555	if !reflect.DeepEqual(expected, rl.Roles) {
556		t.Errorf("expected %v, got %v", expected, rl.Roles)
557	}
558}
559
560func TestAuthInfoFromCtx(t *testing.T) {
561	as, tearDown := setupAuthStore(t)
562	defer tearDown(t)
563
564	ctx := context.Background()
565	ai, err := as.AuthInfoFromCtx(ctx)
566	if err != nil && ai != nil {
567		t.Errorf("expected (nil, nil), got (%v, %v)", ai, err)
568	}
569
570	// as if it came from RPC
571	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"tokens": "dummy"}))
572	ai, err = as.AuthInfoFromCtx(ctx)
573	if err != nil && ai != nil {
574		t.Errorf("expected (nil, nil), got (%v, %v)", ai, err)
575	}
576
577	ctx = context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
578	resp, err := as.Authenticate(ctx, "foo", "bar")
579	if err != nil {
580		t.Error(err)
581	}
582
583	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "Invalid Token"}))
584	_, err = as.AuthInfoFromCtx(ctx)
585	if err != ErrInvalidAuthToken {
586		t.Errorf("expected %v, got %v", ErrInvalidAuthToken, err)
587	}
588
589	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "Invalid.Token"}))
590	_, err = as.AuthInfoFromCtx(ctx)
591	if err != ErrInvalidAuthToken {
592		t.Errorf("expected %v, got %v", ErrInvalidAuthToken, err)
593	}
594
595	ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: resp.Token}))
596	ai, err = as.AuthInfoFromCtx(ctx)
597	if err != nil {
598		t.Error(err)
599	}
600	if ai.Username != "foo" {
601		t.Errorf("expected %v, got %v", "foo", ai.Username)
602	}
603}
604
605func TestAuthDisable(t *testing.T) {
606	as, tearDown := setupAuthStore(t)
607	defer tearDown(t)
608
609	as.AuthDisable()
610	ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
611	_, err := as.Authenticate(ctx, "foo", "bar")
612	if err != ErrAuthNotEnabled {
613		t.Errorf("expected %v, got %v", ErrAuthNotEnabled, err)
614	}
615
616	// Disabling disabled auth to make sure it can return safely if store is already disabled.
617	as.AuthDisable()
618	_, err = as.Authenticate(ctx, "foo", "bar")
619	if err != ErrAuthNotEnabled {
620		t.Errorf("expected %v, got %v", ErrAuthNotEnabled, err)
621	}
622}
623
624func TestIsAuthEnabled(t *testing.T) {
625	as, tearDown := setupAuthStore(t)
626	defer tearDown(t)
627
628	// enable authentication to test the first possible condition
629	as.AuthEnable()
630
631	status := as.IsAuthEnabled()
632	ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
633	_, _ = as.Authenticate(ctx, "foo", "bar")
634	if status != true {
635		t.Errorf("expected %v, got %v", true, false)
636	}
637
638	// Disabling disabled auth to test the other condition that can be return
639	as.AuthDisable()
640
641	status = as.IsAuthEnabled()
642	_, _ = as.Authenticate(ctx, "foo", "bar")
643	if status != false {
644		t.Errorf("expected %v, got %v", false, true)
645	}
646}
647
648// TestAuthRevisionRace ensures that access to authStore.revision is thread-safe.
649func TestAuthInfoFromCtxRace(t *testing.T) {
650	b, tPath := backend.NewDefaultTmpBackend()
651	defer os.Remove(tPath)
652
653	tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
654	if err != nil {
655		t.Fatal(err)
656	}
657	as := NewAuthStore(zap.NewExample(), b, nil, tp, bcrypt.MinCost)
658	defer as.Close()
659
660	donec := make(chan struct{})
661	go func() {
662		defer close(donec)
663		ctx := metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "test"}))
664		as.AuthInfoFromCtx(ctx)
665	}()
666	as.UserAdd(&pb.AuthUserAddRequest{Name: "test", Options: &authpb.UserAddOptions{NoPassword: false}})
667	<-donec
668}
669
670func TestIsAdminPermitted(t *testing.T) {
671	as, tearDown := setupAuthStore(t)
672	defer tearDown(t)
673
674	err := as.IsAdminPermitted(&AuthInfo{Username: "root", Revision: 1})
675	if err != nil {
676		t.Errorf("expected nil, got %v", err)
677	}
678
679	// invalid user
680	err = as.IsAdminPermitted(&AuthInfo{Username: "rooti", Revision: 1})
681	if err != ErrUserNotFound {
682		t.Errorf("expected %v, got %v", ErrUserNotFound, err)
683	}
684
685	// non-admin user
686	err = as.IsAdminPermitted(&AuthInfo{Username: "foo", Revision: 1})
687	if err != ErrPermissionDenied {
688		t.Errorf("expected %v, got %v", ErrPermissionDenied, err)
689	}
690
691	// disabled auth should return nil
692	as.AuthDisable()
693	err = as.IsAdminPermitted(&AuthInfo{Username: "root", Revision: 1})
694	if err != nil {
695		t.Errorf("expected nil, got %v", err)
696	}
697}
698
699func TestRecoverFromSnapshot(t *testing.T) {
700	as, _ := setupAuthStore(t)
701
702	ua := &pb.AuthUserAddRequest{Name: "foo", Options: &authpb.UserAddOptions{NoPassword: false}}
703	_, err := as.UserAdd(ua) // add an existing user
704	if err == nil {
705		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
706	}
707	if err != ErrUserAlreadyExist {
708		t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
709	}
710
711	ua = &pb.AuthUserAddRequest{Name: "", Options: &authpb.UserAddOptions{NoPassword: false}}
712	_, err = as.UserAdd(ua) // add a user with empty name
713	if err != ErrUserEmpty {
714		t.Fatal(err)
715	}
716
717	as.Close()
718
719	tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
720	if err != nil {
721		t.Fatal(err)
722	}
723	as2 := NewAuthStore(zap.NewExample(), as.be, nil, tp, bcrypt.MinCost)
724	defer func(a *authStore) {
725		a.Close()
726	}(as2)
727
728	if !as2.IsAuthEnabled() {
729		t.Fatal("recovering authStore from existing backend failed")
730	}
731
732	ul, err := as.UserList(&pb.AuthUserListRequest{})
733	if err != nil {
734		t.Fatal(err)
735	}
736	if !contains(ul.Users, "root") {
737		t.Errorf("expected %v in %v", "root", ul.Users)
738	}
739}
740
741func contains(array []string, str string) bool {
742	for _, s := range array {
743		if s == str {
744			return true
745		}
746	}
747	return false
748}
749
750func TestHammerSimpleAuthenticate(t *testing.T) {
751	// set TTL values low to try to trigger races
752	oldTTL, oldTTLRes := simpleTokenTTL, simpleTokenTTLResolution
753	defer func() {
754		simpleTokenTTL = oldTTL
755		simpleTokenTTLResolution = oldTTLRes
756	}()
757	simpleTokenTTL = 10 * time.Millisecond
758	simpleTokenTTLResolution = simpleTokenTTL
759	users := make(map[string]struct{})
760
761	as, tearDown := setupAuthStore(t)
762	defer tearDown(t)
763
764	// create lots of users
765	for i := 0; i < 50; i++ {
766		u := fmt.Sprintf("user-%d", i)
767		ua := &pb.AuthUserAddRequest{Name: u, Password: "123", Options: &authpb.UserAddOptions{NoPassword: false}}
768		if _, err := as.UserAdd(ua); err != nil {
769			t.Fatal(err)
770		}
771		users[u] = struct{}{}
772	}
773
774	// hammer on authenticate with lots of users
775	for i := 0; i < 10; i++ {
776		var wg sync.WaitGroup
777		wg.Add(len(users))
778		for u := range users {
779			go func(user string) {
780				defer wg.Done()
781				token := fmt.Sprintf("%s(%d)", user, i)
782				ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, token)
783				if _, err := as.Authenticate(ctx, user, "123"); err != nil {
784					t.Error(err)
785				}
786				if _, err := as.AuthInfoFromCtx(ctx); err != nil {
787					t.Error(err)
788				}
789			}(u)
790		}
791		time.Sleep(time.Millisecond)
792		wg.Wait()
793	}
794}
795
796// TestRolesOrder tests authpb.User.Roles is sorted
797func TestRolesOrder(t *testing.T) {
798	b, tPath := backend.NewDefaultTmpBackend()
799	defer os.Remove(tPath)
800
801	tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
802	if err != nil {
803		t.Fatal(err)
804	}
805	as := NewAuthStore(zap.NewExample(), b, nil, tp, bcrypt.MinCost)
806	err = enableAuthAndCreateRoot(as)
807	if err != nil {
808		t.Fatal(err)
809	}
810
811	username := "user"
812	_, err = as.UserAdd(&pb.AuthUserAddRequest{Name: username, Password: "pass", Options: &authpb.UserAddOptions{NoPassword: false}})
813	if err != nil {
814		t.Fatal(err)
815	}
816
817	roles := []string{"role1", "role2", "abc", "xyz", "role3"}
818	for _, role := range roles {
819		_, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: role})
820		if err != nil {
821			t.Fatal(err)
822		}
823
824		_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: username, Role: role})
825		if err != nil {
826			t.Fatal(err)
827		}
828	}
829
830	user, err := as.UserGet(&pb.AuthUserGetRequest{Name: username})
831	if err != nil {
832		t.Fatal(err)
833	}
834
835	for i := 1; i < len(user.Roles); i++ {
836		if strings.Compare(user.Roles[i-1], user.Roles[i]) != -1 {
837			t.Errorf("User.Roles isn't sorted (%s vs %s)", user.Roles[i-1], user.Roles[i])
838		}
839	}
840}
841
842func TestAuthInfoFromCtxWithRootSimple(t *testing.T) {
843	testAuthInfoFromCtxWithRoot(t, tokenTypeSimple)
844}
845
846func TestAuthInfoFromCtxWithRootJWT(t *testing.T) {
847	opts := testJWTOpts()
848	testAuthInfoFromCtxWithRoot(t, opts)
849}
850
851// testAuthInfoFromCtxWithRoot ensures "WithRoot" properly embeds token in the context.
852func testAuthInfoFromCtxWithRoot(t *testing.T, opts string) {
853	b, tPath := backend.NewDefaultTmpBackend()
854	defer os.Remove(tPath)
855
856	tp, err := NewTokenProvider(zap.NewExample(), opts, dummyIndexWaiter)
857	if err != nil {
858		t.Fatal(err)
859	}
860	as := NewAuthStore(zap.NewExample(), b, nil, tp, bcrypt.MinCost)
861	defer as.Close()
862
863	if err = enableAuthAndCreateRoot(as); err != nil {
864		t.Fatal(err)
865	}
866
867	ctx := context.Background()
868	ctx = as.WithRoot(ctx)
869
870	ai, aerr := as.AuthInfoFromCtx(ctx)
871	if aerr != nil {
872		t.Error(err)
873	}
874	if ai == nil {
875		t.Error("expected non-nil *AuthInfo")
876	}
877	if ai.Username != "root" {
878		t.Errorf("expected user name 'root', got %+v", ai)
879	}
880}
881
882func TestUserNoPasswordAdd(t *testing.T) {
883	as, tearDown := setupAuthStore(t)
884	defer tearDown(t)
885
886	username := "usernopass"
887	ua := &pb.AuthUserAddRequest{Name: username, Options: &authpb.UserAddOptions{NoPassword: true}}
888	_, err := as.UserAdd(ua)
889	if err != nil {
890		t.Fatal(err)
891	}
892
893	ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
894	_, err = as.Authenticate(ctx, username, "")
895	if err != ErrAuthFailed {
896		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
897	}
898}
899