1/*
2Copyright 2018 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package authenticator
18
19import (
20	"context"
21	"errors"
22	"fmt"
23	"reflect"
24	"testing"
25
26	"k8s.io/apimachinery/pkg/util/diff"
27	"k8s.io/apiserver/pkg/authentication/user"
28)
29
30func TestAuthenticate(t *testing.T) {
31	type treq struct {
32		resp          *Response
33		authenticated bool
34		err           error
35
36		wantResp          *Response
37		wantAuthenticated bool
38		wantErr           bool
39	}
40	type taudcfg struct {
41		auds         Audiences
42		implicitAuds Audiences
43	}
44	cs := []struct {
45		name string
46
47		taudcfgs []taudcfg
48		treqs    []treq
49	}{
50		{
51			name: "good audience",
52
53			taudcfgs: []taudcfg{
54				{
55					implicitAuds: Audiences{"api"},
56					auds:         Audiences{"api"},
57				},
58				{
59					implicitAuds: Audiences{"api", "other"},
60					auds:         Audiences{"api"},
61				},
62				{
63					implicitAuds: Audiences{"api"},
64					auds:         Audiences{"api", "other"},
65				},
66				{
67					implicitAuds: Audiences{"api", "other"},
68					auds:         Audiences{"api", "other_other"},
69				},
70			},
71
72			treqs: []treq{
73				{
74					resp: &Response{
75						User: &user.DefaultInfo{
76							Name: "test_user",
77						},
78					},
79					authenticated: true,
80
81					wantResp: &Response{
82						User: &user.DefaultInfo{
83							Name: "test_user",
84						},
85						Audiences: Audiences{"api"},
86					},
87					wantAuthenticated: true,
88				},
89				{
90					err:     errors.New("uhoh"),
91					wantErr: true,
92				},
93				{
94					authenticated:     false,
95					wantAuthenticated: false,
96				},
97			},
98		},
99		{
100			name: "multiple good audiences",
101
102			taudcfgs: []taudcfg{
103				{
104					implicitAuds: Audiences{"api", "other_api"},
105					auds:         Audiences{"api", "other_api"},
106				},
107				{
108					implicitAuds: Audiences{"api", "other_api", "other"},
109					auds:         Audiences{"api", "other_api"},
110				},
111				{
112					implicitAuds: Audiences{"api", "other_api"},
113					auds:         Audiences{"api", "other_api", "other"},
114				},
115				{
116					implicitAuds: Audiences{"api", "other_api", "other"},
117					auds:         Audiences{"api", "other_api", "other_other"},
118				},
119			},
120
121			treqs: []treq{
122				{
123					resp: &Response{
124						User: &user.DefaultInfo{
125							Name: "test_user",
126						},
127					},
128					authenticated: true,
129
130					wantResp: &Response{
131						User: &user.DefaultInfo{
132							Name: "test_user",
133						},
134						Audiences: Audiences{"api", "other_api"},
135					},
136					wantAuthenticated: true,
137				},
138				{
139					err: errors.New("uhoh"),
140
141					wantErr: true,
142				},
143				{
144					authenticated: false,
145
146					wantAuthenticated: false,
147				},
148			},
149		},
150		{
151			name: "bad audience(s)",
152
153			taudcfgs: []taudcfg{
154				{
155					implicitAuds: Audiences{"api"},
156					auds:         Audiences{"other_api"},
157				},
158				{
159					implicitAuds: Audiences{},
160					auds:         Audiences{"other_api"},
161				},
162				{
163					implicitAuds: Audiences{"api"},
164					auds:         Audiences{},
165				},
166				{
167					implicitAuds: Audiences{"api", "other"},
168					auds:         Audiences{},
169				},
170				{
171					implicitAuds: Audiences{},
172					auds:         Audiences{"api", "other"},
173				},
174			},
175
176			treqs: []treq{
177				{
178					resp: &Response{
179						User: &user.DefaultInfo{
180							Name: "test_user",
181						},
182					},
183					authenticated: true,
184
185					wantAuthenticated: false,
186				},
187				{
188					err: errors.New("uhoh"),
189
190					wantAuthenticated: false,
191				},
192				{
193					authenticated: false,
194
195					wantAuthenticated: false,
196				},
197			},
198		},
199	}
200
201	for _, c := range cs {
202		t.Run(c.name, func(t *testing.T) {
203			for _, taudcfg := range c.taudcfgs {
204				for _, treq := range c.treqs {
205					t.Run(fmt.Sprintf("auds=%q,implicit=%q", taudcfg.auds, taudcfg.implicitAuds), func(t *testing.T) {
206						ctx := context.Background()
207						ctx = WithAudiences(ctx, taudcfg.auds)
208						resp, ok, err := authenticate(ctx, taudcfg.implicitAuds, func() (*Response, bool, error) {
209							if treq.resp != nil {
210								resp := *treq.resp
211								return &resp, treq.authenticated, treq.err
212							}
213							return nil, treq.authenticated, treq.err
214						})
215						if got, want := (err != nil), treq.wantErr; got != want {
216							t.Errorf("Unexpected error. got=%v, want=%v, err=%v", got, want, err)
217						}
218						if got, want := ok, treq.wantAuthenticated; got != want {
219							t.Errorf("Unexpected authentication. got=%v, want=%v", got, want)
220						}
221						if got, want := resp, treq.wantResp; !reflect.DeepEqual(got, want) {
222							t.Errorf("Unexpected response. diff:\n%v", diff.ObjectGoPrintDiff(got, want))
223						}
224					})
225				}
226			}
227		})
228	}
229}
230