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