diff --git a/pkg/apiserver/handlers.go b/pkg/apiserver/handlers.go index 788795d2212..296ea9aa32b 100644 --- a/pkg/apiserver/handlers.go +++ b/pkg/apiserver/handlers.go @@ -367,6 +367,8 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib apiRequestInfo, _ := r.apiRequestInfoResolver.GetAPIRequestInfo(req) + attribs.APIGroup = apiRequestInfo.APIGroup + // If a path follows the conventions of the REST object store, then // we can extract the resource. Otherwise, not. attribs.Resource = apiRequestInfo.Resource diff --git a/pkg/auth/authorizer/interfaces.go b/pkg/auth/authorizer/interfaces.go index b93da19ab14..d64576764d7 100644 --- a/pkg/auth/authorizer/interfaces.go +++ b/pkg/auth/authorizer/interfaces.go @@ -41,6 +41,9 @@ type Attributes interface { // The kind of object, if a request is for a REST object. GetResource() string + + // The group of the resource, if a request is for a REST object. + GetAPIGroup() string } // Authorizer makes an authorization decision based on information gained by making @@ -61,6 +64,7 @@ type AttributesRecord struct { User user.Info ReadOnly bool Namespace string + APIGroup string Resource string } @@ -83,3 +87,7 @@ func (a AttributesRecord) GetNamespace() string { func (a AttributesRecord) GetResource() string { return a.Resource } + +func (a AttributesRecord) GetAPIGroup() string { + return a.APIGroup +} diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index c6cc30b3ebf..54b9f3d64ba 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -792,6 +792,94 @@ func newAuthorizerWithContents(t *testing.T, contents string) authorizer.Authori return pl } +type trackingAuthorizer struct { + requestAttributes []authorizer.Attributes +} + +func (a *trackingAuthorizer) Authorize(attributes authorizer.Attributes) error { + a.requestAttributes = append(a.requestAttributes, attributes) + return nil +} + +// TestAuthorizationAttributeDetermination tests that authorization attributes are built correctly +func TestAuthorizationAttributeDetermination(t *testing.T) { + framework.DeleteAllEtcdKeys() + + etcdStorage, err := framework.NewEtcdStorage() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + trackingAuthorizer := &trackingAuthorizer{} + + var m *master.Master + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + m.Handler.ServeHTTP(w, req) + })) + defer s.Close() + + m = master.New(&master.Config{ + DatabaseStorage: etcdStorage, + KubeletClient: client.FakeKubeletClient{}, + EnableCoreControllers: true, + EnableLogsSupport: false, + EnableUISupport: false, + EnableIndex: true, + APIPrefix: "/api", + Authenticator: getTestTokenAuth(), + Authorizer: trackingAuthorizer, + AdmissionControl: admit.NewAlwaysAdmit(), + StorageVersions: map[string]string{"": testapi.Default.Version()}, + }) + + transport := http.DefaultTransport + + requests := map[string]struct { + verb string + URL string + expectedAttributes authorizer.Attributes + }{ + "prefix/version/resource": {"GET", "/api/v1/pods", authorizer.AttributesRecord{APIGroup: "", Resource: "pods"}}, + "prefix/group/version/resource": {"GET", "/apis/experimental/v1/pods", authorizer.AttributesRecord{APIGroup: "experimental", Resource: "pods"}}, + } + + currentAuthorizationAttributesIndex := 0 + + for testName, r := range requests { + token := BobToken + req, err := http.NewRequest(r.verb, s.URL+r.URL, nil) + if err != nil { + t.Logf("case %v", testName) + t.Fatalf("unexpected error: %v", err) + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + func() { + resp, err := transport.RoundTrip(req) + defer resp.Body.Close() + if err != nil { + t.Logf("case %v", r) + t.Fatalf("unexpected error: %v", err) + } + + found := false + for i := currentAuthorizationAttributesIndex; i < len(trackingAuthorizer.requestAttributes); i++ { + if trackingAuthorizer.requestAttributes[i].GetAPIGroup() == r.expectedAttributes.GetAPIGroup() && + trackingAuthorizer.requestAttributes[i].GetResource() == r.expectedAttributes.GetResource() { + found = true + break + } + + t.Logf("%#v did not match %#v", r.expectedAttributes, trackingAuthorizer.requestAttributes[i].(*authorizer.AttributesRecord)) + } + if !found { + t.Errorf("did not find %#v in %#v", r.expectedAttributes, trackingAuthorizer.requestAttributes[currentAuthorizationAttributesIndex:]) + } + + currentAuthorizationAttributesIndex = len(trackingAuthorizer.requestAttributes) + }() + } +} + // TestNamespaceAuthorization tests that authorization can be controlled // by namespace. func TestNamespaceAuthorization(t *testing.T) {