1
16
17 package abac
18
19 import (
20 "context"
21 "io/ioutil"
22 "os"
23 "reflect"
24 "testing"
25
26 "k8s.io/apimachinery/pkg/runtime"
27 "k8s.io/apiserver/pkg/authentication/user"
28 "k8s.io/apiserver/pkg/authorization/authorizer"
29 "k8s.io/kubernetes/pkg/apis/abac"
30 "k8s.io/kubernetes/pkg/apis/abac/v0"
31 "k8s.io/kubernetes/pkg/apis/abac/v1beta1"
32 )
33
34 func TestEmptyFile(t *testing.T) {
35 _, err := newWithContents(t, "")
36 if err != nil {
37 t.Errorf("unable to read policy file: %v", err)
38 }
39 }
40
41 func TestOneLineFileNoNewLine(t *testing.T) {
42 _, err := newWithContents(t, `{"user":"scheduler", "readonly": true, "resource": "pods", "namespace":"ns1"}`)
43 if err != nil {
44 t.Errorf("unable to read policy file: %v", err)
45 }
46 }
47
48 func TestTwoLineFile(t *testing.T) {
49 _, err := newWithContents(t, `{"user":"scheduler", "readonly": true, "resource": "pods"}
50 {"user":"scheduler", "readonly": true, "resource": "services"}
51 `)
52 if err != nil {
53 t.Errorf("unable to read policy file: %v", err)
54 }
55 }
56
57
58 func TestExampleFile(t *testing.T) {
59 _, err := NewFromFile("./example_policy_file.jsonl")
60 if err != nil {
61 t.Errorf("unable to read policy file: %v", err)
62 }
63 }
64
65 func TestAuthorizeV0(t *testing.T) {
66 a, err := newWithContents(t, `{ "readonly": true, "resource": "events" }
67 {"user":"scheduler", "readonly": true, "resource": "pods" }
68 {"user":"scheduler", "resource": "bindings" }
69 {"user":"kubelet", "readonly": true, "resource": "bindings" }
70 {"user":"kubelet", "resource": "events" }
71 {"user":"alice", "namespace": "projectCaribou"}
72 {"user":"bob", "readonly": true, "namespace": "projectCaribou"}
73 `)
74 if err != nil {
75 t.Fatalf("unable to read policy file: %v", err)
76 }
77
78 authenticatedGroup := []string{user.AllAuthenticated}
79
80 uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1", Groups: authenticatedGroup}
81 uAlice := user.DefaultInfo{Name: "alice", UID: "uid3", Groups: authenticatedGroup}
82 uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5", Groups: authenticatedGroup}
83
84 testCases := []struct {
85 User user.DefaultInfo
86 Verb string
87 Resource string
88 NS string
89 APIGroup string
90 Path string
91 ExpectDecision authorizer.Decision
92 }{
93
94 {User: uScheduler, Verb: "list", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionAllow},
95 {User: uScheduler, Verb: "list", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionAllow},
96
97 {User: uScheduler, Verb: "create", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
98 {User: uScheduler, Verb: "create", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionNoOpinion},
99
100 {User: uScheduler, Verb: "get", Resource: "bindings", NS: "ns1", ExpectDecision: authorizer.DecisionAllow},
101 {User: uScheduler, Verb: "get", Resource: "bindings", NS: "", ExpectDecision: authorizer.DecisionAllow},
102
103
104 {User: uAlice, Verb: "get", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
105 {User: uAlice, Verb: "get", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
106 {User: uAlice, Verb: "get", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
107 {User: uAlice, Verb: "update", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
108 {User: uAlice, Verb: "update", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
109 {User: uAlice, Verb: "update", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
110 {User: uAlice, Verb: "update", Resource: "foo", NS: "projectCaribou", APIGroup: "bar", ExpectDecision: authorizer.DecisionAllow},
111
112 {User: uAlice, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
113 {User: uAlice, Verb: "get", Resource: "widgets", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
114 {User: uAlice, Verb: "get", Resource: "", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
115
116
117 {User: uChuck, Verb: "get", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionAllow},
118 {User: uChuck, Verb: "get", Resource: "events", NS: "", ExpectDecision: authorizer.DecisionAllow},
119
120 {User: uChuck, Verb: "update", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
121 {User: uChuck, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
122 {User: uChuck, Verb: "get", Resource: "floop", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
123
124 {User: uChuck, Verb: "get", Path: "/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion},
125 }
126 for i, tc := range testCases {
127 attr := authorizer.AttributesRecord{
128 User: &tc.User,
129 Verb: tc.Verb,
130 Resource: tc.Resource,
131 Namespace: tc.NS,
132 APIGroup: tc.APIGroup,
133 Path: tc.Path,
134
135 ResourceRequest: len(tc.NS) > 0 || len(tc.Resource) > 0,
136 }
137 decision, _, _ := a.Authorize(context.Background(), attr)
138 if tc.ExpectDecision != decision {
139 t.Logf("tc: %v -> attr %v", tc, attr)
140 t.Errorf("%d: Expected allowed=%v but actually allowed=%v\n\t%v",
141 i, tc.ExpectDecision, decision, tc)
142 }
143 }
144 }
145
146 func getResourceRules(infos []authorizer.ResourceRuleInfo) []authorizer.DefaultResourceRuleInfo {
147 rules := make([]authorizer.DefaultResourceRuleInfo, len(infos))
148 for i, info := range infos {
149 rules[i] = authorizer.DefaultResourceRuleInfo{
150 Verbs: info.GetVerbs(),
151 APIGroups: info.GetAPIGroups(),
152 Resources: info.GetResources(),
153 ResourceNames: info.GetResourceNames(),
154 }
155 }
156 return rules
157 }
158
159 func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizer.DefaultNonResourceRuleInfo {
160 rules := make([]authorizer.DefaultNonResourceRuleInfo, len(infos))
161 for i, info := range infos {
162 rules[i] = authorizer.DefaultNonResourceRuleInfo{
163 Verbs: info.GetVerbs(),
164 NonResourceURLs: info.GetNonResourceURLs(),
165 }
166 }
167 return rules
168 }
169
170 func TestRulesFor(t *testing.T) {
171 a, err := newWithContents(t, `
172 { "readonly": true, "resource": "events" }
173 {"user":"scheduler", "readonly": true, "resource": "pods" }
174 {"user":"scheduler", "resource": "bindings" }
175 {"user":"kubelet", "readonly": true, "resource": "pods" }
176 {"user":"kubelet", "resource": "events" }
177 {"user":"alice", "namespace": "projectCaribou"}
178 {"user":"bob", "readonly": true, "namespace": "projectCaribou"}
179 {"user":"bob", "readonly": true, "nonResourcePath": "*"}
180 {"group":"a", "resource": "bindings" }
181 {"group":"b", "readonly": true, "nonResourcePath": "*"}
182 `)
183 if err != nil {
184 t.Fatalf("unable to read policy file: %v", err)
185 }
186
187 authenticatedGroup := []string{user.AllAuthenticated}
188
189 uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1", Groups: authenticatedGroup}
190 uKubelet := user.DefaultInfo{Name: "kubelet", UID: "uid2", Groups: []string{"a", "b"}}
191 uAlice := user.DefaultInfo{Name: "alice", UID: "uid3", Groups: authenticatedGroup}
192 uBob := user.DefaultInfo{Name: "bob", UID: "uid4", Groups: authenticatedGroup}
193 uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5", Groups: []string{"a", "b"}}
194
195 testCases := []struct {
196 User user.DefaultInfo
197 Namespace string
198 ExpectResourceRules []authorizer.DefaultResourceRuleInfo
199 ExpectNonResourceRules []authorizer.DefaultNonResourceRuleInfo
200 }{
201 {
202 User: uScheduler,
203 Namespace: "ns1",
204 ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
205 {
206 Verbs: []string{"get", "list", "watch"},
207 APIGroups: []string{"*"},
208 Resources: []string{"events"},
209 },
210 {
211 Verbs: []string{"get", "list", "watch"},
212 APIGroups: []string{"*"},
213 Resources: []string{"pods"},
214 },
215 {
216 Verbs: []string{"*"},
217 APIGroups: []string{"*"},
218 Resources: []string{"bindings"},
219 },
220 },
221 ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{},
222 },
223 {
224 User: uKubelet,
225 Namespace: "ns1",
226 ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
227 {
228 Verbs: []string{"get", "list", "watch"},
229 APIGroups: []string{"*"},
230 Resources: []string{"pods"},
231 },
232 {
233 Verbs: []string{"*"},
234 APIGroups: []string{"*"},
235 Resources: []string{"events"},
236 },
237 {
238 Verbs: []string{"*"},
239 APIGroups: []string{"*"},
240 Resources: []string{"bindings"},
241 },
242 {
243 Verbs: []string{"get", "list", "watch"},
244 APIGroups: []string{"*"},
245 Resources: []string{"*"},
246 },
247 },
248 ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{
249 {
250 Verbs: []string{"get", "list", "watch"},
251 NonResourceURLs: []string{"*"},
252 },
253 },
254 },
255 {
256 User: uAlice,
257 Namespace: "projectCaribou",
258 ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
259 {
260 Verbs: []string{"get", "list", "watch"},
261 APIGroups: []string{"*"},
262 Resources: []string{"events"},
263 },
264 {
265 Verbs: []string{"*"},
266 APIGroups: []string{"*"},
267 Resources: []string{"*"},
268 },
269 },
270 ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{},
271 },
272 {
273 User: uBob,
274 Namespace: "projectCaribou",
275 ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
276 {
277 Verbs: []string{"get", "list", "watch"},
278 APIGroups: []string{"*"},
279 Resources: []string{"events"},
280 },
281 {
282 Verbs: []string{"get", "list", "watch"},
283 APIGroups: []string{"*"},
284 Resources: []string{"*"},
285 },
286 {
287 Verbs: []string{"get", "list", "watch"},
288 APIGroups: []string{"*"},
289 Resources: []string{"*"},
290 },
291 },
292 ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{
293 {
294 Verbs: []string{"get", "list", "watch"},
295 NonResourceURLs: []string{"*"},
296 },
297 },
298 },
299 {
300 User: uChuck,
301 Namespace: "ns1",
302 ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{
303 {
304 Verbs: []string{"*"},
305 APIGroups: []string{"*"},
306 Resources: []string{"bindings"},
307 },
308 {
309 Verbs: []string{"get", "list", "watch"},
310 APIGroups: []string{"*"},
311 Resources: []string{"*"},
312 },
313 },
314 ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{
315 {
316 Verbs: []string{"get", "list", "watch"},
317 NonResourceURLs: []string{"*"},
318 },
319 },
320 },
321 }
322 for i, tc := range testCases {
323 attr := authorizer.AttributesRecord{
324 User: &tc.User,
325 Namespace: tc.Namespace,
326 }
327 resourceRules, nonResourceRules, _, _ := a.RulesFor(attr.GetUser(), attr.GetNamespace())
328 actualResourceRules := getResourceRules(resourceRules)
329 if !reflect.DeepEqual(tc.ExpectResourceRules, actualResourceRules) {
330 t.Logf("tc: %v -> attr %v", tc, attr)
331 t.Errorf("%d: Expected: \n%#v\n but actual: \n%#v\n",
332 i, tc.ExpectResourceRules, actualResourceRules)
333 }
334 actualNonResourceRules := getNonResourceRules(nonResourceRules)
335 if !reflect.DeepEqual(tc.ExpectNonResourceRules, actualNonResourceRules) {
336 t.Logf("tc: %v -> attr %v", tc, attr)
337 t.Errorf("%d: Expected: \n%#v\n but actual: \n%#v\n",
338 i, tc.ExpectNonResourceRules, actualNonResourceRules)
339 }
340 }
341 }
342
343 func TestAuthorizeV1beta1(t *testing.T) {
344 a, err := newWithContents(t,
345 `
346 # Comment line, after a blank line
347 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "readonly": true, "nonResourcePath": "/api"}}
348 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "nonResourcePath": "/custom"}}
349 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "nonResourcePath": "/root/*"}}
350 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"noresource", "nonResourcePath": "*"}}
351 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "readonly": true, "resource": "events", "namespace": "*"}}
352 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"scheduler", "readonly": true, "resource": "pods", "namespace": "*"}}
353 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"scheduler", "resource": "bindings", "namespace": "*"}}
354 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"kubelet", "readonly": true, "resource": "bindings", "namespace": "*"}}
355 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"kubelet", "resource": "events", "namespace": "*"}}
356 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"alice", "resource": "*", "namespace": "projectCaribou"}}
357 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"bob", "readonly": true, "resource": "*", "namespace": "projectCaribou"}}
358 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"debbie", "resource": "pods", "namespace": "projectCaribou"}}
359 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"apigroupuser", "resource": "*", "namespace": "projectAnyGroup", "apiGroup": "*"}}
360 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"apigroupuser", "resource": "*", "namespace": "projectEmptyGroup", "apiGroup": "" }}
361 {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"apigroupuser", "resource": "*", "namespace": "projectXGroup", "apiGroup": "x"}}`)
362
363 if err != nil {
364 t.Fatalf("unable to read policy file: %v", err)
365 }
366
367 authenticatedGroup := []string{user.AllAuthenticated}
368
369 uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1", Groups: authenticatedGroup}
370 uAlice := user.DefaultInfo{Name: "alice", UID: "uid3", Groups: authenticatedGroup}
371 uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5", Groups: authenticatedGroup}
372 uDebbie := user.DefaultInfo{Name: "debbie", UID: "uid6", Groups: authenticatedGroup}
373 uNoResource := user.DefaultInfo{Name: "noresource", UID: "uid7", Groups: authenticatedGroup}
374 uAPIGroup := user.DefaultInfo{Name: "apigroupuser", UID: "uid8", Groups: authenticatedGroup}
375
376 testCases := []struct {
377 User user.DefaultInfo
378 Verb string
379 Resource string
380 APIGroup string
381 NS string
382 Path string
383 ExpectDecision authorizer.Decision
384 }{
385
386 {User: uScheduler, Verb: "list", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionAllow},
387 {User: uScheduler, Verb: "list", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionAllow},
388
389 {User: uScheduler, Verb: "create", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
390 {User: uScheduler, Verb: "create", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionNoOpinion},
391
392 {User: uScheduler, Verb: "get", Resource: "bindings", NS: "ns1", ExpectDecision: authorizer.DecisionAllow},
393 {User: uScheduler, Verb: "get", Resource: "bindings", NS: "", ExpectDecision: authorizer.DecisionAllow},
394
395
396 {User: uAlice, Verb: "get", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
397 {User: uAlice, Verb: "get", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
398 {User: uAlice, Verb: "get", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
399 {User: uAlice, Verb: "update", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
400 {User: uAlice, Verb: "update", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
401 {User: uAlice, Verb: "update", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
402
403 {User: uAlice, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
404 {User: uAlice, Verb: "get", Resource: "widgets", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
405 {User: uAlice, Verb: "get", Resource: "", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
406
407
408 {User: uDebbie, Verb: "update", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow},
409
410
411 {User: uChuck, Verb: "get", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionAllow},
412 {User: uChuck, Verb: "get", Resource: "events", NS: "", ExpectDecision: authorizer.DecisionAllow},
413
414 {User: uChuck, Verb: "update", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
415 {User: uChuck, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
416 {User: uChuck, Verb: "get", Resource: "floop", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion},
417
418 {User: uChuck, Verb: "get", Path: "/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion},
419
420 {User: uChuck, Verb: "get", Path: "/api", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow},
421
422 {User: uChuck, Verb: "create", Path: "/api", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion},
423
424 {User: uChuck, Verb: "update", Path: "/custom", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow},
425
426 {User: uChuck, Verb: "get", Path: "/root", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion},
427
428 {User: uChuck, Verb: "get", Path: "/root/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow},
429 {User: uChuck, Verb: "get", Path: "/root/test/1/2/3", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow},
430
431
432 {User: uNoResource, Verb: "get", Path: "", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow},
433 {User: uNoResource, Verb: "get", Path: "/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow},
434 {User: uNoResource, Verb: "get", Path: "/foo/bar/baz", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow},
435
436 {User: uNoResource, Verb: "get", Path: "/", Resource: "", NS: "bar", ExpectDecision: authorizer.DecisionNoOpinion},
437 {User: uNoResource, Verb: "get", Path: "/foo/bar/baz", Resource: "foo", NS: "bar", ExpectDecision: authorizer.DecisionNoOpinion},
438
439
440 {User: uAPIGroup, Verb: "get", APIGroup: "x", Resource: "foo", NS: "projectAnyGroup", ExpectDecision: authorizer.DecisionAllow},
441 {User: uAPIGroup, Verb: "get", APIGroup: "x", Resource: "foo", NS: "projectEmptyGroup", ExpectDecision: authorizer.DecisionNoOpinion},
442 {User: uAPIGroup, Verb: "get", APIGroup: "x", Resource: "foo", NS: "projectXGroup", ExpectDecision: authorizer.DecisionAllow},
443 }
444 for i, tc := range testCases {
445 attr := authorizer.AttributesRecord{
446 User: &tc.User,
447 Verb: tc.Verb,
448 Resource: tc.Resource,
449 APIGroup: tc.APIGroup,
450 Namespace: tc.NS,
451 ResourceRequest: len(tc.NS) > 0 || len(tc.Resource) > 0,
452 Path: tc.Path,
453 }
454
455 decision, _, _ := a.Authorize(context.Background(), attr)
456 if tc.ExpectDecision != decision {
457 t.Errorf("%d: Expected allowed=%v but actually allowed=%v, for case %+v & %+v",
458 i, tc.ExpectDecision, decision, tc, attr)
459 }
460 }
461 }
462
463 func TestSubjectMatches(t *testing.T) {
464 testCases := map[string]struct {
465 User user.DefaultInfo
466 Policy runtime.Object
467 ExpectMatch bool
468 }{
469 "v0 empty policy does not match unauthed user": {
470 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
471 Policy: &v0.Policy{
472 User: "",
473 Group: "",
474 },
475 ExpectMatch: false,
476 },
477 "v0 * user policy does not match unauthed user": {
478 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
479 Policy: &v0.Policy{
480 User: "*",
481 Group: "",
482 },
483 ExpectMatch: false,
484 },
485 "v0 * group policy does not match unauthed user": {
486 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
487 Policy: &v0.Policy{
488 User: "",
489 Group: "*",
490 },
491 ExpectMatch: false,
492 },
493 "v0 empty policy matches authed user": {
494 User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}},
495 Policy: &v0.Policy{
496 User: "",
497 Group: "",
498 },
499 ExpectMatch: true,
500 },
501 "v0 empty policy matches authed user with groups": {
502 User: user.DefaultInfo{Name: "Foo", Groups: []string{"a", "b", user.AllAuthenticated}},
503 Policy: &v0.Policy{
504 User: "",
505 Group: "",
506 },
507 ExpectMatch: true,
508 },
509
510 "v0 user policy does not match unauthed user": {
511 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
512 Policy: &v0.Policy{
513 User: "Foo",
514 Group: "",
515 },
516 ExpectMatch: false,
517 },
518 "v0 user policy does not match different user": {
519 User: user.DefaultInfo{Name: "Bar", Groups: []string{user.AllAuthenticated}},
520 Policy: &v0.Policy{
521 User: "Foo",
522 Group: "",
523 },
524 ExpectMatch: false,
525 },
526 "v0 user policy is case-sensitive": {
527 User: user.DefaultInfo{Name: "foo", Groups: []string{user.AllAuthenticated}},
528 Policy: &v0.Policy{
529 User: "Foo",
530 Group: "",
531 },
532 ExpectMatch: false,
533 },
534 "v0 user policy does not match substring": {
535 User: user.DefaultInfo{Name: "FooBar", Groups: []string{user.AllAuthenticated}},
536 Policy: &v0.Policy{
537 User: "Foo",
538 Group: "",
539 },
540 ExpectMatch: false,
541 },
542 "v0 user policy matches username": {
543 User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}},
544 Policy: &v0.Policy{
545 User: "Foo",
546 Group: "",
547 },
548 ExpectMatch: true,
549 },
550
551 "v0 group policy does not match unauthed user": {
552 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
553 Policy: &v0.Policy{
554 User: "",
555 Group: "Foo",
556 },
557 ExpectMatch: false,
558 },
559 "v0 group policy does not match user in different group": {
560 User: user.DefaultInfo{Name: "FooBar", Groups: []string{"B", user.AllAuthenticated}},
561 Policy: &v0.Policy{
562 User: "",
563 Group: "A",
564 },
565 ExpectMatch: false,
566 },
567 "v0 group policy is case-sensitive": {
568 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
569 Policy: &v0.Policy{
570 User: "",
571 Group: "b",
572 },
573 ExpectMatch: false,
574 },
575 "v0 group policy does not match substring": {
576 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "BBB", "C", user.AllAuthenticated}},
577 Policy: &v0.Policy{
578 User: "",
579 Group: "B",
580 },
581 ExpectMatch: false,
582 },
583 "v0 group policy matches user in group": {
584 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
585 Policy: &v0.Policy{
586 User: "",
587 Group: "B",
588 },
589 ExpectMatch: true,
590 },
591
592 "v0 user and group policy requires user match": {
593 User: user.DefaultInfo{Name: "Bar", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
594 Policy: &v0.Policy{
595 User: "Foo",
596 Group: "B",
597 },
598 ExpectMatch: false,
599 },
600 "v0 user and group policy requires group match": {
601 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
602 Policy: &v0.Policy{
603 User: "Foo",
604 Group: "D",
605 },
606 ExpectMatch: false,
607 },
608 "v0 user and group policy matches": {
609 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
610 Policy: &v0.Policy{
611 User: "Foo",
612 Group: "B",
613 },
614 ExpectMatch: true,
615 },
616
617 "v1 empty policy does not match unauthed user": {
618 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
619 Policy: &v1beta1.Policy{
620 Spec: v1beta1.PolicySpec{
621 User: "",
622 Group: "",
623 },
624 },
625 ExpectMatch: false,
626 },
627 "v1 * user policy does not match unauthed user": {
628 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
629 Policy: &v1beta1.Policy{
630 Spec: v1beta1.PolicySpec{
631 User: "*",
632 Group: "",
633 },
634 },
635 ExpectMatch: false,
636 },
637 "v1 * group policy does not match unauthed user": {
638 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
639 Policy: &v1beta1.Policy{
640 Spec: v1beta1.PolicySpec{
641 User: "",
642 Group: "*",
643 },
644 },
645 ExpectMatch: false,
646 },
647 "v1 empty policy does not match authed user": {
648 User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}},
649 Policy: &v1beta1.Policy{
650 Spec: v1beta1.PolicySpec{
651 User: "",
652 Group: "",
653 },
654 },
655 ExpectMatch: false,
656 },
657 "v1 empty policy does not match authed user with groups": {
658 User: user.DefaultInfo{Name: "Foo", Groups: []string{"a", "b", user.AllAuthenticated}},
659 Policy: &v1beta1.Policy{
660 Spec: v1beta1.PolicySpec{
661 User: "",
662 Group: "",
663 },
664 },
665 ExpectMatch: false,
666 },
667
668 "v1 user policy does not match unauthed user": {
669 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
670 Policy: &v1beta1.Policy{
671 Spec: v1beta1.PolicySpec{
672 User: "Foo",
673 Group: "",
674 },
675 },
676 ExpectMatch: false,
677 },
678 "v1 user policy does not match different user": {
679 User: user.DefaultInfo{Name: "Bar", Groups: []string{user.AllAuthenticated}},
680 Policy: &v1beta1.Policy{
681 Spec: v1beta1.PolicySpec{
682 User: "Foo",
683 Group: "",
684 },
685 },
686 ExpectMatch: false,
687 },
688 "v1 user policy is case-sensitive": {
689 User: user.DefaultInfo{Name: "foo", Groups: []string{user.AllAuthenticated}},
690 Policy: &v1beta1.Policy{
691 Spec: v1beta1.PolicySpec{
692 User: "Foo",
693 Group: "",
694 },
695 },
696 ExpectMatch: false,
697 },
698 "v1 user policy does not match substring": {
699 User: user.DefaultInfo{Name: "FooBar", Groups: []string{user.AllAuthenticated}},
700 Policy: &v1beta1.Policy{
701 Spec: v1beta1.PolicySpec{
702 User: "Foo",
703 Group: "",
704 },
705 },
706 ExpectMatch: false,
707 },
708 "v1 user policy matches username": {
709 User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}},
710 Policy: &v1beta1.Policy{
711 Spec: v1beta1.PolicySpec{
712 User: "Foo",
713 Group: "",
714 },
715 },
716 ExpectMatch: true,
717 },
718
719 "v1 group policy does not match unauthed user": {
720 User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}},
721 Policy: &v1beta1.Policy{
722 Spec: v1beta1.PolicySpec{
723 User: "",
724 Group: "Foo",
725 },
726 },
727 ExpectMatch: false,
728 },
729 "v1 group policy does not match user in different group": {
730 User: user.DefaultInfo{Name: "FooBar", Groups: []string{"B", user.AllAuthenticated}},
731 Policy: &v1beta1.Policy{
732 Spec: v1beta1.PolicySpec{
733 User: "",
734 Group: "A",
735 },
736 },
737 ExpectMatch: false,
738 },
739 "v1 group policy is case-sensitive": {
740 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
741 Policy: &v1beta1.Policy{
742 Spec: v1beta1.PolicySpec{
743 User: "",
744 Group: "b",
745 },
746 },
747 ExpectMatch: false,
748 },
749 "v1 group policy does not match substring": {
750 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "BBB", "C", user.AllAuthenticated}},
751 Policy: &v1beta1.Policy{
752 Spec: v1beta1.PolicySpec{
753 User: "",
754 Group: "B",
755 },
756 },
757 ExpectMatch: false,
758 },
759 "v1 group policy matches user in group": {
760 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
761 Policy: &v1beta1.Policy{
762 Spec: v1beta1.PolicySpec{
763 User: "",
764 Group: "B",
765 },
766 },
767 ExpectMatch: true,
768 },
769
770 "v1 user and group policy requires user match": {
771 User: user.DefaultInfo{Name: "Bar", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
772 Policy: &v1beta1.Policy{
773 Spec: v1beta1.PolicySpec{
774 User: "Foo",
775 Group: "B",
776 },
777 },
778 ExpectMatch: false,
779 },
780 "v1 user and group policy requires group match": {
781 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
782 Policy: &v1beta1.Policy{
783 Spec: v1beta1.PolicySpec{
784 User: "Foo",
785 Group: "D",
786 },
787 },
788 ExpectMatch: false,
789 },
790 "v1 user and group policy matches": {
791 User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}},
792 Policy: &v1beta1.Policy{
793 Spec: v1beta1.PolicySpec{
794 User: "Foo",
795 Group: "B",
796 },
797 },
798 ExpectMatch: true,
799 },
800 }
801
802 for k, tc := range testCases {
803 policy := &abac.Policy{}
804 if err := abac.Scheme.Convert(tc.Policy, policy, nil); err != nil {
805 t.Errorf("%s: error converting: %v", k, err)
806 continue
807 }
808 attr := authorizer.AttributesRecord{
809 User: &tc.User,
810 }
811 actualMatch := subjectMatches(*policy, attr.GetUser())
812 if tc.ExpectMatch != actualMatch {
813 t.Errorf("%v: Expected actorMatches=%v but actually got=%v",
814 k, tc.ExpectMatch, actualMatch)
815 }
816 }
817 }
818
819 func newWithContents(t *testing.T, contents string) (PolicyList, error) {
820 f, err := ioutil.TempFile("", "abac_test")
821 if err != nil {
822 t.Fatalf("unexpected error creating policyfile: %v", err)
823 }
824 f.Close()
825 defer os.Remove(f.Name())
826
827 if err := ioutil.WriteFile(f.Name(), []byte(contents), 0700); err != nil {
828 t.Fatalf("unexpected error writing policyfile: %v", err)
829 }
830
831 pl, err := NewFromFile(f.Name())
832 return pl, err
833 }
834
835 func TestPolicy(t *testing.T) {
836 tests := []struct {
837 policy runtime.Object
838 attr authorizer.Attributes
839 matches bool
840 name string
841 }{
842
843 {
844 policy: &v0.Policy{
845 Readonly: true,
846 },
847 attr: authorizer.AttributesRecord{
848 User: &user.DefaultInfo{
849 Name: "foo",
850 Groups: []string{user.AllAuthenticated},
851 },
852 Verb: "create",
853 },
854 matches: false,
855 name: "v0 read-only mismatch",
856 },
857 {
858 policy: &v0.Policy{
859 User: "foo",
860 },
861 attr: authorizer.AttributesRecord{
862 User: &user.DefaultInfo{
863 Name: "bar",
864 Groups: []string{user.AllAuthenticated},
865 },
866 },
867 matches: false,
868 name: "v0 user name mis-match",
869 },
870 {
871 policy: &v0.Policy{
872 Resource: "foo",
873 },
874 attr: authorizer.AttributesRecord{
875 User: &user.DefaultInfo{
876 Name: "foo",
877 Groups: []string{user.AllAuthenticated},
878 },
879 Resource: "bar",
880 ResourceRequest: true,
881 },
882 matches: false,
883 name: "v0 resource mis-match",
884 },
885 {
886 policy: &v0.Policy{
887 User: "foo",
888 Resource: "foo",
889 Namespace: "foo",
890 },
891 attr: authorizer.AttributesRecord{
892 User: &user.DefaultInfo{
893 Name: "foo",
894 Groups: []string{user.AllAuthenticated},
895 },
896 Resource: "foo",
897 Namespace: "foo",
898 ResourceRequest: true,
899 },
900 matches: true,
901 name: "v0 namespace mis-match",
902 },
903
904
905 {
906 policy: &v0.Policy{},
907 attr: authorizer.AttributesRecord{
908 User: &user.DefaultInfo{
909 Name: "foo",
910 Groups: []string{user.AllAuthenticated},
911 },
912 ResourceRequest: true,
913 },
914 matches: true,
915 name: "v0 null resource",
916 },
917 {
918 policy: &v0.Policy{
919 Readonly: true,
920 },
921 attr: authorizer.AttributesRecord{
922 User: &user.DefaultInfo{
923 Name: "foo",
924 Groups: []string{user.AllAuthenticated},
925 },
926 Verb: "get",
927 },
928 matches: true,
929 name: "v0 read-only match",
930 },
931 {
932 policy: &v0.Policy{
933 User: "foo",
934 },
935 attr: authorizer.AttributesRecord{
936 User: &user.DefaultInfo{
937 Name: "foo",
938 Groups: []string{user.AllAuthenticated},
939 },
940 },
941 matches: true,
942 name: "v0 user name match",
943 },
944 {
945 policy: &v0.Policy{
946 Resource: "foo",
947 },
948 attr: authorizer.AttributesRecord{
949 User: &user.DefaultInfo{
950 Name: "foo",
951 Groups: []string{user.AllAuthenticated},
952 },
953 Resource: "foo",
954 ResourceRequest: true,
955 },
956 matches: true,
957 name: "v0 resource match",
958 },
959
960
961 {
962 policy: &v1beta1.Policy{},
963 attr: authorizer.AttributesRecord{
964 User: &user.DefaultInfo{
965 Name: "foo",
966 Groups: []string{user.AllAuthenticated},
967 },
968 ResourceRequest: true,
969 },
970 matches: false,
971 name: "v1 null",
972 },
973 {
974 policy: &v1beta1.Policy{
975 Spec: v1beta1.PolicySpec{
976 User: "foo",
977 },
978 },
979 attr: authorizer.AttributesRecord{
980 User: &user.DefaultInfo{
981 Name: "bar",
982 Groups: []string{user.AllAuthenticated},
983 },
984 ResourceRequest: true,
985 },
986 matches: false,
987 name: "v1 user name mis-match",
988 },
989 {
990 policy: &v1beta1.Policy{
991 Spec: v1beta1.PolicySpec{
992 User: "*",
993 Readonly: true,
994 },
995 },
996 attr: authorizer.AttributesRecord{
997 User: &user.DefaultInfo{
998 Name: "foo",
999 Groups: []string{user.AllAuthenticated},
1000 },
1001 ResourceRequest: true,
1002 },
1003 matches: false,
1004 name: "v1 read-only mismatch",
1005 },
1006 {
1007 policy: &v1beta1.Policy{
1008 Spec: v1beta1.PolicySpec{
1009 User: "*",
1010 Resource: "foo",
1011 },
1012 },
1013 attr: authorizer.AttributesRecord{
1014 User: &user.DefaultInfo{
1015 Name: "foo",
1016 Groups: []string{user.AllAuthenticated},
1017 },
1018 Resource: "bar",
1019 ResourceRequest: true,
1020 },
1021 matches: false,
1022 name: "v1 resource mis-match",
1023 },
1024 {
1025 policy: &v1beta1.Policy{
1026 Spec: v1beta1.PolicySpec{
1027 User: "foo",
1028 Namespace: "barr",
1029 Resource: "baz",
1030 },
1031 },
1032 attr: authorizer.AttributesRecord{
1033 User: &user.DefaultInfo{
1034 Name: "foo",
1035 Groups: []string{user.AllAuthenticated},
1036 },
1037 Namespace: "bar",
1038 Resource: "baz",
1039 ResourceRequest: true,
1040 },
1041 matches: false,
1042 name: "v1 namespace mis-match",
1043 },
1044 {
1045 policy: &v1beta1.Policy{
1046 Spec: v1beta1.PolicySpec{
1047 User: "*",
1048 NonResourcePath: "/api",
1049 },
1050 },
1051 attr: authorizer.AttributesRecord{
1052 User: &user.DefaultInfo{
1053 Name: "foo",
1054 Groups: []string{user.AllAuthenticated},
1055 },
1056 Path: "/api2",
1057 ResourceRequest: false,
1058 },
1059 matches: false,
1060 name: "v1 non-resource mis-match",
1061 },
1062 {
1063 policy: &v1beta1.Policy{
1064 Spec: v1beta1.PolicySpec{
1065 User: "*",
1066 NonResourcePath: "/api/*",
1067 },
1068 },
1069 attr: authorizer.AttributesRecord{
1070 User: &user.DefaultInfo{
1071 Name: "foo",
1072 Groups: []string{user.AllAuthenticated},
1073 },
1074 Path: "/api2/foo",
1075 ResourceRequest: false,
1076 },
1077 matches: false,
1078 name: "v1 non-resource wildcard subpath mis-match",
1079 },
1080
1081
1082 {
1083 policy: &v1beta1.Policy{
1084 Spec: v1beta1.PolicySpec{
1085 User: "foo",
1086 },
1087 },
1088 attr: authorizer.AttributesRecord{
1089 User: &user.DefaultInfo{
1090 Name: "foo",
1091 Groups: []string{user.AllAuthenticated},
1092 },
1093 ResourceRequest: true,
1094 },
1095 matches: true,
1096 name: "v1 user match",
1097 },
1098 {
1099 policy: &v1beta1.Policy{
1100 Spec: v1beta1.PolicySpec{
1101 User: "*",
1102 },
1103 },
1104 attr: authorizer.AttributesRecord{
1105 User: &user.DefaultInfo{
1106 Name: "foo",
1107 Groups: []string{user.AllAuthenticated},
1108 },
1109 ResourceRequest: true,
1110 },
1111 matches: true,
1112 name: "v1 user wildcard match",
1113 },
1114 {
1115 policy: &v1beta1.Policy{
1116 Spec: v1beta1.PolicySpec{
1117 Group: "bar",
1118 },
1119 },
1120 attr: authorizer.AttributesRecord{
1121 User: &user.DefaultInfo{
1122 Name: "foo",
1123 Groups: []string{"bar", user.AllAuthenticated},
1124 },
1125 ResourceRequest: true,
1126 },
1127 matches: true,
1128 name: "v1 group match",
1129 },
1130 {
1131 policy: &v1beta1.Policy{
1132 Spec: v1beta1.PolicySpec{
1133 Group: "*",
1134 },
1135 },
1136 attr: authorizer.AttributesRecord{
1137 User: &user.DefaultInfo{
1138 Name: "foo",
1139 Groups: []string{"bar", user.AllAuthenticated},
1140 },
1141 ResourceRequest: true,
1142 },
1143 matches: true,
1144 name: "v1 group wildcard match",
1145 },
1146 {
1147 policy: &v1beta1.Policy{
1148 Spec: v1beta1.PolicySpec{
1149 User: "*",
1150 Readonly: true,
1151 },
1152 },
1153 attr: authorizer.AttributesRecord{
1154 User: &user.DefaultInfo{
1155 Name: "foo",
1156 Groups: []string{user.AllAuthenticated},
1157 },
1158 Verb: "get",
1159 ResourceRequest: true,
1160 },
1161 matches: true,
1162 name: "v1 read-only match",
1163 },
1164 {
1165 policy: &v1beta1.Policy{
1166 Spec: v1beta1.PolicySpec{
1167 User: "*",
1168 Resource: "foo",
1169 },
1170 },
1171 attr: authorizer.AttributesRecord{
1172 User: &user.DefaultInfo{
1173 Name: "foo",
1174 Groups: []string{user.AllAuthenticated},
1175 },
1176 Resource: "foo",
1177 ResourceRequest: true,
1178 },
1179 matches: true,
1180 name: "v1 resource match",
1181 },
1182 {
1183 policy: &v1beta1.Policy{
1184 Spec: v1beta1.PolicySpec{
1185 User: "foo",
1186 Namespace: "bar",
1187 Resource: "baz",
1188 },
1189 },
1190 attr: authorizer.AttributesRecord{
1191 User: &user.DefaultInfo{
1192 Name: "foo",
1193 Groups: []string{user.AllAuthenticated},
1194 },
1195 Namespace: "bar",
1196 Resource: "baz",
1197 ResourceRequest: true,
1198 },
1199 matches: true,
1200 name: "v1 namespace match",
1201 },
1202 {
1203 policy: &v1beta1.Policy{
1204 Spec: v1beta1.PolicySpec{
1205 User: "*",
1206 NonResourcePath: "/api",
1207 },
1208 },
1209 attr: authorizer.AttributesRecord{
1210 User: &user.DefaultInfo{
1211 Name: "foo",
1212 Groups: []string{user.AllAuthenticated},
1213 },
1214 Path: "/api",
1215 ResourceRequest: false,
1216 },
1217 matches: true,
1218 name: "v1 non-resource match",
1219 },
1220 {
1221 policy: &v1beta1.Policy{
1222 Spec: v1beta1.PolicySpec{
1223 User: "*",
1224 NonResourcePath: "*",
1225 },
1226 },
1227 attr: authorizer.AttributesRecord{
1228 User: &user.DefaultInfo{
1229 Name: "foo",
1230 Groups: []string{user.AllAuthenticated},
1231 },
1232 Path: "/api",
1233 ResourceRequest: false,
1234 },
1235 matches: true,
1236 name: "v1 non-resource wildcard match",
1237 },
1238 {
1239 policy: &v1beta1.Policy{
1240 Spec: v1beta1.PolicySpec{
1241 User: "*",
1242 NonResourcePath: "/api/*",
1243 },
1244 },
1245 attr: authorizer.AttributesRecord{
1246 User: &user.DefaultInfo{
1247 Name: "foo",
1248 Groups: []string{user.AllAuthenticated},
1249 },
1250 Path: "/api/foo",
1251 ResourceRequest: false,
1252 },
1253 matches: true,
1254 name: "v1 non-resource wildcard subpath match",
1255 },
1256 }
1257 for _, test := range tests {
1258 policy := &abac.Policy{}
1259 if err := abac.Scheme.Convert(test.policy, policy, nil); err != nil {
1260 t.Errorf("%s: error converting: %v", test.name, err)
1261 continue
1262 }
1263 matches := matches(*policy, test.attr)
1264 if test.matches != matches {
1265 t.Errorf("%s: expected: %t, saw: %t", test.name, test.matches, matches)
1266 continue
1267 }
1268 }
1269 }
1270
View as plain text