1// Copyright 2015 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 v2http
16
17import (
18	"crypto/tls"
19	"crypto/x509"
20	"encoding/json"
21	"encoding/pem"
22	"errors"
23	"fmt"
24	"io/ioutil"
25	"net/http"
26	"net/http/httptest"
27	"net/url"
28	"path"
29	"sort"
30	"strings"
31	"testing"
32
33	"go.etcd.io/etcd/etcdserver/api"
34	"go.etcd.io/etcd/etcdserver/api/v2auth"
35
36	"go.uber.org/zap"
37)
38
39const goodPassword = "good"
40
41func mustJSONRequest(t *testing.T, method string, p string, body string) *http.Request {
42	req, err := http.NewRequest(method, path.Join(authPrefix, p), strings.NewReader(body))
43	if err != nil {
44		t.Fatalf("Error making JSON request: %s %s %s\n", method, p, body)
45	}
46	req.Header.Set("Content-Type", "application/json")
47	return req
48}
49
50type mockAuthStore struct {
51	users   map[string]*v2auth.User
52	roles   map[string]*v2auth.Role
53	err     error
54	enabled bool
55}
56
57func (s *mockAuthStore) AllUsers() ([]string, error) {
58	var us []string
59	for u := range s.users {
60		us = append(us, u)
61	}
62	sort.Strings(us)
63	return us, s.err
64}
65func (s *mockAuthStore) GetUser(name string) (v2auth.User, error) {
66	u, ok := s.users[name]
67	if !ok {
68		return v2auth.User{}, s.err
69	}
70	return *u, s.err
71}
72func (s *mockAuthStore) CreateOrUpdateUser(user v2auth.User) (out v2auth.User, created bool, err error) {
73	if s.users == nil {
74		out, err = s.CreateUser(user)
75		return out, true, err
76	}
77	out, err = s.UpdateUser(user)
78	return out, false, err
79}
80func (s *mockAuthStore) CreateUser(user v2auth.User) (v2auth.User, error) { return user, s.err }
81func (s *mockAuthStore) DeleteUser(name string) error                     { return s.err }
82func (s *mockAuthStore) UpdateUser(user v2auth.User) (v2auth.User, error) {
83	return *s.users[user.User], s.err
84}
85func (s *mockAuthStore) AllRoles() ([]string, error) {
86	return []string{"awesome", "guest", "root"}, s.err
87}
88func (s *mockAuthStore) GetRole(name string) (v2auth.Role, error) {
89	r, ok := s.roles[name]
90	if ok {
91		return *r, s.err
92	}
93	return v2auth.Role{}, fmt.Errorf("%q does not exist (%v)", name, s.err)
94}
95func (s *mockAuthStore) CreateRole(role v2auth.Role) error { return s.err }
96func (s *mockAuthStore) DeleteRole(name string) error      { return s.err }
97func (s *mockAuthStore) UpdateRole(role v2auth.Role) (v2auth.Role, error) {
98	return *s.roles[role.Role], s.err
99}
100func (s *mockAuthStore) AuthEnabled() bool  { return s.enabled }
101func (s *mockAuthStore) EnableAuth() error  { return s.err }
102func (s *mockAuthStore) DisableAuth() error { return s.err }
103
104func (s *mockAuthStore) CheckPassword(user v2auth.User, password string) bool {
105	return user.Password == password
106}
107
108func (s *mockAuthStore) HashPassword(password string) (string, error) {
109	return password, nil
110}
111
112func TestAuthFlow(t *testing.T) {
113	api.EnableCapability(api.AuthCapability)
114	var testCases = []struct {
115		req   *http.Request
116		store mockAuthStore
117
118		wcode int
119		wbody string
120	}{
121		{
122			req:   mustJSONRequest(t, "PUT", "users/alice", `{{{{{{{`),
123			store: mockAuthStore{},
124			wcode: http.StatusBadRequest,
125			wbody: `{"message":"Invalid JSON in request body."}`,
126		},
127		{
128			req:   mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`),
129			store: mockAuthStore{enabled: true},
130			wcode: http.StatusUnauthorized,
131			wbody: `{"message":"Insufficient credentials"}`,
132		},
133		// Users
134		{
135			req: mustJSONRequest(t, "GET", "users", ""),
136			store: mockAuthStore{
137				users: map[string]*v2auth.User{
138					"alice": {
139						User:     "alice",
140						Roles:    []string{"alicerole", "guest"},
141						Password: "wheeee",
142					},
143					"bob": {
144						User:     "bob",
145						Roles:    []string{"guest"},
146						Password: "wheeee",
147					},
148					"root": {
149						User:     "root",
150						Roles:    []string{"root"},
151						Password: "wheeee",
152					},
153				},
154				roles: map[string]*v2auth.Role{
155					"alicerole": {
156						Role: "alicerole",
157					},
158					"guest": {
159						Role: "guest",
160					},
161					"root": {
162						Role: "root",
163					},
164				},
165			},
166			wcode: http.StatusOK,
167			wbody: `{"users":[` +
168				`{"user":"alice","roles":[` +
169				`{"role":"alicerole","permissions":{"kv":{"read":null,"write":null}}},` +
170				`{"role":"guest","permissions":{"kv":{"read":null,"write":null}}}` +
171				`]},` +
172				`{"user":"bob","roles":[{"role":"guest","permissions":{"kv":{"read":null,"write":null}}}]},` +
173				`{"user":"root","roles":[{"role":"root","permissions":{"kv":{"read":null,"write":null}}}]}]}`,
174		},
175		{
176			req: mustJSONRequest(t, "GET", "users/alice", ""),
177			store: mockAuthStore{
178				users: map[string]*v2auth.User{
179					"alice": {
180						User:     "alice",
181						Roles:    []string{"alicerole"},
182						Password: "wheeee",
183					},
184				},
185				roles: map[string]*v2auth.Role{
186					"alicerole": {
187						Role: "alicerole",
188					},
189				},
190			},
191			wcode: http.StatusOK,
192			wbody: `{"user":"alice","roles":[{"role":"alicerole","permissions":{"kv":{"read":null,"write":null}}}]}`,
193		},
194		{
195			req:   mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`),
196			store: mockAuthStore{},
197			wcode: http.StatusCreated,
198			wbody: `{"user":"alice","roles":null}`,
199		},
200		{
201			req:   mustJSONRequest(t, "DELETE", "users/alice", ``),
202			store: mockAuthStore{},
203			wcode: http.StatusOK,
204			wbody: ``,
205		},
206		{
207			req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`),
208			store: mockAuthStore{
209				users: map[string]*v2auth.User{
210					"alice": {
211						User:     "alice",
212						Roles:    []string{"alicerole", "guest"},
213						Password: "wheeee",
214					},
215				},
216			},
217			wcode: http.StatusOK,
218			wbody: `{"user":"alice","roles":["alicerole","guest"]}`,
219		},
220		{
221			req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "grant": ["alicerole"]}`),
222			store: mockAuthStore{
223				users: map[string]*v2auth.User{
224					"alice": {
225						User:     "alice",
226						Roles:    []string{"alicerole", "guest"},
227						Password: "wheeee",
228					},
229				},
230			},
231			wcode: http.StatusOK,
232			wbody: `{"user":"alice","roles":["alicerole","guest"]}`,
233		},
234		{
235			req: mustJSONRequest(t, "GET", "users/alice", ``),
236			store: mockAuthStore{
237				users: map[string]*v2auth.User{},
238				err:   v2auth.Error{Status: http.StatusNotFound, Errmsg: "auth: User alice doesn't exist."},
239			},
240			wcode: http.StatusNotFound,
241			wbody: `{"message":"auth: User alice doesn't exist."}`,
242		},
243		{
244			req: mustJSONRequest(t, "GET", "roles/manager", ""),
245			store: mockAuthStore{
246				roles: map[string]*v2auth.Role{
247					"manager": {
248						Role: "manager",
249					},
250				},
251			},
252			wcode: http.StatusOK,
253			wbody: `{"role":"manager","permissions":{"kv":{"read":null,"write":null}}}`,
254		},
255		{
256			req:   mustJSONRequest(t, "DELETE", "roles/manager", ``),
257			store: mockAuthStore{},
258			wcode: http.StatusOK,
259			wbody: ``,
260		},
261		{
262			req:   mustJSONRequest(t, "PUT", "roles/manager", `{"role":"manager","permissions":{"kv":{"read":[],"write":[]}}}`),
263			store: mockAuthStore{},
264			wcode: http.StatusCreated,
265			wbody: `{"role":"manager","permissions":{"kv":{"read":[],"write":[]}}}`,
266		},
267		{
268			req: mustJSONRequest(t, "PUT", "roles/manager", `{"role":"manager","revoke":{"kv":{"read":["foo"],"write":[]}}}`),
269			store: mockAuthStore{
270				roles: map[string]*v2auth.Role{
271					"manager": {
272						Role: "manager",
273					},
274				},
275			},
276			wcode: http.StatusOK,
277			wbody: `{"role":"manager","permissions":{"kv":{"read":null,"write":null}}}`,
278		},
279		{
280			req: mustJSONRequest(t, "GET", "roles", ""),
281			store: mockAuthStore{
282				roles: map[string]*v2auth.Role{
283					"awesome": {
284						Role: "awesome",
285					},
286					"guest": {
287						Role: "guest",
288					},
289					"root": {
290						Role: "root",
291					},
292				},
293			},
294			wcode: http.StatusOK,
295			wbody: `{"roles":[{"role":"awesome","permissions":{"kv":{"read":null,"write":null}}},` +
296				`{"role":"guest","permissions":{"kv":{"read":null,"write":null}}},` +
297				`{"role":"root","permissions":{"kv":{"read":null,"write":null}}}]}`,
298		},
299		{
300			req: mustJSONRequest(t, "GET", "enable", ""),
301			store: mockAuthStore{
302				enabled: true,
303			},
304			wcode: http.StatusOK,
305			wbody: `{"enabled":true}`,
306		},
307		{
308			req: mustJSONRequest(t, "PUT", "enable", ""),
309			store: mockAuthStore{
310				enabled: false,
311			},
312			wcode: http.StatusOK,
313			wbody: ``,
314		},
315		{
316			req: (func() *http.Request {
317				req := mustJSONRequest(t, "DELETE", "enable", "")
318				req.SetBasicAuth("root", "good")
319				return req
320			})(),
321			store: mockAuthStore{
322				enabled: true,
323				users: map[string]*v2auth.User{
324					"root": {
325						User:     "root",
326						Password: goodPassword,
327						Roles:    []string{"root"},
328					},
329				},
330				roles: map[string]*v2auth.Role{
331					"root": {
332						Role: "root",
333					},
334				},
335			},
336			wcode: http.StatusOK,
337			wbody: ``,
338		},
339		{
340			req: (func() *http.Request {
341				req := mustJSONRequest(t, "DELETE", "enable", "")
342				req.SetBasicAuth("root", "bad")
343				return req
344			})(),
345			store: mockAuthStore{
346				enabled: true,
347				users: map[string]*v2auth.User{
348					"root": {
349						User:     "root",
350						Password: goodPassword,
351						Roles:    []string{"root"},
352					},
353				},
354				roles: map[string]*v2auth.Role{
355					"root": {
356						Role: "guest",
357					},
358				},
359			},
360			wcode: http.StatusUnauthorized,
361			wbody: `{"message":"Insufficient credentials"}`,
362		},
363	}
364
365	for i, tt := range testCases {
366		mux := http.NewServeMux()
367		h := &authHandler{
368			lg:      zap.NewExample(),
369			sec:     &tt.store,
370			cluster: &fakeCluster{id: 1},
371		}
372		handleAuth(mux, h)
373		rw := httptest.NewRecorder()
374		mux.ServeHTTP(rw, tt.req)
375		if rw.Code != tt.wcode {
376			t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
377		}
378		g := rw.Body.String()
379		g = strings.TrimSpace(g)
380		if g != tt.wbody {
381			t.Errorf("#%d: got body=%s, want %s", i, g, tt.wbody)
382		}
383	}
384}
385
386func TestGetUserGrantedWithNonexistingRole(t *testing.T) {
387	sh := &authHandler{
388		sec: &mockAuthStore{
389			users: map[string]*v2auth.User{
390				"root": {
391					User:  "root",
392					Roles: []string{"root", "foo"},
393				},
394			},
395			roles: map[string]*v2auth.Role{
396				"root": {
397					Role: "root",
398				},
399			},
400		},
401		cluster: &fakeCluster{id: 1},
402	}
403	srv := httptest.NewServer(http.HandlerFunc(sh.baseUsers))
404	defer srv.Close()
405
406	req, err := http.NewRequest("GET", "", nil)
407	if err != nil {
408		t.Fatal(err)
409	}
410	req.URL, err = url.Parse(srv.URL)
411	if err != nil {
412		t.Fatal(err)
413	}
414	req.Header.Set("Content-Type", "application/json")
415
416	cli := http.DefaultClient
417	resp, err := cli.Do(req)
418	if err != nil {
419		t.Fatal(err)
420	}
421	defer resp.Body.Close()
422
423	var uc usersCollections
424	if err := json.NewDecoder(resp.Body).Decode(&uc); err != nil {
425		t.Fatal(err)
426	}
427	if len(uc.Users) != 1 {
428		t.Fatalf("expected 1 user, got %+v", uc.Users)
429	}
430	if uc.Users[0].User != "root" {
431		t.Fatalf("expected 'root', got %q", uc.Users[0].User)
432	}
433	if len(uc.Users[0].Roles) != 1 {
434		t.Fatalf("expected 1 role, got %+v", uc.Users[0].Roles)
435	}
436	if uc.Users[0].Roles[0].Role != "root" {
437		t.Fatalf("expected 'root', got %q", uc.Users[0].Roles[0].Role)
438	}
439}
440
441func mustAuthRequest(username, password string) *http.Request {
442	req, err := http.NewRequest(http.MethodGet, "path", strings.NewReader(""))
443	if err != nil {
444		panic("Cannot make auth request: " + err.Error())
445	}
446	req.SetBasicAuth(username, password)
447	return req
448}
449
450func unauthedRequest() *http.Request {
451	req, err := http.NewRequest(http.MethodGet, "path", strings.NewReader(""))
452	if err != nil {
453		panic("Cannot make request: " + err.Error())
454	}
455	return req
456}
457
458func tlsAuthedRequest(req *http.Request, certname string) *http.Request {
459	bytes, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.pem", certname))
460	if err != nil {
461		panic(err)
462	}
463
464	block, _ := pem.Decode(bytes)
465	cert, err := x509.ParseCertificate(block.Bytes)
466	if err != nil {
467		panic(err)
468	}
469
470	req.TLS = &tls.ConnectionState{
471		VerifiedChains: [][]*x509.Certificate{{cert}},
472	}
473	return req
474}
475
476func TestPrefixAccess(t *testing.T) {
477	var table = []struct {
478		key                string
479		req                *http.Request
480		store              *mockAuthStore
481		hasRoot            bool
482		hasKeyPrefixAccess bool
483		hasRecursiveAccess bool
484	}{
485		{
486			key: "/foo",
487			req: mustAuthRequest("root", "good"),
488			store: &mockAuthStore{
489				users: map[string]*v2auth.User{
490					"root": {
491						User:     "root",
492						Password: goodPassword,
493						Roles:    []string{"root"},
494					},
495				},
496				roles: map[string]*v2auth.Role{
497					"root": {
498						Role: "root",
499					},
500				},
501				enabled: true,
502			},
503			hasRoot:            true,
504			hasKeyPrefixAccess: true,
505			hasRecursiveAccess: true,
506		},
507		{
508			key: "/foo",
509			req: mustAuthRequest("user", "good"),
510			store: &mockAuthStore{
511				users: map[string]*v2auth.User{
512					"user": {
513						User:     "user",
514						Password: goodPassword,
515						Roles:    []string{"foorole"},
516					},
517				},
518				roles: map[string]*v2auth.Role{
519					"foorole": {
520						Role: "foorole",
521						Permissions: v2auth.Permissions{
522							KV: v2auth.RWPermission{
523								Read:  []string{"/foo"},
524								Write: []string{"/foo"},
525							},
526						},
527					},
528				},
529				enabled: true,
530			},
531			hasRoot:            false,
532			hasKeyPrefixAccess: true,
533			hasRecursiveAccess: false,
534		},
535		{
536			key: "/foo",
537			req: mustAuthRequest("user", "good"),
538			store: &mockAuthStore{
539				users: map[string]*v2auth.User{
540					"user": {
541						User:     "user",
542						Password: goodPassword,
543						Roles:    []string{"foorole"},
544					},
545				},
546				roles: map[string]*v2auth.Role{
547					"foorole": {
548						Role: "foorole",
549						Permissions: v2auth.Permissions{
550							KV: v2auth.RWPermission{
551								Read:  []string{"/foo*"},
552								Write: []string{"/foo*"},
553							},
554						},
555					},
556				},
557				enabled: true,
558			},
559			hasRoot:            false,
560			hasKeyPrefixAccess: true,
561			hasRecursiveAccess: true,
562		},
563		{
564			key: "/foo",
565			req: mustAuthRequest("user", "bad"),
566			store: &mockAuthStore{
567				users: map[string]*v2auth.User{
568					"user": {
569						User:     "user",
570						Password: goodPassword,
571						Roles:    []string{"foorole"},
572					},
573				},
574				roles: map[string]*v2auth.Role{
575					"foorole": {
576						Role: "foorole",
577						Permissions: v2auth.Permissions{
578							KV: v2auth.RWPermission{
579								Read:  []string{"/foo*"},
580								Write: []string{"/foo*"},
581							},
582						},
583					},
584				},
585				enabled: true,
586			},
587			hasRoot:            false,
588			hasKeyPrefixAccess: false,
589			hasRecursiveAccess: false,
590		},
591		{
592			key: "/foo",
593			req: mustAuthRequest("user", "good"),
594			store: &mockAuthStore{
595				users:   map[string]*v2auth.User{},
596				err:     errors.New("Not the user"),
597				enabled: true,
598			},
599			hasRoot:            false,
600			hasKeyPrefixAccess: false,
601			hasRecursiveAccess: false,
602		},
603		{
604			key: "/foo",
605			req: mustJSONRequest(t, "GET", "somepath", ""),
606			store: &mockAuthStore{
607				users: map[string]*v2auth.User{
608					"user": {
609						User:     "user",
610						Password: goodPassword,
611						Roles:    []string{"foorole"},
612					},
613				},
614				roles: map[string]*v2auth.Role{
615					"guest": {
616						Role: "guest",
617						Permissions: v2auth.Permissions{
618							KV: v2auth.RWPermission{
619								Read:  []string{"/foo*"},
620								Write: []string{"/foo*"},
621							},
622						},
623					},
624				},
625				enabled: true,
626			},
627			hasRoot:            false,
628			hasKeyPrefixAccess: true,
629			hasRecursiveAccess: true,
630		},
631		{
632			key: "/bar",
633			req: mustJSONRequest(t, "GET", "somepath", ""),
634			store: &mockAuthStore{
635				users: map[string]*v2auth.User{
636					"user": {
637						User:     "user",
638						Password: goodPassword,
639						Roles:    []string{"foorole"},
640					},
641				},
642				roles: map[string]*v2auth.Role{
643					"guest": {
644						Role: "guest",
645						Permissions: v2auth.Permissions{
646							KV: v2auth.RWPermission{
647								Read:  []string{"/foo*"},
648								Write: []string{"/foo*"},
649							},
650						},
651					},
652				},
653				enabled: true,
654			},
655			hasRoot:            false,
656			hasKeyPrefixAccess: false,
657			hasRecursiveAccess: false,
658		},
659		// check access for multiple roles
660		{
661			key: "/foo",
662			req: mustAuthRequest("user", "good"),
663			store: &mockAuthStore{
664				users: map[string]*v2auth.User{
665					"user": {
666						User:     "user",
667						Password: goodPassword,
668						Roles:    []string{"role1", "role2"},
669					},
670				},
671				roles: map[string]*v2auth.Role{
672					"role1": {
673						Role: "role1",
674					},
675					"role2": {
676						Role: "role2",
677						Permissions: v2auth.Permissions{
678							KV: v2auth.RWPermission{
679								Read:  []string{"/foo"},
680								Write: []string{"/foo"},
681							},
682						},
683					},
684				},
685				enabled: true,
686			},
687			hasRoot:            false,
688			hasKeyPrefixAccess: true,
689			hasRecursiveAccess: false,
690		},
691		{
692			key: "/foo",
693			req: (func() *http.Request {
694				req := mustJSONRequest(t, "GET", "somepath", "")
695				req.Header.Set("Authorization", "malformedencoding")
696				return req
697			})(),
698			store: &mockAuthStore{
699				enabled: true,
700				users: map[string]*v2auth.User{
701					"root": {
702						User:     "root",
703						Password: goodPassword,
704						Roles:    []string{"root"},
705					},
706				},
707				roles: map[string]*v2auth.Role{
708					"guest": {
709						Role: "guest",
710						Permissions: v2auth.Permissions{
711							KV: v2auth.RWPermission{
712								Read:  []string{"/foo*"},
713								Write: []string{"/foo*"},
714							},
715						},
716					},
717				},
718			},
719			hasRoot:            false,
720			hasKeyPrefixAccess: false,
721			hasRecursiveAccess: false,
722		},
723		{ // guest access in non-TLS mode
724			key: "/foo",
725			req: (func() *http.Request {
726				return mustJSONRequest(t, "GET", "somepath", "")
727			})(),
728			store: &mockAuthStore{
729				enabled: true,
730				users: map[string]*v2auth.User{
731					"root": {
732						User:     "root",
733						Password: goodPassword,
734						Roles:    []string{"root"},
735					},
736				},
737				roles: map[string]*v2auth.Role{
738					"guest": {
739						Role: "guest",
740						Permissions: v2auth.Permissions{
741							KV: v2auth.RWPermission{
742								Read:  []string{"/foo*"},
743								Write: []string{"/foo*"},
744							},
745						},
746					},
747				},
748			},
749			hasRoot:            false,
750			hasKeyPrefixAccess: true,
751			hasRecursiveAccess: true,
752		},
753	}
754
755	for i, tt := range table {
756		if tt.hasRoot != hasRootAccess(zap.NewExample(), tt.store, tt.req, true) {
757			t.Errorf("#%d: hasRoot doesn't match (expected %v)", i, tt.hasRoot)
758		}
759		if tt.hasKeyPrefixAccess != hasKeyPrefixAccess(zap.NewExample(), tt.store, tt.req, tt.key, false, true) {
760			t.Errorf("#%d: hasKeyPrefixAccess doesn't match (expected %v)", i, tt.hasRoot)
761		}
762		if tt.hasRecursiveAccess != hasKeyPrefixAccess(zap.NewExample(), tt.store, tt.req, tt.key, true, true) {
763			t.Errorf("#%d: hasRecursiveAccess doesn't match (expected %v)", i, tt.hasRoot)
764		}
765	}
766}
767
768func TestUserFromClientCertificate(t *testing.T) {
769	witherror := &mockAuthStore{
770		users: map[string]*v2auth.User{
771			"user": {
772				User:     "user",
773				Roles:    []string{"root"},
774				Password: "password",
775			},
776			"basicauth": {
777				User:     "basicauth",
778				Roles:    []string{"root"},
779				Password: "password",
780			},
781		},
782		roles: map[string]*v2auth.Role{
783			"root": {
784				Role: "root",
785			},
786		},
787		err: errors.New(""),
788	}
789
790	noerror := &mockAuthStore{
791		users: map[string]*v2auth.User{
792			"user": {
793				User:     "user",
794				Roles:    []string{"root"},
795				Password: "password",
796			},
797			"basicauth": {
798				User:     "basicauth",
799				Roles:    []string{"root"},
800				Password: "password",
801			},
802		},
803		roles: map[string]*v2auth.Role{
804			"root": {
805				Role: "root",
806			},
807		},
808	}
809
810	var table = []struct {
811		req        *http.Request
812		userExists bool
813		store      v2auth.Store
814		username   string
815	}{
816		{
817			// non tls request
818			req:        unauthedRequest(),
819			userExists: false,
820			store:      witherror,
821		},
822		{
823			// cert with cn of existing user
824			req:        tlsAuthedRequest(unauthedRequest(), "user"),
825			userExists: true,
826			username:   "user",
827			store:      noerror,
828		},
829		{
830			// cert with cn of non-existing user
831			req:        tlsAuthedRequest(unauthedRequest(), "otheruser"),
832			userExists: false,
833			store:      witherror,
834		},
835	}
836
837	for i, tt := range table {
838		user := userFromClientCertificate(zap.NewExample(), tt.store, tt.req)
839		userExists := user != nil
840
841		if tt.userExists != userExists {
842			t.Errorf("#%d: userFromClientCertificate doesn't match (expected %v)", i, tt.userExists)
843		}
844		if user != nil && (tt.username != user.User) {
845			t.Errorf("#%d: userFromClientCertificate username doesn't match (expected %s, got %s)", i, tt.username, user.User)
846		}
847	}
848}
849
850func TestUserFromBasicAuth(t *testing.T) {
851	sec := &mockAuthStore{
852		users: map[string]*v2auth.User{
853			"user": {
854				User:     "user",
855				Roles:    []string{"root"},
856				Password: "password",
857			},
858		},
859		roles: map[string]*v2auth.Role{
860			"root": {
861				Role: "root",
862			},
863		},
864	}
865
866	var table = []struct {
867		username   string
868		req        *http.Request
869		userExists bool
870	}{
871		{
872			// valid user, valid pass
873			username:   "user",
874			req:        mustAuthRequest("user", "password"),
875			userExists: true,
876		},
877		{
878			// valid user, bad pass
879			username:   "user",
880			req:        mustAuthRequest("user", "badpass"),
881			userExists: false,
882		},
883		{
884			// valid user, no pass
885			username:   "user",
886			req:        mustAuthRequest("user", ""),
887			userExists: false,
888		},
889		{
890			// missing user
891			username:   "missing",
892			req:        mustAuthRequest("missing", "badpass"),
893			userExists: false,
894		},
895		{
896			// no basic auth
897			req:        unauthedRequest(),
898			userExists: false,
899		},
900	}
901
902	for i, tt := range table {
903		user := userFromBasicAuth(zap.NewExample(), sec, tt.req)
904		userExists := user != nil
905
906		if tt.userExists != userExists {
907			t.Errorf("#%d: userFromBasicAuth doesn't match (expected %v)", i, tt.userExists)
908		}
909		if user != nil && (tt.username != user.User) {
910			t.Errorf("#%d: userFromBasicAuth username doesn't match (expected %s, got %s)", i, tt.username, user.User)
911		}
912	}
913}
914