From 87ff84a7b0d7e6ab927880576b7703516881fbbf Mon Sep 17 00:00:00 2001 From: deads2k Date: Thu, 22 Sep 2016 14:59:52 -0400 Subject: [PATCH 1/2] add system:discovery role --- plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go index 2540d3b59f5..592b5f8668f 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go @@ -32,11 +32,19 @@ var ( func ClusterRoles() []rbac.ClusterRole { return []rbac.ClusterRole{ { + // a "root" role which can do absolutely anything ObjectMeta: api.ObjectMeta{Name: "cluster-admin"}, Rules: []rbac.PolicyRule{ rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(), rbac.NewRule("*").URLs("*").RuleOrDie(), }, }, + { + // a role which provides just enough power to discovery API versions for negotiation + ObjectMeta: api.ObjectMeta{Name: "system:discovery"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get").URLs("/version", "/api", "/api/*", "/apis", "/apis/*").RuleOrDie(), + }, + }, } } From 4e2f8196958acbdf36fcd18cee3fa65526d877e1 Mon Sep 17 00:00:00 2001 From: deads2k Date: Fri, 23 Sep 2016 11:17:18 -0400 Subject: [PATCH 2/2] add tests proving rbac rule matches --- pkg/generated/openapi/zz_generated.openapi.go | 17 ++ plugin/pkg/auth/authorizer/rbac/rbac_test.go | 180 ++++++++++++++++++ 2 files changed, 197 insertions(+) diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 81c45f096f5..6102f4d5f0e 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -5142,6 +5142,23 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{ Dependencies: []string{ "runtime.Object"}, }, + "rbac.PolicyRuleBuilder": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PolicyRuleBuilder let's us attach methods. A no-no for API types. We use it to construct rules in code. It's more compact than trying to write them out in a literal and allows us to perform some basic checking during construction", + Properties: map[string]spec.Schema{ + "PolicyRule": { + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef("#/definitions/rbac.PolicyRule"), + }, + }, + }, + Required: []string{"PolicyRule"}, + }, + }, + Dependencies: []string{ + "rbac.PolicyRule"}, + }, "rbac.Role": { Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ diff --git a/plugin/pkg/auth/authorizer/rbac/rbac_test.go b/plugin/pkg/auth/authorizer/rbac/rbac_test.go index adeebac6b33..63f8a4d3f2e 100644 --- a/plugin/pkg/auth/authorizer/rbac/rbac_test.go +++ b/plugin/pkg/auth/authorizer/rbac/rbac_test.go @@ -236,3 +236,183 @@ func TestAuthorizer(t *testing.T) { } } } + +func TestRuleMatches(t *testing.T) { + tests := []struct { + name string + rule rbac.PolicyRule + + requestsToExpected map[authorizer.AttributesRecord]bool + }{ + { + name: "star verb, exact match other", + rule: rbac.NewRule("*").Groups("group1").Resources("resource1").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, + resourceRequest("verb1").Group("group2").Resource("resource1").New(): false, + resourceRequest("verb1").Group("group1").Resource("resource2").New(): false, + resourceRequest("verb1").Group("group2").Resource("resource2").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource1").New(): true, + resourceRequest("verb2").Group("group2").Resource("resource1").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource2").New(): false, + resourceRequest("verb2").Group("group2").Resource("resource2").New(): false, + }, + }, + { + name: "star group, exact match other", + rule: rbac.NewRule("verb1").Groups("*").Resources("resource1").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, + resourceRequest("verb1").Group("group2").Resource("resource1").New(): true, + resourceRequest("verb1").Group("group1").Resource("resource2").New(): false, + resourceRequest("verb1").Group("group2").Resource("resource2").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource1").New(): false, + resourceRequest("verb2").Group("group2").Resource("resource1").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource2").New(): false, + resourceRequest("verb2").Group("group2").Resource("resource2").New(): false, + }, + }, + { + name: "star resource, exact match other", + rule: rbac.NewRule("verb1").Groups("group1").Resources("*").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, + resourceRequest("verb1").Group("group2").Resource("resource1").New(): false, + resourceRequest("verb1").Group("group1").Resource("resource2").New(): true, + resourceRequest("verb1").Group("group2").Resource("resource2").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource1").New(): false, + resourceRequest("verb2").Group("group2").Resource("resource1").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource2").New(): false, + resourceRequest("verb2").Group("group2").Resource("resource2").New(): false, + }, + }, + { + name: "tuple expansion", + rule: rbac.NewRule("verb1", "verb2").Groups("group1", "group2").Resources("resource1", "resource2").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, + resourceRequest("verb1").Group("group2").Resource("resource1").New(): true, + resourceRequest("verb1").Group("group1").Resource("resource2").New(): true, + resourceRequest("verb1").Group("group2").Resource("resource2").New(): true, + resourceRequest("verb2").Group("group1").Resource("resource1").New(): true, + resourceRequest("verb2").Group("group2").Resource("resource1").New(): true, + resourceRequest("verb2").Group("group1").Resource("resource2").New(): true, + resourceRequest("verb2").Group("group2").Resource("resource2").New(): true, + }, + }, + { + name: "subresource expansion", + rule: rbac.NewRule("*").Groups("*").Resources("resource1/subresource1").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + resourceRequest("verb1").Group("group1").Resource("resource1").Subresource("subresource1").New(): true, + resourceRequest("verb1").Group("group2").Resource("resource1").Subresource("subresource2").New(): false, + resourceRequest("verb1").Group("group1").Resource("resource2").Subresource("subresource1").New(): false, + resourceRequest("verb1").Group("group2").Resource("resource2").Subresource("subresource1").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource1").Subresource("subresource1").New(): true, + resourceRequest("verb2").Group("group2").Resource("resource1").Subresource("subresource2").New(): false, + resourceRequest("verb2").Group("group1").Resource("resource2").Subresource("subresource1").New(): false, + resourceRequest("verb2").Group("group2").Resource("resource2").Subresource("subresource1").New(): false, + }, + }, + { + name: "star nonresource, exact match other", + rule: rbac.NewRule("verb1").URLs("*").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + nonresourceRequest("verb1").URL("/foo").New(): true, + nonresourceRequest("verb1").URL("/foo/bar").New(): true, + nonresourceRequest("verb1").URL("/foo/baz").New(): true, + nonresourceRequest("verb1").URL("/foo/bar/one").New(): true, + nonresourceRequest("verb1").URL("/foo/baz/one").New(): true, + nonresourceRequest("verb2").URL("/foo").New(): false, + nonresourceRequest("verb2").URL("/foo/bar").New(): false, + nonresourceRequest("verb2").URL("/foo/baz").New(): false, + nonresourceRequest("verb2").URL("/foo/bar/one").New(): false, + nonresourceRequest("verb2").URL("/foo/baz/one").New(): false, + }, + }, + { + name: "star nonresource subpath", + rule: rbac.NewRule("verb1").URLs("/foo/*").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + nonresourceRequest("verb1").URL("/foo").New(): false, + nonresourceRequest("verb1").URL("/foo/bar").New(): true, + nonresourceRequest("verb1").URL("/foo/baz").New(): true, + nonresourceRequest("verb1").URL("/foo/bar/one").New(): true, + nonresourceRequest("verb1").URL("/foo/baz/one").New(): true, + nonresourceRequest("verb1").URL("/notfoo").New(): false, + nonresourceRequest("verb1").URL("/notfoo/bar").New(): false, + nonresourceRequest("verb1").URL("/notfoo/baz").New(): false, + nonresourceRequest("verb1").URL("/notfoo/bar/one").New(): false, + nonresourceRequest("verb1").URL("/notfoo/baz/one").New(): false, + }, + }, + { + name: "star verb, exact nonresource", + rule: rbac.NewRule("*").URLs("/foo", "/foo/bar/one").RuleOrDie(), + requestsToExpected: map[authorizer.AttributesRecord]bool{ + nonresourceRequest("verb1").URL("/foo").New(): true, + nonresourceRequest("verb1").URL("/foo/bar").New(): false, + nonresourceRequest("verb1").URL("/foo/baz").New(): false, + nonresourceRequest("verb1").URL("/foo/bar/one").New(): true, + nonresourceRequest("verb1").URL("/foo/baz/one").New(): false, + nonresourceRequest("verb2").URL("/foo").New(): true, + nonresourceRequest("verb2").URL("/foo/bar").New(): false, + nonresourceRequest("verb2").URL("/foo/baz").New(): false, + nonresourceRequest("verb2").URL("/foo/bar/one").New(): true, + nonresourceRequest("verb2").URL("/foo/baz/one").New(): false, + }, + }, + } + for _, tc := range tests { + for request, expected := range tc.requestsToExpected { + if e, a := expected, RuleAllows(request, tc.rule); e != a { + t.Errorf("%q: expected %v, got %v for %v", tc.name, e, a, request) + } + } + } +} + +type requestAttributeBuilder struct { + request authorizer.AttributesRecord +} + +func resourceRequest(verb string) *requestAttributeBuilder { + return &requestAttributeBuilder{ + request: authorizer.AttributesRecord{ResourceRequest: true, Verb: verb}, + } +} + +func nonresourceRequest(verb string) *requestAttributeBuilder { + return &requestAttributeBuilder{ + request: authorizer.AttributesRecord{ResourceRequest: false, Verb: verb}, + } +} + +func (r *requestAttributeBuilder) Group(group string) *requestAttributeBuilder { + r.request.APIGroup = group + return r +} + +func (r *requestAttributeBuilder) Resource(resource string) *requestAttributeBuilder { + r.request.Resource = resource + return r +} + +func (r *requestAttributeBuilder) Subresource(subresource string) *requestAttributeBuilder { + r.request.Subresource = subresource + return r +} + +func (r *requestAttributeBuilder) Name(name string) *requestAttributeBuilder { + r.request.Name = name + return r +} + +func (r *requestAttributeBuilder) URL(url string) *requestAttributeBuilder { + r.request.Path = url + return r +} + +func (r *requestAttributeBuilder) New() authorizer.AttributesRecord { + return r.request +}