Plumb subresource through subjectaccessreview
This commit is contained in:
		| @@ -5,6 +5,7 @@ licenses(["notice"]) | |||||||
| load( | load( | ||||||
|     "@io_bazel_rules_go//go:def.bzl", |     "@io_bazel_rules_go//go:def.bzl", | ||||||
|     "go_library", |     "go_library", | ||||||
|  |     "go_test", | ||||||
| ) | ) | ||||||
|  |  | ||||||
| go_library( | go_library( | ||||||
| @@ -34,3 +35,16 @@ filegroup( | |||||||
|     srcs = [":package-srcs"], |     srcs = [":package-srcs"], | ||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | go_test( | ||||||
|  |     name = "go_default_test", | ||||||
|  |     srcs = ["rest_test.go"], | ||||||
|  |     library = ":go_default_library", | ||||||
|  |     tags = ["automanaged"], | ||||||
|  |     deps = [ | ||||||
|  |         "//pkg/apis/authorization:go_default_library", | ||||||
|  |         "//vendor:k8s.io/apiserver/pkg/authentication/user", | ||||||
|  |         "//vendor:k8s.io/apiserver/pkg/authorization/authorizer", | ||||||
|  |         "//vendor:k8s.io/apiserver/pkg/endpoints/request", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|   | |||||||
							
								
								
									
										198
									
								
								pkg/registry/authorization/subjectaccessreview/rest_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								pkg/registry/authorization/subjectaccessreview/rest_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2017 The Kubernetes Authors. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package subjectaccessreview | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"reflect" | ||||||
|  |  | ||||||
|  | 	"k8s.io/apiserver/pkg/authentication/user" | ||||||
|  | 	"k8s.io/apiserver/pkg/authorization/authorizer" | ||||||
|  | 	genericapirequest "k8s.io/apiserver/pkg/endpoints/request" | ||||||
|  | 	authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type fakeAuthorizer struct { | ||||||
|  | 	attrs authorizer.Attributes | ||||||
|  |  | ||||||
|  | 	ok     bool | ||||||
|  | 	reason string | ||||||
|  | 	err    error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *fakeAuthorizer) Authorize(attrs authorizer.Attributes) (bool, string, error) { | ||||||
|  | 	f.attrs = attrs | ||||||
|  | 	return f.ok, f.reason, f.err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestCreate(t *testing.T) { | ||||||
|  | 	testcases := map[string]struct { | ||||||
|  | 		spec   authorizationapi.SubjectAccessReviewSpec | ||||||
|  | 		ok     bool | ||||||
|  | 		reason string | ||||||
|  | 		err    error | ||||||
|  |  | ||||||
|  | 		expectedErr    string | ||||||
|  | 		expectedAttrs  authorizer.Attributes | ||||||
|  | 		expectedStatus authorizationapi.SubjectAccessReviewStatus | ||||||
|  | 	}{ | ||||||
|  | 		"empty": { | ||||||
|  | 			expectedErr: "nonResourceAttributes or resourceAttributes", | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"nonresource rejected": { | ||||||
|  | 			spec: authorizationapi.SubjectAccessReviewSpec{ | ||||||
|  | 				User: "bob", | ||||||
|  | 				NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"}, | ||||||
|  | 			}, | ||||||
|  | 			ok:     false, | ||||||
|  | 			reason: "myreason", | ||||||
|  | 			err:    errors.New("myerror"), | ||||||
|  | 			expectedAttrs: authorizer.AttributesRecord{ | ||||||
|  | 				User:            &user.DefaultInfo{Name: "bob"}, | ||||||
|  | 				Verb:            "get", | ||||||
|  | 				Path:            "/mypath", | ||||||
|  | 				ResourceRequest: false, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: authorizationapi.SubjectAccessReviewStatus{ | ||||||
|  | 				Allowed:         false, | ||||||
|  | 				Reason:          "myreason", | ||||||
|  | 				EvaluationError: "myerror", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"nonresource allowed": { | ||||||
|  | 			spec: authorizationapi.SubjectAccessReviewSpec{ | ||||||
|  | 				User: "bob", | ||||||
|  | 				NonResourceAttributes: &authorizationapi.NonResourceAttributes{Verb: "get", Path: "/mypath"}, | ||||||
|  | 			}, | ||||||
|  | 			ok:     true, | ||||||
|  | 			reason: "allowed", | ||||||
|  | 			err:    nil, | ||||||
|  | 			expectedAttrs: authorizer.AttributesRecord{ | ||||||
|  | 				User:            &user.DefaultInfo{Name: "bob"}, | ||||||
|  | 				Verb:            "get", | ||||||
|  | 				Path:            "/mypath", | ||||||
|  | 				ResourceRequest: false, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: authorizationapi.SubjectAccessReviewStatus{ | ||||||
|  | 				Allowed:         true, | ||||||
|  | 				Reason:          "allowed", | ||||||
|  | 				EvaluationError: "", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"resource rejected": { | ||||||
|  | 			spec: authorizationapi.SubjectAccessReviewSpec{ | ||||||
|  | 				User: "bob", | ||||||
|  | 				ResourceAttributes: &authorizationapi.ResourceAttributes{ | ||||||
|  | 					Namespace:   "myns", | ||||||
|  | 					Verb:        "create", | ||||||
|  | 					Group:       "extensions", | ||||||
|  | 					Version:     "v1beta1", | ||||||
|  | 					Resource:    "deployments", | ||||||
|  | 					Subresource: "scale", | ||||||
|  | 					Name:        "mydeployment", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			ok:     false, | ||||||
|  | 			reason: "myreason", | ||||||
|  | 			err:    errors.New("myerror"), | ||||||
|  | 			expectedAttrs: authorizer.AttributesRecord{ | ||||||
|  | 				User:            &user.DefaultInfo{Name: "bob"}, | ||||||
|  | 				Namespace:       "myns", | ||||||
|  | 				Verb:            "create", | ||||||
|  | 				APIGroup:        "extensions", | ||||||
|  | 				APIVersion:      "v1beta1", | ||||||
|  | 				Resource:        "deployments", | ||||||
|  | 				Subresource:     "scale", | ||||||
|  | 				Name:            "mydeployment", | ||||||
|  | 				ResourceRequest: true, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: authorizationapi.SubjectAccessReviewStatus{ | ||||||
|  | 				Allowed:         false, | ||||||
|  | 				Reason:          "myreason", | ||||||
|  | 				EvaluationError: "myerror", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"resource allowed": { | ||||||
|  | 			spec: authorizationapi.SubjectAccessReviewSpec{ | ||||||
|  | 				User: "bob", | ||||||
|  | 				ResourceAttributes: &authorizationapi.ResourceAttributes{ | ||||||
|  | 					Namespace:   "myns", | ||||||
|  | 					Verb:        "create", | ||||||
|  | 					Group:       "extensions", | ||||||
|  | 					Version:     "v1beta1", | ||||||
|  | 					Resource:    "deployments", | ||||||
|  | 					Subresource: "scale", | ||||||
|  | 					Name:        "mydeployment", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			ok:     true, | ||||||
|  | 			reason: "allowed", | ||||||
|  | 			err:    nil, | ||||||
|  | 			expectedAttrs: authorizer.AttributesRecord{ | ||||||
|  | 				User:            &user.DefaultInfo{Name: "bob"}, | ||||||
|  | 				Namespace:       "myns", | ||||||
|  | 				Verb:            "create", | ||||||
|  | 				APIGroup:        "extensions", | ||||||
|  | 				APIVersion:      "v1beta1", | ||||||
|  | 				Resource:        "deployments", | ||||||
|  | 				Subresource:     "scale", | ||||||
|  | 				Name:            "mydeployment", | ||||||
|  | 				ResourceRequest: true, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: authorizationapi.SubjectAccessReviewStatus{ | ||||||
|  | 				Allowed:         true, | ||||||
|  | 				Reason:          "allowed", | ||||||
|  | 				EvaluationError: "", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for k, tc := range testcases { | ||||||
|  | 		auth := &fakeAuthorizer{ | ||||||
|  | 			ok:     tc.ok, | ||||||
|  | 			reason: tc.reason, | ||||||
|  | 			err:    tc.err, | ||||||
|  | 		} | ||||||
|  | 		rest := NewREST(auth) | ||||||
|  |  | ||||||
|  | 		result, err := rest.Create(genericapirequest.NewContext(), &authorizationapi.SubjectAccessReview{Spec: tc.spec}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			if tc.expectedErr != "" { | ||||||
|  | 				if !strings.Contains(err.Error(), tc.expectedErr) { | ||||||
|  | 					t.Errorf("%s: expected %s to contain %q", k, err, tc.expectedErr) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				t.Errorf("%s: %v", k, err) | ||||||
|  | 			} | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(auth.attrs, tc.expectedAttrs) { | ||||||
|  | 			t.Errorf("%s: expected\n%#v\ngot\n%#v", k, tc.expectedAttrs, auth.attrs) | ||||||
|  | 		} | ||||||
|  | 		status := result.(*authorizationapi.SubjectAccessReview).Status | ||||||
|  | 		if !reflect.DeepEqual(status, tc.expectedStatus) { | ||||||
|  | 			t.Errorf("%s: expected\n%#v\ngot\n%#v", k, tc.expectedStatus, status) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -5,6 +5,7 @@ licenses(["notice"]) | |||||||
| load( | load( | ||||||
|     "@io_bazel_rules_go//go:def.bzl", |     "@io_bazel_rules_go//go:def.bzl", | ||||||
|     "go_library", |     "go_library", | ||||||
|  |     "go_test", | ||||||
| ) | ) | ||||||
|  |  | ||||||
| go_library( | go_library( | ||||||
| @@ -30,3 +31,15 @@ filegroup( | |||||||
|     srcs = [":package-srcs"], |     srcs = [":package-srcs"], | ||||||
|     tags = ["automanaged"], |     tags = ["automanaged"], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | go_test( | ||||||
|  |     name = "go_default_test", | ||||||
|  |     srcs = ["helpers_test.go"], | ||||||
|  |     library = ":go_default_library", | ||||||
|  |     tags = ["automanaged"], | ||||||
|  |     deps = [ | ||||||
|  |         "//pkg/apis/authorization:go_default_library", | ||||||
|  |         "//vendor:k8s.io/apimachinery/pkg/util/sets", | ||||||
|  |         "//vendor:k8s.io/apiserver/pkg/authorization/authorizer", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|   | |||||||
| @@ -29,7 +29,10 @@ func ResourceAttributesFrom(user user.Info, in authorizationapi.ResourceAttribut | |||||||
| 		Verb:            in.Verb, | 		Verb:            in.Verb, | ||||||
| 		Namespace:       in.Namespace, | 		Namespace:       in.Namespace, | ||||||
| 		APIGroup:        in.Group, | 		APIGroup:        in.Group, | ||||||
|  | 		APIVersion:      in.Version, | ||||||
| 		Resource:        in.Resource, | 		Resource:        in.Resource, | ||||||
|  | 		Subresource:     in.Subresource, | ||||||
|  | 		Name:            in.Name, | ||||||
| 		ResourceRequest: true, | 		ResourceRequest: true, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								pkg/registry/authorization/util/helpers_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								pkg/registry/authorization/util/helpers_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2017 The Kubernetes Authors. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package util | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/sets" | ||||||
|  | 	"k8s.io/apiserver/pkg/authorization/authorizer" | ||||||
|  | 	authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestResourceAttributesFrom(t *testing.T) { | ||||||
|  | 	knownResourceAttributesNames := sets.NewString( | ||||||
|  | 		// Fields we copy in ResourceAttributesFrom | ||||||
|  | 		"Verb", | ||||||
|  | 		"Namespace", | ||||||
|  | 		"Group", | ||||||
|  | 		"Version", | ||||||
|  | 		"Resource", | ||||||
|  | 		"Subresource", | ||||||
|  | 		"Name", | ||||||
|  |  | ||||||
|  | 		// Fields we copy in NonResourceAttributesFrom | ||||||
|  | 		"Path", | ||||||
|  | 		"Verb", | ||||||
|  | 	) | ||||||
|  | 	reflect.TypeOf(authorizationapi.ResourceAttributes{}).FieldByNameFunc(func(name string) bool { | ||||||
|  | 		if !knownResourceAttributesNames.Has(name) { | ||||||
|  | 			t.Errorf("authorizationapi.ResourceAttributes has a new field: %q. Add to ResourceAttributesFrom/NonResourceAttributesFrom as appropriate, then add to knownResourceAttributesNames", name) | ||||||
|  | 		} | ||||||
|  | 		return false | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	knownAttributesRecordFieldNames := sets.NewString( | ||||||
|  | 		// Fields we set in ResourceAttributesFrom | ||||||
|  | 		"User", | ||||||
|  | 		"Verb", | ||||||
|  | 		"Namespace", | ||||||
|  | 		"APIGroup", | ||||||
|  | 		"APIVersion", | ||||||
|  | 		"Resource", | ||||||
|  | 		"Subresource", | ||||||
|  | 		"Name", | ||||||
|  | 		"ResourceRequest", | ||||||
|  |  | ||||||
|  | 		// Fields we set in NonResourceAttributesFrom | ||||||
|  | 		"User", | ||||||
|  | 		"ResourceRequest", | ||||||
|  | 		"Path", | ||||||
|  | 		"Verb", | ||||||
|  | 	) | ||||||
|  | 	reflect.TypeOf(authorizer.AttributesRecord{}).FieldByNameFunc(func(name string) bool { | ||||||
|  | 		if !knownAttributesRecordFieldNames.Has(name) { | ||||||
|  | 			t.Errorf("authorizer.AttributesRecord has a new field: %q. Add to ResourceAttributesFrom/NonResourceAttributesFrom as appropriate, then add to knownAttributesRecordFieldNames", name) | ||||||
|  | 		} | ||||||
|  | 		return false | ||||||
|  | 	}) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Jordan Liggitt
					Jordan Liggitt