Plumb subresource through subjectaccessreview
This commit is contained in:
		| @@ -5,6 +5,7 @@ licenses(["notice"]) | ||||
| load( | ||||
|     "@io_bazel_rules_go//go:def.bzl", | ||||
|     "go_library", | ||||
|     "go_test", | ||||
| ) | ||||
|  | ||||
| go_library( | ||||
| @@ -34,3 +35,16 @@ filegroup( | ||||
|     srcs = [":package-srcs"], | ||||
|     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( | ||||
|     "@io_bazel_rules_go//go:def.bzl", | ||||
|     "go_library", | ||||
|     "go_test", | ||||
| ) | ||||
|  | ||||
| go_library( | ||||
| @@ -30,3 +31,15 @@ filegroup( | ||||
|     srcs = [":package-srcs"], | ||||
|     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, | ||||
| 		Namespace:       in.Namespace, | ||||
| 		APIGroup:        in.Group, | ||||
| 		APIVersion:      in.Version, | ||||
| 		Resource:        in.Resource, | ||||
| 		Subresource:     in.Subresource, | ||||
| 		Name:            in.Name, | ||||
| 		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