diff --git a/pkg/apiserver/handlers.go b/pkg/apiserver/handlers.go index 8661b735101..a23b095eeef 100644 --- a/pkg/apiserver/handlers.go +++ b/pkg/apiserver/handlers.go @@ -362,11 +362,10 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib } } - attribs.ReadOnly = IsReadOnlyReq(*req) - apiRequestInfo, _ := r.apiRequestInfoResolver.GetAPIRequestInfo(req) attribs.APIGroup = apiRequestInfo.APIGroup + attribs.Verb = apiRequestInfo.Verb // If a path follows the conventions of the REST object store, then // we can extract the resource. Otherwise, not. @@ -441,7 +440,8 @@ type APIRequestInfoResolver struct { // /api/{version}/watch/namespaces/{namespace}/{resource} func (r *APIRequestInfoResolver) GetAPIRequestInfo(req *http.Request) (APIRequestInfo, error) { requestInfo := APIRequestInfo{ - Raw: splitPath(req.URL.Path), + Raw: splitPath(req.URL.Path), + Verb: strings.ToLower(req.Method), } currentParts := requestInfo.Raw @@ -489,8 +489,9 @@ func (r *APIRequestInfoResolver) GetAPIRequestInfo(req *http.Request) (APIReques requestInfo.Verb = "patch" case "DELETE": requestInfo.Verb = "delete" + default: + requestInfo.Verb = "" } - } // URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind diff --git a/pkg/auth/authorizer/abac/abac_test.go b/pkg/auth/authorizer/abac/abac_test.go index c05fba8ec1a..ef42ade6ca9 100644 --- a/pkg/auth/authorizer/abac/abac_test.go +++ b/pkg/auth/authorizer/abac/abac_test.go @@ -75,49 +75,49 @@ func TestNotAuthorized(t *testing.T) { testCases := []struct { User user.DefaultInfo - RO bool + Verb string Resource string NS string ExpectAllow bool }{ // Scheduler can read pods - {User: uScheduler, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: true}, - {User: uScheduler, RO: true, Resource: "pods", NS: "", ExpectAllow: true}, + {User: uScheduler, Verb: "list", Resource: "pods", NS: "ns1", ExpectAllow: true}, + {User: uScheduler, Verb: "list", Resource: "pods", NS: "", ExpectAllow: true}, // Scheduler cannot write pods - {User: uScheduler, RO: false, Resource: "pods", NS: "ns1", ExpectAllow: false}, - {User: uScheduler, RO: false, Resource: "pods", NS: "", ExpectAllow: false}, + {User: uScheduler, Verb: "create", Resource: "pods", NS: "ns1", ExpectAllow: false}, + {User: uScheduler, Verb: "create", Resource: "pods", NS: "", ExpectAllow: false}, // Scheduler can write bindings - {User: uScheduler, RO: true, Resource: "bindings", NS: "ns1", ExpectAllow: true}, - {User: uScheduler, RO: true, Resource: "bindings", NS: "", ExpectAllow: true}, + {User: uScheduler, Verb: "get", Resource: "bindings", NS: "ns1", ExpectAllow: true}, + {User: uScheduler, Verb: "get", Resource: "bindings", NS: "", ExpectAllow: true}, // Alice can read and write anything in the right namespace. - {User: uAlice, RO: true, Resource: "pods", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: true, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: true, Resource: "", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: false, Resource: "pods", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: false, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: false, Resource: "", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, Verb: "get", Resource: "pods", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, Verb: "get", Resource: "widgets", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, Verb: "get", Resource: "", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, Verb: "update", Resource: "pods", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, Verb: "update", Resource: "widgets", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, Verb: "update", Resource: "", NS: "projectCaribou", ExpectAllow: true}, // .. but not the wrong namespace. - {User: uAlice, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false}, - {User: uAlice, RO: true, Resource: "widgets", NS: "ns1", ExpectAllow: false}, - {User: uAlice, RO: true, Resource: "", NS: "ns1", ExpectAllow: false}, + {User: uAlice, Verb: "get", Resource: "pods", NS: "ns1", ExpectAllow: false}, + {User: uAlice, Verb: "get", Resource: "widgets", NS: "ns1", ExpectAllow: false}, + {User: uAlice, Verb: "get", Resource: "", NS: "ns1", ExpectAllow: false}, // Chuck can read events, since anyone can. - {User: uChuck, RO: true, Resource: "events", NS: "ns1", ExpectAllow: true}, - {User: uChuck, RO: true, Resource: "events", NS: "", ExpectAllow: true}, + {User: uChuck, Verb: "get", Resource: "events", NS: "ns1", ExpectAllow: true}, + {User: uChuck, Verb: "get", Resource: "events", NS: "", ExpectAllow: true}, // Chuck can't do other things. - {User: uChuck, RO: false, Resource: "events", NS: "ns1", ExpectAllow: false}, - {User: uChuck, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false}, - {User: uChuck, RO: true, Resource: "floop", NS: "ns1", ExpectAllow: false}, + {User: uChuck, Verb: "update", Resource: "events", NS: "ns1", ExpectAllow: false}, + {User: uChuck, Verb: "get", Resource: "pods", NS: "ns1", ExpectAllow: false}, + {User: uChuck, Verb: "get", Resource: "floop", NS: "ns1", ExpectAllow: false}, // Chunk can't access things with no kind or namespace // TODO: find a way to give someone access to miscellaneous endpoints, such as // /healthz, /version, etc. - {User: uChuck, RO: true, Resource: "", NS: "", ExpectAllow: false}, + {User: uChuck, Verb: "get", Resource: "", NS: "", ExpectAllow: false}, } for i, tc := range testCases { attr := authorizer.AttributesRecord{ User: &tc.User, - ReadOnly: tc.RO, + Verb: tc.Verb, Resource: tc.Resource, Namespace: tc.NS, } diff --git a/pkg/auth/authorizer/interfaces.go b/pkg/auth/authorizer/interfaces.go index d64576764d7..98b6ad2094a 100644 --- a/pkg/auth/authorizer/interfaces.go +++ b/pkg/auth/authorizer/interfaces.go @@ -32,6 +32,10 @@ type Attributes interface { // authentication occurred. GetGroups() []string + // GetVerb returns the kube verb associated with API requests (this includes get, list, watch, create, update, patch, delete, and proxy), + // or the lowercased HTTP verb associated with non-API requests (this includes get, put, post, patch, and delete) + GetVerb() string + // When IsReadOnly() == true, the request has no side effects, other than // caching, logging, and other incidentals. IsReadOnly() bool @@ -62,7 +66,7 @@ func (f AuthorizerFunc) Authorize(a Attributes) error { // AttributesRecord implements Attributes interface. type AttributesRecord struct { User user.Info - ReadOnly bool + Verb string Namespace string APIGroup string Resource string @@ -76,8 +80,12 @@ func (a AttributesRecord) GetGroups() []string { return a.User.GetGroups() } +func (a AttributesRecord) GetVerb() string { + return a.Verb +} + func (a AttributesRecord) IsReadOnly() bool { - return a.ReadOnly + return a.Verb == "get" || a.Verb == "list" || a.Verb == "watch" } func (a AttributesRecord) GetNamespace() string {