mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-13 22:05:59 +00:00
add selfsubjectaccessreview API
This commit is contained in:
parent
8675adf138
commit
97529e3687
@ -38,6 +38,10 @@ type SubjectAccessReview struct {
|
|||||||
Status SubjectAccessReviewStatus
|
Status SubjectAccessReviewStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +genclient=true
|
||||||
|
// +nonNamespaced=true
|
||||||
|
// +noMethods=true
|
||||||
|
|
||||||
// SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a
|
// SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a
|
||||||
// spec.namespace means "in all namespaces". Self is a special case, because users should always be able
|
// spec.namespace means "in all namespaces". Self is a special case, because users should always be able
|
||||||
// to check whether they can perform an action
|
// to check whether they can perform an action
|
||||||
|
@ -39,6 +39,10 @@ type SubjectAccessReview struct {
|
|||||||
Status SubjectAccessReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
Status SubjectAccessReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +genclient=true
|
||||||
|
// +nonNamespaced=true
|
||||||
|
// +noMethods=true
|
||||||
|
|
||||||
// SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a
|
// SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a
|
||||||
// spec.namespace means "in all namespaces". Self is a special case, because users should always be able
|
// spec.namespace means "in all namespaces". Self is a special case, because users should always be able
|
||||||
// to check whether they can perform an action
|
// to check whether they can perform an action
|
||||||
|
@ -26,3 +26,8 @@ func (c *FakeSubjectAccessReviews) Create(sar *authorizationapi.SubjectAccessRev
|
|||||||
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("subjectaccessreviews"), sar), &authorizationapi.SubjectAccessReview{})
|
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("subjectaccessreviews"), sar), &authorizationapi.SubjectAccessReview{})
|
||||||
return obj.(*authorizationapi.SubjectAccessReview), err
|
return obj.(*authorizationapi.SubjectAccessReview), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FakeSelfSubjectAccessReviews) Create(sar *authorizationapi.SelfSubjectAccessReview) (result *authorizationapi.SelfSubjectAccessReview, err error) {
|
||||||
|
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("selfsubjectaccessreviews"), sar), &authorizationapi.SelfSubjectAccessReview{})
|
||||||
|
return obj.(*authorizationapi.SelfSubjectAccessReview), err
|
||||||
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 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 unversioned
|
||||||
|
|
||||||
|
import (
|
||||||
|
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SelfSubjectAccessReviewExpansion interface {
|
||||||
|
Create(sar *authorizationapi.SelfSubjectAccessReview) (result *authorizationapi.SelfSubjectAccessReview, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *selfSubjectAccessReviews) Create(sar *authorizationapi.SelfSubjectAccessReview) (result *authorizationapi.SelfSubjectAccessReview, err error) {
|
||||||
|
result = &authorizationapi.SelfSubjectAccessReview{}
|
||||||
|
err = c.client.Post().
|
||||||
|
Resource("selfsubjectaccessreviews").
|
||||||
|
Body(sar).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
@ -20,7 +20,6 @@ import (
|
|||||||
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
|
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The PodExpansion interface allows manually adding extra methods to the PodInterface.
|
|
||||||
type SubjectAccessReviewExpansion interface {
|
type SubjectAccessReviewExpansion interface {
|
||||||
Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error)
|
Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReview, err error)
|
||||||
}
|
}
|
||||||
|
@ -26,3 +26,8 @@ func (c *FakeSubjectAccessReviews) Create(sar *authorizationapi.SubjectAccessRev
|
|||||||
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("subjectaccessreviews"), sar), &authorizationapi.SubjectAccessReview{})
|
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("subjectaccessreviews"), sar), &authorizationapi.SubjectAccessReview{})
|
||||||
return obj.(*authorizationapi.SubjectAccessReview), err
|
return obj.(*authorizationapi.SubjectAccessReview), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FakeSelfSubjectAccessReviews) Create(sar *authorizationapi.SelfSubjectAccessReview) (result *authorizationapi.SelfSubjectAccessReview, err error) {
|
||||||
|
obj, err := c.Fake.Invokes(core.NewRootCreateAction(authorizationapi.SchemeGroupVersion.WithResource("selfsubjectaccessreviews"), sar), &authorizationapi.SelfSubjectAccessReview{})
|
||||||
|
return obj.(*authorizationapi.SelfSubjectAccessReview), err
|
||||||
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 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 v1beta1
|
||||||
|
|
||||||
|
import (
|
||||||
|
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SelfSubjectAccessReviewExpansion interface {
|
||||||
|
Create(sar *authorizationapi.SelfSubjectAccessReview) (result *authorizationapi.SelfSubjectAccessReview, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *selfSubjectAccessReviews) Create(sar *authorizationapi.SelfSubjectAccessReview) (result *authorizationapi.SelfSubjectAccessReview, err error) {
|
||||||
|
result = &authorizationapi.SelfSubjectAccessReview{}
|
||||||
|
err = c.client.Post().
|
||||||
|
Resource("selfsubjectaccessreviews").
|
||||||
|
Body(sar).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
@ -525,12 +525,13 @@ func (gc *GarbageCollector) monitorFor(resource unversioned.GroupVersionResource
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ignoredResources = map[unversioned.GroupVersionResource]struct{}{
|
var ignoredResources = map[unversioned.GroupVersionResource]struct{}{
|
||||||
unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "replicationcontrollers"}: {},
|
unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "replicationcontrollers"}: {},
|
||||||
unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "bindings"}: {},
|
unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "bindings"}: {},
|
||||||
unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "componentstatuses"}: {},
|
unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "componentstatuses"}: {},
|
||||||
unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "events"}: {},
|
unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "events"}: {},
|
||||||
unversioned.GroupVersionResource{Group: "authentication.k8s.io", Version: "v1beta1", Resource: "tokenreviews"}: {},
|
unversioned.GroupVersionResource{Group: "authentication.k8s.io", Version: "v1beta1", Resource: "tokenreviews"}: {},
|
||||||
unversioned.GroupVersionResource{Group: "authorization.k8s.io", Version: "v1beta1", Resource: "subjectaccessreviews"}: {},
|
unversioned.GroupVersionResource{Group: "authorization.k8s.io", Version: "v1beta1", Resource: "subjectaccessreviews"}: {},
|
||||||
|
unversioned.GroupVersionResource{Group: "authorization.k8s.io", Version: "v1beta1", Resource: "selfsubjectaccessreviews"}: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGarbageCollector(metaOnlyClientPool dynamic.ClientPool, clientPool dynamic.ClientPool, resources []unversioned.GroupVersionResource) (*GarbageCollector, error) {
|
func NewGarbageCollector(metaOnlyClientPool dynamic.ClientPool, clientPool dynamic.ClientPool, resources []unversioned.GroupVersionResource) (*GarbageCollector, error) {
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
authorizationv1beta1 "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
|
authorizationv1beta1 "k8s.io/kubernetes/pkg/apis/authorization/v1beta1"
|
||||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||||
|
"k8s.io/kubernetes/pkg/registry/authorization/selfsubjectaccessreview"
|
||||||
"k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview"
|
"k8s.io/kubernetes/pkg/registry/authorization/subjectaccessreview"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,6 +54,9 @@ func (p AuthorizationRESTStorageProvider) v1beta1Storage(apiResourceConfigSource
|
|||||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("subjectaccessreviews")) {
|
if apiResourceConfigSource.ResourceEnabled(version.WithResource("subjectaccessreviews")) {
|
||||||
storage["subjectaccessreviews"] = subjectaccessreview.NewREST(p.Authorizer)
|
storage["subjectaccessreviews"] = subjectaccessreview.NewREST(p.Authorizer)
|
||||||
}
|
}
|
||||||
|
if apiResourceConfigSource.ResourceEnabled(version.WithResource("selfsubjectaccessreviews")) {
|
||||||
|
storage["selfsubjectaccessreviews"] = selfsubjectaccessreview.NewREST(p.Authorizer)
|
||||||
|
}
|
||||||
|
|
||||||
return storage
|
return storage
|
||||||
}
|
}
|
||||||
|
74
pkg/registry/authorization/selfsubjectaccessreview/rest.go
Normal file
74
pkg/registry/authorization/selfsubjectaccessreview/rest.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 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 selfsubjectaccessreview
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
||||||
|
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
|
||||||
|
authorizationvalidation "k8s.io/kubernetes/pkg/apis/authorization/validation"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||||
|
authorizationutil "k8s.io/kubernetes/pkg/registry/authorization/util"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type REST struct {
|
||||||
|
authorizer authorizer.Authorizer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewREST(authorizer authorizer.Authorizer) *REST {
|
||||||
|
return &REST{authorizer}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *REST) New() runtime.Object {
|
||||||
|
return &authorizationapi.SelfSubjectAccessReview{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||||
|
selfSAR, ok := obj.(*authorizationapi.SelfSubjectAccessReview)
|
||||||
|
if !ok {
|
||||||
|
return nil, apierrors.NewBadRequest(fmt.Sprintf("not a SelfSubjectAccessReview: %#v", obj))
|
||||||
|
}
|
||||||
|
if errs := authorizationvalidation.ValidateSelfSubjectAccessReview(selfSAR); len(errs) > 0 {
|
||||||
|
return nil, apierrors.NewInvalid(authorizationapi.Kind(selfSAR.Kind), "", errs)
|
||||||
|
}
|
||||||
|
userToCheck, exists := api.UserFrom(ctx)
|
||||||
|
if !exists {
|
||||||
|
return nil, apierrors.NewBadRequest("no user present on request")
|
||||||
|
}
|
||||||
|
|
||||||
|
var authorizationAttributes authorizer.AttributesRecord
|
||||||
|
if selfSAR.Spec.ResourceAttributes != nil {
|
||||||
|
authorizationAttributes = authorizationutil.ResourceAttributesFrom(userToCheck, *selfSAR.Spec.ResourceAttributes)
|
||||||
|
} else {
|
||||||
|
authorizationAttributes = authorizationutil.NonResourceAttributesFrom(userToCheck, *selfSAR.Spec.NonResourceAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes)
|
||||||
|
|
||||||
|
selfSAR.Status = authorizationapi.SubjectAccessReviewStatus{
|
||||||
|
Allowed: allowed,
|
||||||
|
Reason: reason,
|
||||||
|
}
|
||||||
|
if evaluationErr != nil {
|
||||||
|
selfSAR.Status.EvaluationError = evaluationErr.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return selfSAR, nil
|
||||||
|
}
|
@ -57,7 +57,6 @@ func alwaysAlice(req *http.Request) (user.Info, bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSubjectAccessReview(t *testing.T) {
|
func TestSubjectAccessReview(t *testing.T) {
|
||||||
// Set up a master
|
|
||||||
var m *master.Master
|
var m *master.Master
|
||||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
m.Handler.ServeHTTP(w, req)
|
m.Handler.ServeHTTP(w, req)
|
||||||
@ -153,7 +152,96 @@ func TestSubjectAccessReview(t *testing.T) {
|
|||||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status)
|
t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelfSubjectAccessReview(t *testing.T) {
|
||||||
|
var m *master.Master
|
||||||
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
m.Handler.ServeHTTP(w, req)
|
||||||
|
}))
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
username := "alice"
|
||||||
|
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||||
|
masterConfig.Authenticator = authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
|
||||||
|
return &user.DefaultInfo{Name: username}, true, nil
|
||||||
|
})
|
||||||
|
masterConfig.Authorizer = sarAuthorizer{}
|
||||||
|
masterConfig.AdmissionControl = admit.NewAlwaysAdmit()
|
||||||
|
m, err := master.New(masterConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error in bringing up the master: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}})
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
username string
|
||||||
|
sar *authorizationapi.SelfSubjectAccessReview
|
||||||
|
expectedError string
|
||||||
|
expectedStatus authorizationapi.SubjectAccessReviewStatus
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple allow",
|
||||||
|
username: "alice",
|
||||||
|
sar: &authorizationapi.SelfSubjectAccessReview{
|
||||||
|
Spec: authorizationapi.SelfSubjectAccessReviewSpec{
|
||||||
|
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||||
|
Verb: "list",
|
||||||
|
Group: api.GroupName,
|
||||||
|
Version: "v1",
|
||||||
|
Resource: "pods",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||||
|
Allowed: true,
|
||||||
|
Reason: "you're not dave",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple deny",
|
||||||
|
username: "dave",
|
||||||
|
sar: &authorizationapi.SelfSubjectAccessReview{
|
||||||
|
Spec: authorizationapi.SelfSubjectAccessReviewSpec{
|
||||||
|
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||||
|
Verb: "list",
|
||||||
|
Group: api.GroupName,
|
||||||
|
Version: "v1",
|
||||||
|
Resource: "pods",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||||
|
Allowed: false,
|
||||||
|
Reason: "no",
|
||||||
|
EvaluationError: "I'm sorry, Dave",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
username = test.username
|
||||||
|
|
||||||
|
response, err := clientset.Authorization().SelfSubjectAccessReviews().Create(test.sar)
|
||||||
|
switch {
|
||||||
|
case err == nil && len(test.expectedError) == 0:
|
||||||
|
|
||||||
|
case err != nil && strings.Contains(err.Error(), test.expectedError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
case err != nil && len(test.expectedError) != 0:
|
||||||
|
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if response.Status != test.expectedStatus {
|
||||||
|
t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user