1
16
17 package auth
18
19 import (
20 "context"
21 "fmt"
22 "net/http"
23 "reflect"
24 "sync"
25 "sync/atomic"
26 "testing"
27
28 authenticationv1 "k8s.io/api/authentication/v1"
29 authenticationv1alpha1 "k8s.io/api/authentication/v1alpha1"
30 authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 "k8s.io/apiserver/pkg/authentication/authenticator"
33 "k8s.io/apiserver/pkg/authentication/user"
34 "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
35 "k8s.io/kubernetes/pkg/controlplane"
36 "k8s.io/kubernetes/test/integration/framework"
37 "k8s.io/kubernetes/test/utils/ktesting"
38 )
39
40 func TestGetsSelfAttributes(t *testing.T) {
41 tests := []struct {
42 name string
43 userInfo *user.DefaultInfo
44 expectedName string
45 expectedUID string
46 expectedGroups []string
47 expectedExtra map[string]authenticationv1.ExtraValue
48 }{
49 {
50 name: "Username",
51 userInfo: &user.DefaultInfo{
52 Name: "alice",
53 },
54 expectedName: "alice",
55 },
56 {
57 name: "Username with groups and UID",
58 userInfo: &user.DefaultInfo{
59 Name: "alice",
60 UID: "unique-id",
61 Groups: []string{"devs", "admins"},
62 },
63 expectedName: "alice",
64 expectedUID: "unique-id",
65 expectedGroups: []string{"devs", "admins"},
66 },
67 {
68 name: "Username with extra attributes",
69 userInfo: &user.DefaultInfo{
70 Name: "alice",
71 Extra: map[string][]string{
72 "nicknames": {"cutie", "bestie"},
73 },
74 },
75 expectedName: "alice",
76 expectedExtra: map[string]authenticationv1.ExtraValue{
77 "nicknames": authenticationv1.ExtraValue([]string{"cutie", "bestie"}),
78 },
79 },
80 {
81 name: "Without username",
82 userInfo: &user.DefaultInfo{
83 UID: "unique-id",
84 },
85 expectedUID: "unique-id",
86 },
87 }
88
89 tCtx := ktesting.Init(t)
90 var respMu sync.RWMutex
91 response := &user.DefaultInfo{
92 Name: "stub",
93 }
94
95 kubeClient, _, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
96 ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
97 opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1alpha1=true")
98 opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1beta1=true")
99 opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1=true")
100 opts.Authorization.Modes = []string{"AlwaysAllow"}
101 },
102 ModifyServerConfig: func(config *controlplane.Config) {
103
104 config.GenericConfig.LoopbackClientConfig.BearerToken = ""
105 config.GenericConfig.Authentication.Authenticator = authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
106 respMu.RLock()
107 defer respMu.RUnlock()
108 return &authenticator.Response{User: response}, true, nil
109 })
110 },
111 })
112 defer tearDownFn()
113
114 for _, tc := range tests {
115 t.Run(tc.name, func(t *testing.T) {
116 respMu.Lock()
117 response = tc.userInfo
118 respMu.Unlock()
119
120 res, err := kubeClient.AuthenticationV1alpha1().
121 SelfSubjectReviews().
122 Create(tCtx, &authenticationv1alpha1.SelfSubjectReview{}, metav1.CreateOptions{})
123 if err != nil {
124 t.Fatalf("unexpected error: %v", err)
125 }
126
127 if res == nil {
128 t.Fatalf("empty response")
129 }
130
131 if res.Status.UserInfo.Username != tc.expectedName {
132 t.Fatalf("unexpected username: wanted %s, got %s", tc.expectedName, res.Status.UserInfo.Username)
133 }
134
135 if res.Status.UserInfo.UID != tc.expectedUID {
136 t.Fatalf("unexpected uid: wanted %s, got %s", tc.expectedUID, res.Status.UserInfo.UID)
137 }
138
139 if !reflect.DeepEqual(res.Status.UserInfo.Groups, tc.expectedGroups) {
140 t.Fatalf("unexpected groups: wanted %v, got %v", tc.expectedGroups, res.Status.UserInfo.Groups)
141 }
142
143 if !reflect.DeepEqual(res.Status.UserInfo.Extra, tc.expectedExtra) {
144 t.Fatalf("unexpected extra: wanted %v, got %v", tc.expectedExtra, res.Status.UserInfo.Extra)
145 }
146
147 res2, err := kubeClient.AuthenticationV1beta1().
148 SelfSubjectReviews().
149 Create(tCtx, &authenticationv1beta1.SelfSubjectReview{}, metav1.CreateOptions{})
150 if err != nil {
151 t.Fatalf("unexpected error: %v", err)
152 }
153
154 if res2 == nil {
155 t.Fatalf("empty response")
156 }
157
158 if res2.Status.UserInfo.Username != tc.expectedName {
159 t.Fatalf("unexpected username: wanted %s, got %s", tc.expectedName, res.Status.UserInfo.Username)
160 }
161
162 if res2.Status.UserInfo.UID != tc.expectedUID {
163 t.Fatalf("unexpected uid: wanted %s, got %s", tc.expectedUID, res.Status.UserInfo.UID)
164 }
165
166 if !reflect.DeepEqual(res2.Status.UserInfo.Groups, tc.expectedGroups) {
167 t.Fatalf("unexpected groups: wanted %v, got %v", tc.expectedGroups, res.Status.UserInfo.Groups)
168 }
169
170 if !reflect.DeepEqual(res2.Status.UserInfo.Extra, tc.expectedExtra) {
171 t.Fatalf("unexpected extra: wanted %v, got %v", tc.expectedExtra, res.Status.UserInfo.Extra)
172 }
173
174 res3, err := kubeClient.AuthenticationV1().
175 SelfSubjectReviews().
176 Create(context.TODO(), &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
177 if err != nil {
178 t.Fatalf("unexpected error: %v", err)
179 }
180
181 if res3 == nil {
182 t.Fatalf("empty response")
183 }
184
185 if res3.Status.UserInfo.Username != tc.expectedName {
186 t.Fatalf("unexpected username: wanted %s, got %s", tc.expectedName, res.Status.UserInfo.Username)
187 }
188
189 if res3.Status.UserInfo.UID != tc.expectedUID {
190 t.Fatalf("unexpected uid: wanted %s, got %s", tc.expectedUID, res.Status.UserInfo.UID)
191 }
192
193 if !reflect.DeepEqual(res3.Status.UserInfo.Groups, tc.expectedGroups) {
194 t.Fatalf("unexpected groups: wanted %v, got %v", tc.expectedGroups, res.Status.UserInfo.Groups)
195 }
196
197 if !reflect.DeepEqual(res3.Status.UserInfo.Extra, tc.expectedExtra) {
198 t.Fatalf("unexpected extra: wanted %v, got %v", tc.expectedExtra, res.Status.UserInfo.Extra)
199 }
200 })
201 }
202 }
203
204 func TestGetsSelfAttributesError(t *testing.T) {
205 toggle := &atomic.Value{}
206 toggle.Store(true)
207
208 tCtx := ktesting.Init(t)
209 kubeClient, _, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
210 ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
211 opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1alpha1=true")
212 opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1beta1=true")
213 opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1=true")
214 opts.Authorization.Modes = []string{"AlwaysAllow"}
215 },
216 ModifyServerConfig: func(config *controlplane.Config) {
217
218 config.GenericConfig.LoopbackClientConfig.BearerToken = ""
219 config.GenericConfig.Authentication.Authenticator = authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
220 if toggle.Load().(bool) {
221 return &authenticator.Response{
222 User: &user.DefaultInfo{
223 Name: "alice",
224 },
225 }, true, nil
226 }
227
228 return nil, false, fmt.Errorf("test error")
229 })
230 },
231 })
232 defer tearDownFn()
233
234 expected := fmt.Errorf("Unauthorized")
235
236 {
237 toggle.Store(!toggle.Load().(bool))
238
239 _, err := kubeClient.AuthenticationV1alpha1().
240 SelfSubjectReviews().
241 Create(tCtx, &authenticationv1alpha1.SelfSubjectReview{}, metav1.CreateOptions{})
242 if err == nil {
243 t.Fatalf("expected error: %v, got nil", err)
244 }
245
246 toggle.Store(!toggle.Load().(bool))
247 if expected.Error() != err.Error() {
248 t.Fatalf("expected error: %v, got %v", expected, err)
249 }
250 }
251
252 {
253 toggle.Store(!toggle.Load().(bool))
254
255 _, err := kubeClient.AuthenticationV1beta1().
256 SelfSubjectReviews().
257 Create(tCtx, &authenticationv1beta1.SelfSubjectReview{}, metav1.CreateOptions{})
258 if err == nil {
259 t.Fatalf("expected error: %v, got nil", err)
260 }
261
262 toggle.Store(!toggle.Load().(bool))
263 if expected.Error() != err.Error() {
264 t.Fatalf("expected error: %v, got %v", expected, err)
265 }
266 }
267
268 {
269 toggle.Store(!toggle.Load().(bool))
270
271 _, err := kubeClient.AuthenticationV1().
272 SelfSubjectReviews().
273 Create(context.TODO(), &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
274 if err == nil {
275 t.Fatalf("expected error: %v, got nil", err)
276 }
277
278 toggle.Store(!toggle.Load().(bool))
279 if expected.Error() != err.Error() {
280 t.Fatalf("expected error: %v, got %v", expected, err)
281 }
282 }
283 }
284
View as plain text