1
16
17 package rbac
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23 "testing"
24
25 rbacv1 "k8s.io/api/rbac/v1"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apiserver/pkg/authentication/user"
28 "k8s.io/apiserver/pkg/authorization/authorizer"
29 rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
30 rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
31 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
32 )
33
34 func newRule(verbs, apiGroups, resources, nonResourceURLs string) rbacv1.PolicyRule {
35 return rbacv1.PolicyRule{
36 Verbs: strings.Split(verbs, ","),
37 APIGroups: strings.Split(apiGroups, ","),
38 Resources: strings.Split(resources, ","),
39 NonResourceURLs: strings.Split(nonResourceURLs, ","),
40 }
41 }
42
43 func newRole(name, namespace string, rules ...rbacv1.PolicyRule) *rbacv1.Role {
44 return &rbacv1.Role{ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}, Rules: rules}
45 }
46
47 func newClusterRole(name string, rules ...rbacv1.PolicyRule) *rbacv1.ClusterRole {
48 return &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: name}, Rules: rules}
49 }
50
51 const (
52 bindToRole uint16 = 0x0
53 bindToClusterRole uint16 = 0x1
54 )
55
56 func newClusterRoleBinding(roleName string, subjects ...string) *rbacv1.ClusterRoleBinding {
57 r := &rbacv1.ClusterRoleBinding{
58 ObjectMeta: metav1.ObjectMeta{},
59 RoleRef: rbacv1.RoleRef{
60 APIGroup: rbacv1.GroupName,
61 Kind: "ClusterRole",
62 Name: roleName,
63 },
64 }
65
66 r.Subjects = make([]rbacv1.Subject, len(subjects))
67 for i, subject := range subjects {
68 split := strings.SplitN(subject, ":", 2)
69 r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1]
70
71 switch r.Subjects[i].Kind {
72 case rbacv1.ServiceAccountKind:
73 r.Subjects[i].APIGroup = ""
74 case rbacv1.UserKind, rbacv1.GroupKind:
75 r.Subjects[i].APIGroup = rbacv1.GroupName
76 default:
77 panic(fmt.Errorf("invalid kind %s", r.Subjects[i].Kind))
78 }
79 }
80 return r
81 }
82
83 func newRoleBinding(namespace, roleName string, bindType uint16, subjects ...string) *rbacv1.RoleBinding {
84 r := &rbacv1.RoleBinding{ObjectMeta: metav1.ObjectMeta{Namespace: namespace}}
85
86 switch bindType {
87 case bindToRole:
88 r.RoleRef = rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: roleName}
89 case bindToClusterRole:
90 r.RoleRef = rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: roleName}
91 }
92
93 r.Subjects = make([]rbacv1.Subject, len(subjects))
94 for i, subject := range subjects {
95 split := strings.SplitN(subject, ":", 2)
96 r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1]
97
98 switch r.Subjects[i].Kind {
99 case rbacv1.ServiceAccountKind:
100 r.Subjects[i].APIGroup = ""
101 case rbacv1.UserKind, rbacv1.GroupKind:
102 r.Subjects[i].APIGroup = rbacv1.GroupName
103 default:
104 panic(fmt.Errorf("invalid kind %s", r.Subjects[i].Kind))
105 }
106 }
107 return r
108 }
109
110 type defaultAttributes struct {
111 user string
112 groups string
113 verb string
114 resource string
115 subresource string
116 namespace string
117 apiGroup string
118 }
119
120 func (d *defaultAttributes) String() string {
121 return fmt.Sprintf("user=(%s), groups=(%s), verb=(%s), resource=(%s), namespace=(%s), apiGroup=(%s)",
122 d.user, strings.Split(d.groups, ","), d.verb, d.resource, d.namespace, d.apiGroup)
123 }
124
125 func (d *defaultAttributes) GetUser() user.Info {
126 return &user.DefaultInfo{Name: d.user, Groups: strings.Split(d.groups, ",")}
127 }
128 func (d *defaultAttributes) GetVerb() string { return d.verb }
129 func (d *defaultAttributes) IsReadOnly() bool { return d.verb == "get" || d.verb == "watch" }
130 func (d *defaultAttributes) GetNamespace() string { return d.namespace }
131 func (d *defaultAttributes) GetResource() string { return d.resource }
132 func (d *defaultAttributes) GetSubresource() string { return d.subresource }
133 func (d *defaultAttributes) GetName() string { return "" }
134 func (d *defaultAttributes) GetAPIGroup() string { return d.apiGroup }
135 func (d *defaultAttributes) GetAPIVersion() string { return "" }
136 func (d *defaultAttributes) IsResourceRequest() bool { return true }
137 func (d *defaultAttributes) GetPath() string { return "" }
138
139 func TestAuthorizer(t *testing.T) {
140 tests := []struct {
141 roles []*rbacv1.Role
142 roleBindings []*rbacv1.RoleBinding
143 clusterRoles []*rbacv1.ClusterRole
144 clusterRoleBindings []*rbacv1.ClusterRoleBinding
145
146 shouldPass []authorizer.Attributes
147 shouldFail []authorizer.Attributes
148 }{
149 {
150 clusterRoles: []*rbacv1.ClusterRole{
151 newClusterRole("admin", newRule("*", "*", "*", "*")),
152 },
153 roleBindings: []*rbacv1.RoleBinding{
154 newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
155 },
156 shouldPass: []authorizer.Attributes{
157 &defaultAttributes{"admin", "", "get", "Pods", "", "ns1", ""},
158 &defaultAttributes{"admin", "", "watch", "Pods", "", "ns1", ""},
159 &defaultAttributes{"admin", "group1", "watch", "Foobar", "", "ns1", ""},
160 &defaultAttributes{"joe", "admins", "watch", "Foobar", "", "ns1", ""},
161 &defaultAttributes{"joe", "group1,admins", "watch", "Foobar", "", "ns1", ""},
162 },
163 shouldFail: []authorizer.Attributes{
164 &defaultAttributes{"admin", "", "GET", "Pods", "", "ns2", ""},
165 &defaultAttributes{"admin", "", "GET", "Nodes", "", "", ""},
166 &defaultAttributes{"admin", "admins", "GET", "Pods", "", "ns2", ""},
167 &defaultAttributes{"admin", "admins", "GET", "Nodes", "", "", ""},
168 },
169 },
170 {
171
172 clusterRoles: []*rbacv1.ClusterRole{
173 newClusterRole("non-resource-url-getter", newRule("get", "", "", "/apis")),
174 newClusterRole("non-resource-url", newRule("*", "", "", "/apis")),
175 newClusterRole("non-resource-url-prefix", newRule("get", "", "", "/apis/*")),
176 },
177 clusterRoleBindings: []*rbacv1.ClusterRoleBinding{
178 newClusterRoleBinding("non-resource-url-getter", "User:foo", "Group:bar"),
179 newClusterRoleBinding("non-resource-url", "User:admin", "Group:admin"),
180 newClusterRoleBinding("non-resource-url-prefix", "User:prefixed", "Group:prefixed"),
181 },
182 shouldPass: []authorizer.Attributes{
183 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "foo"}, Verb: "get", Path: "/apis"},
184 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"bar"}}, Verb: "get", Path: "/apis"},
185 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "admin"}, Verb: "get", Path: "/apis"},
186 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"admin"}}, Verb: "get", Path: "/apis"},
187 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "admin"}, Verb: "watch", Path: "/apis"},
188 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"admin"}}, Verb: "watch", Path: "/apis"},
189
190 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "prefixed"}, Verb: "get", Path: "/apis/v1"},
191 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"prefixed"}}, Verb: "get", Path: "/apis/v1"},
192 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "prefixed"}, Verb: "get", Path: "/apis/v1/foobar"},
193 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"prefixed"}}, Verb: "get", Path: "/apis/v1/foorbar"},
194 },
195 shouldFail: []authorizer.Attributes{
196
197 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "foo"}, Verb: "watch", Path: "/apis"},
198 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"bar"}}, Verb: "watch", Path: "/apis"},
199
200
201 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "foo"}, Verb: "get", Path: "/api/v1"},
202 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"bar"}}, Verb: "get", Path: "/api/v1"},
203 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "admin"}, Verb: "get", Path: "/api/v1"},
204 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"admin"}}, Verb: "get", Path: "/api/v1"},
205
206
207 authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "prefixed"}, Verb: "get", Path: "/api/v1"},
208 authorizer.AttributesRecord{User: &user.DefaultInfo{Groups: []string{"prefixed"}}, Verb: "get", Path: "/api/v1"},
209 },
210 },
211 {
212
213 clusterRoles: []*rbacv1.ClusterRole{
214 newClusterRole("admin", newRule("*", "*", "pods", "*")),
215 },
216 roleBindings: []*rbacv1.RoleBinding{
217 newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
218 },
219 shouldPass: []authorizer.Attributes{
220 &defaultAttributes{"admin", "", "get", "pods", "", "ns1", ""},
221 },
222 shouldFail: []authorizer.Attributes{
223 &defaultAttributes{"admin", "", "get", "pods", "status", "ns1", ""},
224 },
225 },
226 {
227
228 clusterRoles: []*rbacv1.ClusterRole{
229 newClusterRole("admin",
230 newRule("*", "*", "pods/status", "*"),
231 newRule("*", "*", "*/scale", "*"),
232 ),
233 },
234 roleBindings: []*rbacv1.RoleBinding{
235 newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
236 },
237 shouldPass: []authorizer.Attributes{
238 &defaultAttributes{"admin", "", "get", "pods", "status", "ns1", ""},
239 &defaultAttributes{"admin", "", "get", "pods", "scale", "ns1", ""},
240 &defaultAttributes{"admin", "", "get", "deployments", "scale", "ns1", ""},
241 &defaultAttributes{"admin", "", "get", "anything", "scale", "ns1", ""},
242 },
243 shouldFail: []authorizer.Attributes{
244 &defaultAttributes{"admin", "", "get", "pods", "", "ns1", ""},
245 },
246 },
247 }
248 for i, tt := range tests {
249 ruleResolver, _ := rbacregistryvalidation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
250 a := RBACAuthorizer{ruleResolver}
251 for _, attr := range tt.shouldPass {
252 if decision, _, _ := a.Authorize(context.Background(), attr); decision != authorizer.DecisionAllow {
253 t.Errorf("case %d: incorrectly restricted %s", i, attr)
254 }
255 }
256
257 for _, attr := range tt.shouldFail {
258 if decision, _, _ := a.Authorize(context.Background(), attr); decision == authorizer.DecisionAllow {
259 t.Errorf("case %d: incorrectly passed %s", i, attr)
260 }
261 }
262 }
263 }
264
265 func TestRuleMatches(t *testing.T) {
266 tests := []struct {
267 name string
268 rule rbacv1.PolicyRule
269
270 requestsToExpected map[authorizer.AttributesRecord]bool
271 }{
272 {
273 name: "star verb, exact match other",
274 rule: rbacv1helpers.NewRule("*").Groups("group1").Resources("resource1").RuleOrDie(),
275 requestsToExpected: map[authorizer.AttributesRecord]bool{
276 resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
277 resourceRequest("verb1").Group("group2").Resource("resource1").New(): false,
278 resourceRequest("verb1").Group("group1").Resource("resource2").New(): false,
279 resourceRequest("verb1").Group("group2").Resource("resource2").New(): false,
280 resourceRequest("verb2").Group("group1").Resource("resource1").New(): true,
281 resourceRequest("verb2").Group("group2").Resource("resource1").New(): false,
282 resourceRequest("verb2").Group("group1").Resource("resource2").New(): false,
283 resourceRequest("verb2").Group("group2").Resource("resource2").New(): false,
284 },
285 },
286 {
287 name: "star group, exact match other",
288 rule: rbacv1helpers.NewRule("verb1").Groups("*").Resources("resource1").RuleOrDie(),
289 requestsToExpected: map[authorizer.AttributesRecord]bool{
290 resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
291 resourceRequest("verb1").Group("group2").Resource("resource1").New(): true,
292 resourceRequest("verb1").Group("group1").Resource("resource2").New(): false,
293 resourceRequest("verb1").Group("group2").Resource("resource2").New(): false,
294 resourceRequest("verb2").Group("group1").Resource("resource1").New(): false,
295 resourceRequest("verb2").Group("group2").Resource("resource1").New(): false,
296 resourceRequest("verb2").Group("group1").Resource("resource2").New(): false,
297 resourceRequest("verb2").Group("group2").Resource("resource2").New(): false,
298 },
299 },
300 {
301 name: "star resource, exact match other",
302 rule: rbacv1helpers.NewRule("verb1").Groups("group1").Resources("*").RuleOrDie(),
303 requestsToExpected: map[authorizer.AttributesRecord]bool{
304 resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
305 resourceRequest("verb1").Group("group2").Resource("resource1").New(): false,
306 resourceRequest("verb1").Group("group1").Resource("resource2").New(): true,
307 resourceRequest("verb1").Group("group2").Resource("resource2").New(): false,
308 resourceRequest("verb2").Group("group1").Resource("resource1").New(): false,
309 resourceRequest("verb2").Group("group2").Resource("resource1").New(): false,
310 resourceRequest("verb2").Group("group1").Resource("resource2").New(): false,
311 resourceRequest("verb2").Group("group2").Resource("resource2").New(): false,
312 },
313 },
314 {
315 name: "tuple expansion",
316 rule: rbacv1helpers.NewRule("verb1", "verb2").Groups("group1", "group2").Resources("resource1", "resource2").RuleOrDie(),
317 requestsToExpected: map[authorizer.AttributesRecord]bool{
318 resourceRequest("verb1").Group("group1").Resource("resource1").New(): true,
319 resourceRequest("verb1").Group("group2").Resource("resource1").New(): true,
320 resourceRequest("verb1").Group("group1").Resource("resource2").New(): true,
321 resourceRequest("verb1").Group("group2").Resource("resource2").New(): true,
322 resourceRequest("verb2").Group("group1").Resource("resource1").New(): true,
323 resourceRequest("verb2").Group("group2").Resource("resource1").New(): true,
324 resourceRequest("verb2").Group("group1").Resource("resource2").New(): true,
325 resourceRequest("verb2").Group("group2").Resource("resource2").New(): true,
326 },
327 },
328 {
329 name: "subresource expansion",
330 rule: rbacv1helpers.NewRule("*").Groups("*").Resources("resource1/subresource1").RuleOrDie(),
331 requestsToExpected: map[authorizer.AttributesRecord]bool{
332 resourceRequest("verb1").Group("group1").Resource("resource1").Subresource("subresource1").New(): true,
333 resourceRequest("verb1").Group("group2").Resource("resource1").Subresource("subresource2").New(): false,
334 resourceRequest("verb1").Group("group1").Resource("resource2").Subresource("subresource1").New(): false,
335 resourceRequest("verb1").Group("group2").Resource("resource2").Subresource("subresource1").New(): false,
336 resourceRequest("verb2").Group("group1").Resource("resource1").Subresource("subresource1").New(): true,
337 resourceRequest("verb2").Group("group2").Resource("resource1").Subresource("subresource2").New(): false,
338 resourceRequest("verb2").Group("group1").Resource("resource2").Subresource("subresource1").New(): false,
339 resourceRequest("verb2").Group("group2").Resource("resource2").Subresource("subresource1").New(): false,
340 },
341 },
342 {
343 name: "star nonresource, exact match other",
344 rule: rbacv1helpers.NewRule("verb1").URLs("*").RuleOrDie(),
345 requestsToExpected: map[authorizer.AttributesRecord]bool{
346 nonresourceRequest("verb1").URL("/foo").New(): true,
347 nonresourceRequest("verb1").URL("/foo/bar").New(): true,
348 nonresourceRequest("verb1").URL("/foo/baz").New(): true,
349 nonresourceRequest("verb1").URL("/foo/bar/one").New(): true,
350 nonresourceRequest("verb1").URL("/foo/baz/one").New(): true,
351 nonresourceRequest("verb2").URL("/foo").New(): false,
352 nonresourceRequest("verb2").URL("/foo/bar").New(): false,
353 nonresourceRequest("verb2").URL("/foo/baz").New(): false,
354 nonresourceRequest("verb2").URL("/foo/bar/one").New(): false,
355 nonresourceRequest("verb2").URL("/foo/baz/one").New(): false,
356 },
357 },
358 {
359 name: "star nonresource subpath",
360 rule: rbacv1helpers.NewRule("verb1").URLs("/foo/*").RuleOrDie(),
361 requestsToExpected: map[authorizer.AttributesRecord]bool{
362 nonresourceRequest("verb1").URL("/foo").New(): false,
363 nonresourceRequest("verb1").URL("/foo/bar").New(): true,
364 nonresourceRequest("verb1").URL("/foo/baz").New(): true,
365 nonresourceRequest("verb1").URL("/foo/bar/one").New(): true,
366 nonresourceRequest("verb1").URL("/foo/baz/one").New(): true,
367 nonresourceRequest("verb1").URL("/notfoo").New(): false,
368 nonresourceRequest("verb1").URL("/notfoo/bar").New(): false,
369 nonresourceRequest("verb1").URL("/notfoo/baz").New(): false,
370 nonresourceRequest("verb1").URL("/notfoo/bar/one").New(): false,
371 nonresourceRequest("verb1").URL("/notfoo/baz/one").New(): false,
372 },
373 },
374 {
375 name: "star verb, exact nonresource",
376 rule: rbacv1helpers.NewRule("*").URLs("/foo", "/foo/bar/one").RuleOrDie(),
377 requestsToExpected: map[authorizer.AttributesRecord]bool{
378 nonresourceRequest("verb1").URL("/foo").New(): true,
379 nonresourceRequest("verb1").URL("/foo/bar").New(): false,
380 nonresourceRequest("verb1").URL("/foo/baz").New(): false,
381 nonresourceRequest("verb1").URL("/foo/bar/one").New(): true,
382 nonresourceRequest("verb1").URL("/foo/baz/one").New(): false,
383 nonresourceRequest("verb2").URL("/foo").New(): true,
384 nonresourceRequest("verb2").URL("/foo/bar").New(): false,
385 nonresourceRequest("verb2").URL("/foo/baz").New(): false,
386 nonresourceRequest("verb2").URL("/foo/bar/one").New(): true,
387 nonresourceRequest("verb2").URL("/foo/baz/one").New(): false,
388 },
389 },
390 }
391 for _, tc := range tests {
392 for request, expected := range tc.requestsToExpected {
393 if e, a := expected, RuleAllows(request, &tc.rule); e != a {
394 t.Errorf("%q: expected %v, got %v for %v", tc.name, e, a, request)
395 }
396 }
397 }
398 }
399
400 type requestAttributeBuilder struct {
401 request authorizer.AttributesRecord
402 }
403
404 func resourceRequest(verb string) *requestAttributeBuilder {
405 return &requestAttributeBuilder{
406 request: authorizer.AttributesRecord{ResourceRequest: true, Verb: verb},
407 }
408 }
409
410 func nonresourceRequest(verb string) *requestAttributeBuilder {
411 return &requestAttributeBuilder{
412 request: authorizer.AttributesRecord{ResourceRequest: false, Verb: verb},
413 }
414 }
415
416 func (r *requestAttributeBuilder) Group(group string) *requestAttributeBuilder {
417 r.request.APIGroup = group
418 return r
419 }
420
421 func (r *requestAttributeBuilder) Resource(resource string) *requestAttributeBuilder {
422 r.request.Resource = resource
423 return r
424 }
425
426 func (r *requestAttributeBuilder) Subresource(subresource string) *requestAttributeBuilder {
427 r.request.Subresource = subresource
428 return r
429 }
430
431 func (r *requestAttributeBuilder) Name(name string) *requestAttributeBuilder {
432 r.request.Name = name
433 return r
434 }
435
436 func (r *requestAttributeBuilder) URL(url string) *requestAttributeBuilder {
437 r.request.Path = url
438 return r
439 }
440
441 func (r *requestAttributeBuilder) New() authorizer.AttributesRecord {
442 return r.request
443 }
444
445 func BenchmarkAuthorize(b *testing.B) {
446 bootstrapRoles := []rbacv1.ClusterRole{}
447 bootstrapRoles = append(bootstrapRoles, bootstrappolicy.ControllerRoles()...)
448 bootstrapRoles = append(bootstrapRoles, bootstrappolicy.ClusterRoles()...)
449
450 bootstrapBindings := []rbacv1.ClusterRoleBinding{}
451 bootstrapBindings = append(bootstrapBindings, bootstrappolicy.ClusterRoleBindings()...)
452 bootstrapBindings = append(bootstrapBindings, bootstrappolicy.ControllerRoleBindings()...)
453
454 clusterRoles := []*rbacv1.ClusterRole{}
455 for i := range bootstrapRoles {
456 clusterRoles = append(clusterRoles, &bootstrapRoles[i])
457 }
458 clusterRoleBindings := []*rbacv1.ClusterRoleBinding{}
459 for i := range bootstrapBindings {
460 clusterRoleBindings = append(clusterRoleBindings, &bootstrapBindings[i])
461 }
462
463 _, resolver := rbacregistryvalidation.NewTestRuleResolver(nil, nil, clusterRoles, clusterRoleBindings)
464
465 authz := New(resolver, resolver, resolver, resolver)
466
467 nodeUser := &user.DefaultInfo{Name: "system:node:node1", Groups: []string{"system:nodes", "system:authenticated"}}
468 requests := []struct {
469 name string
470 attrs authorizer.Attributes
471 }{
472 {
473 "allow list pods",
474 authorizer.AttributesRecord{
475 ResourceRequest: true,
476 User: nodeUser,
477 Verb: "list",
478 Resource: "pods",
479 Subresource: "",
480 Name: "",
481 Namespace: "",
482 APIGroup: "",
483 APIVersion: "v1",
484 },
485 },
486 {
487 "allow update pods/status",
488 authorizer.AttributesRecord{
489 ResourceRequest: true,
490 User: nodeUser,
491 Verb: "update",
492 Resource: "pods",
493 Subresource: "status",
494 Name: "mypods",
495 Namespace: "myns",
496 APIGroup: "",
497 APIVersion: "v1",
498 },
499 },
500 {
501 "forbid educate dolphins",
502 authorizer.AttributesRecord{
503 ResourceRequest: true,
504 User: nodeUser,
505 Verb: "educate",
506 Resource: "dolphins",
507 Subresource: "",
508 Name: "",
509 Namespace: "",
510 APIGroup: "",
511 APIVersion: "v1",
512 },
513 },
514 }
515
516 b.ResetTimer()
517 for _, request := range requests {
518 b.Run(request.name, func(b *testing.B) {
519 for i := 0; i < b.N; i++ {
520 authz.Authorize(context.Background(), request.attrs)
521 }
522 })
523 }
524 }
525
View as plain text