mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 02:43:36 +00:00
vendor files
This commit is contained in:
61
vendor/k8s.io/kubernetes/pkg/registry/rbac/BUILD
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/registry/rbac/BUILD
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"escalation_check.go",
|
||||
"helpers.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/registry/rbac/clusterrole:all-srcs",
|
||||
"//pkg/registry/rbac/clusterrolebinding:all-srcs",
|
||||
"//pkg/registry/rbac/reconciliation:all-srcs",
|
||||
"//pkg/registry/rbac/rest:all-srcs",
|
||||
"//pkg/registry/rbac/role:all-srcs",
|
||||
"//pkg/registry/rbac/rolebinding:all-srcs",
|
||||
"//pkg/registry/rbac/validation:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["helpers_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
],
|
||||
)
|
4
vendor/k8s.io/kubernetes/pkg/registry/rbac/OWNERS
generated
vendored
Executable file
4
vendor/k8s.io/kubernetes/pkg/registry/rbac/OWNERS
generated
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
reviewers:
|
||||
- deads2k
|
||||
- liggitt
|
||||
- enj
|
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/BUILD
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"registry.go",
|
||||
"strategy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/clusterrole",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/registry/rbac/clusterrole/policybased:all-srcs",
|
||||
"//pkg/registry/rbac/clusterrole/storage:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing ClusterRole objects.
|
||||
package clusterrole // import "k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
|
35
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased/BUILD
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased",
|
||||
deps = [
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
103
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased/storage.go
generated
vendored
Normal file
103
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased/storage.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
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 policybased implements a standard storage for ClusterRole that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("clusterroles")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
|
||||
return &Storage{s, ruleResolver}
|
||||
}
|
||||
|
||||
var fullAuthority = []rbac.PolicyRule{
|
||||
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
|
||||
rbac.NewRule("*").URLs("*").RuleOrDie(),
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized)
|
||||
}
|
||||
|
||||
clusterRole := obj.(*rbac.ClusterRole)
|
||||
rules := clusterRole.Rules
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err)
|
||||
}
|
||||
// to set the aggregation rule, since it can gather anything, requires * on *.*
|
||||
if hasAggregationRule(clusterRole) {
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, fullAuthority); err != nil {
|
||||
return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule"))
|
||||
}
|
||||
}
|
||||
|
||||
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
|
||||
}
|
||||
|
||||
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
clusterRole := obj.(*rbac.ClusterRole)
|
||||
oldClusterRole := oldObj.(*rbac.ClusterRole)
|
||||
|
||||
// if we're only mutating fields needed for the GC to eventually delete this obj, return
|
||||
if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
rules := clusterRole.Rules
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err)
|
||||
}
|
||||
// to change the aggregation rule, since it can gather anything and prevent tightening, requires * on *.*
|
||||
if hasAggregationRule(clusterRole) || hasAggregationRule(oldClusterRole) {
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, fullAuthority); err != nil {
|
||||
return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule"))
|
||||
}
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
|
||||
}
|
||||
|
||||
func hasAggregationRule(clusterRole *rbac.ClusterRole) bool {
|
||||
return clusterRole.AggregationRule != nil && len(clusterRole.AggregationRule.ClusterRoleSelectors) > 0
|
||||
}
|
92
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/registry.go
generated
vendored
Normal file
92
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/registry.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 clusterrole
|
||||
|
||||
import (
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store ClusterRoles.
|
||||
type Registry interface {
|
||||
ListClusterRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.ClusterRoleList, error)
|
||||
CreateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc) error
|
||||
UpdateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error
|
||||
GetClusterRole(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.ClusterRole, error)
|
||||
DeleteClusterRole(ctx genericapirequest.Context, name string) error
|
||||
WatchClusterRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListClusterRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.ClusterRoleList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc) error {
|
||||
_, err := s.Create(ctx, clusterRole, createValidation, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateClusterRole(ctx genericapirequest.Context, clusterRole *rbac.ClusterRole, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error {
|
||||
_, _, err := s.Update(ctx, clusterRole.Name, rest.DefaultUpdatedObjectInfo(clusterRole), createValidation, updateValidation)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchClusterRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetClusterRole(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.ClusterRole, error) {
|
||||
obj, err := s.Get(ctx, name, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRole), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteClusterRole(ctx genericapirequest.Context, name string) error {
|
||||
_, _, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// AuthorizerAdapter adapts the registry to the authorizer interface
|
||||
type AuthorizerAdapter struct {
|
||||
Registry Registry
|
||||
}
|
||||
|
||||
func (a AuthorizerAdapter) GetClusterRole(name string) (*rbac.ClusterRole, error) {
|
||||
return a.Registry.GetClusterRole(genericapirequest.NewContext(), name, &metav1.GetOptions{})
|
||||
}
|
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage/BUILD
generated
vendored
Normal file
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage/BUILD
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage/storage.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage/storage.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for ClusterRole
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against ClusterRole objects.
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.ClusterRole{} },
|
||||
NewListFunc: func() runtime.Object { return &rbac.ClusterRoleList{} },
|
||||
DefaultQualifiedResource: rbac.Resource("clusterroles"),
|
||||
|
||||
CreateStrategy: clusterrole.Strategy,
|
||||
UpdateStrategy: clusterrole.Strategy,
|
||||
DeleteStrategy: clusterrole.Strategy,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/strategy.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/strategy.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 clusterrole
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
)
|
||||
|
||||
// strategy implements behavior for ClusterRoles
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// ClusterRole objects.
|
||||
var Strategy = strategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for ClusterRoles.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for ClusterRoles.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRole)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
|
||||
newClusterRole := obj.(*rbac.ClusterRole)
|
||||
oldClusterRole := old.(*rbac.ClusterRole)
|
||||
|
||||
_, _ = newClusterRole, oldClusterRole
|
||||
}
|
||||
|
||||
// Validate validates a new ClusterRole. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
|
||||
clusterRole := obj.(*rbac.ClusterRole)
|
||||
return validation.ValidateClusterRole(clusterRole)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRole)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.ClusterRole)
|
||||
errorList := validation.ValidateClusterRole(newObj)
|
||||
return append(errorList, validation.ValidateClusterRoleUpdate(newObj, old.(*rbac.ClusterRole))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/BUILD
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"registry.go",
|
||||
"strategy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/registry/rbac/clusterrolebinding/policybased:all-srcs",
|
||||
"//pkg/registry/rbac/clusterrolebinding/storage:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing ClusterRoleBinding objects.
|
||||
package clusterrolebinding // import "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
|
37
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased/BUILD
generated
vendored
Normal file
37
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased/BUILD
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased",
|
||||
deps = [
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
97
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased/storage.go
generated
vendored
Normal file
97
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased/storage.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
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 policybased implements a standard storage for ClusterRoleBinding that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("clusterrolebindings")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
authorizer authorizer.Authorizer
|
||||
|
||||
ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
|
||||
return &Storage{s, authorizer, ruleResolver}
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
if rbacregistry.BindingAuthorized(ctx, clusterRoleBinding.RoleRef, metav1.NamespaceNone, s.authorizer) {
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(clusterRoleBinding.RoleRef, metav1.NamespaceNone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err)
|
||||
}
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
|
||||
}
|
||||
|
||||
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
|
||||
// if we're only mutating fields needed for the GC to eventually delete this obj, return
|
||||
if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// if we're explicitly authorized to bind this clusterrole, return
|
||||
if rbacregistry.BindingAuthorized(ctx, clusterRoleBinding.RoleRef, metav1.NamespaceNone, s.authorizer) {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// Otherwise, see if we already have all the permissions contained in the referenced clusterrole
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(clusterRoleBinding.RoleRef, metav1.NamespaceNone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err)
|
||||
}
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
|
||||
}
|
101
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/registry.go
generated
vendored
Normal file
101
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/registry.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
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 clusterrolebinding
|
||||
|
||||
import (
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store ClusterRoleBindings.
|
||||
type Registry interface {
|
||||
ListClusterRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.ClusterRoleBindingList, error)
|
||||
CreateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc) error
|
||||
UpdateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error
|
||||
GetClusterRoleBinding(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.ClusterRoleBinding, error)
|
||||
DeleteClusterRoleBinding(ctx genericapirequest.Context, name string) error
|
||||
WatchClusterRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListClusterRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.ClusterRoleBindingList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleBindingList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc) error {
|
||||
_, err := s.Create(ctx, clusterRoleBinding, createValidation, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateClusterRoleBinding(ctx genericapirequest.Context, clusterRoleBinding *rbac.ClusterRoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error {
|
||||
_, _, err := s.Update(ctx, clusterRoleBinding.Name, rest.DefaultUpdatedObjectInfo(clusterRoleBinding), createValidation, updateValidation)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchClusterRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetClusterRoleBinding(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.ClusterRoleBinding, error) {
|
||||
obj, err := s.Get(ctx, name, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRoleBinding), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteClusterRoleBinding(ctx genericapirequest.Context, name string) error {
|
||||
_, _, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// AuthorizerAdapter adapts the registry to the authorizer interface
|
||||
type AuthorizerAdapter struct {
|
||||
Registry Registry
|
||||
}
|
||||
|
||||
func (a AuthorizerAdapter) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
|
||||
list, err := a.Registry.ListClusterRoleBindings(genericapirequest.NewContext(), &metainternalversion.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := []*rbac.ClusterRoleBinding{}
|
||||
for i := range list.Items {
|
||||
ret = append(ret, &list.Items[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage/BUILD
generated
vendored
Normal file
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage/BUILD
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage/storage.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage/storage.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for ClusterRoleBinding
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against ClusterRoleBinding objects.
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.ClusterRoleBinding{} },
|
||||
NewListFunc: func() runtime.Object { return &rbac.ClusterRoleBindingList{} },
|
||||
DefaultQualifiedResource: rbac.Resource("clusterrolebindings"),
|
||||
|
||||
CreateStrategy: clusterrolebinding.Strategy,
|
||||
UpdateStrategy: clusterrolebinding.Strategy,
|
||||
DeleteStrategy: clusterrolebinding.Strategy,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/strategy.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/strategy.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 clusterrolebinding
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
)
|
||||
|
||||
// strategy implements behavior for ClusterRoleBindings
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// ClusterRoleBinding objects.
|
||||
var Strategy = strategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for ClusterRoleBindings.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for ClusterRoleBindings.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRoleBinding)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
|
||||
newClusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
oldClusterRoleBinding := old.(*rbac.ClusterRoleBinding)
|
||||
|
||||
_, _ = newClusterRoleBinding, oldClusterRoleBinding
|
||||
}
|
||||
|
||||
// Validate validates a new ClusterRoleBinding. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
return validation.ValidateClusterRoleBinding(clusterRoleBinding)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRoleBinding)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.ClusterRoleBinding)
|
||||
errorList := validation.ValidateClusterRoleBinding(newObj)
|
||||
return append(errorList, validation.ValidateClusterRoleBindingUpdate(newObj, old.(*rbac.ClusterRoleBinding))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
90
vendor/k8s.io/kubernetes/pkg/registry/rbac/escalation_check.go
generated
vendored
Normal file
90
vendor/k8s.io/kubernetes/pkg/registry/rbac/escalation_check.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
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 rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
func EscalationAllowed(ctx genericapirequest.Context) bool {
|
||||
u, ok := genericapirequest.UserFrom(ctx)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// system:masters is special because the API server uses it for privileged loopback connections
|
||||
// therefore we know that a member of system:masters can always do anything
|
||||
for _, group := range u.GetGroups() {
|
||||
if group == user.SystemPrivilegedGroup {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// BindingAuthorized returns true if the user associated with the context is explicitly authorized to bind the specified roleRef
|
||||
func BindingAuthorized(ctx genericapirequest.Context, roleRef rbac.RoleRef, bindingNamespace string, a authorizer.Authorizer) bool {
|
||||
if a == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
user, ok := genericapirequest.UserFrom(ctx)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
attrs := authorizer.AttributesRecord{
|
||||
User: user,
|
||||
Verb: "bind",
|
||||
// check against the namespace where the binding is being created (or the empty namespace for clusterrolebindings).
|
||||
// this allows delegation to bind particular clusterroles in rolebindings within particular namespaces,
|
||||
// and to authorize binding a clusterrole across all namespaces in a clusterrolebinding.
|
||||
Namespace: bindingNamespace,
|
||||
ResourceRequest: true,
|
||||
}
|
||||
|
||||
// This occurs after defaulting and conversion, so values pulled from the roleRef won't change
|
||||
// Invalid APIGroup or Name values will fail validation
|
||||
switch roleRef.Kind {
|
||||
case "ClusterRole":
|
||||
attrs.APIGroup = roleRef.APIGroup
|
||||
attrs.Resource = "clusterroles"
|
||||
attrs.Name = roleRef.Name
|
||||
case "Role":
|
||||
attrs.APIGroup = roleRef.APIGroup
|
||||
attrs.Resource = "roles"
|
||||
attrs.Name = roleRef.Name
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
decision, _, err := a.Authorize(attrs)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf(
|
||||
"error authorizing user %#v to bind %#v in namespace %s: %v",
|
||||
user, roleRef, bindingNamespace, err,
|
||||
))
|
||||
}
|
||||
return decision == authorizer.DecisionAllow
|
||||
}
|
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/helpers.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/helpers.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 rbac
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// IsOnlyMutatingGCFields checks finalizers and ownerrefs which GC manipulates
|
||||
// and indicates that only those fields are changing
|
||||
func IsOnlyMutatingGCFields(obj, old runtime.Object, equalities conversion.Equalities) bool {
|
||||
if old == nil || reflect.ValueOf(old).IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
// make a copy of the newObj so that we can stomp for comparison
|
||||
copied := obj.DeepCopyObject()
|
||||
copiedMeta, err := meta.Accessor(copied)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
oldMeta, err := meta.Accessor(old)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
copiedMeta.SetOwnerReferences(oldMeta.GetOwnerReferences())
|
||||
copiedMeta.SetFinalizers(oldMeta.GetFinalizers())
|
||||
copiedMeta.SetSelfLink(oldMeta.GetSelfLink())
|
||||
|
||||
return equalities.DeepEqual(copied, old)
|
||||
}
|
152
vendor/k8s.io/kubernetes/pkg/registry/rbac/helpers_test.go
generated
vendored
Normal file
152
vendor/k8s.io/kubernetes/pkg/registry/rbac/helpers_test.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
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 rbac
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kapi "k8s.io/kubernetes/pkg/apis/core"
|
||||
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
)
|
||||
|
||||
func newPod() *kapi.Pod {
|
||||
return &kapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
Name: "foo",
|
||||
OwnerReferences: []metav1.OwnerReference{},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIsOnlyMutatingGCFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
obj func() runtime.Object
|
||||
old func() runtime.Object
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "same",
|
||||
obj: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "only annotations",
|
||||
obj: func() runtime.Object {
|
||||
obj := newPod()
|
||||
obj.Annotations["foo"] = "bar"
|
||||
return obj
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "only other",
|
||||
obj: func() runtime.Object {
|
||||
obj := newPod()
|
||||
obj.Spec.RestartPolicy = kapi.RestartPolicyAlways
|
||||
return obj
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "only ownerRef",
|
||||
obj: func() runtime.Object {
|
||||
obj := newPod()
|
||||
obj.OwnerReferences = append(obj.OwnerReferences, metav1.OwnerReference{Name: "foo"})
|
||||
return obj
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "ownerRef and finalizer",
|
||||
obj: func() runtime.Object {
|
||||
obj := newPod()
|
||||
obj.OwnerReferences = append(obj.OwnerReferences, metav1.OwnerReference{Name: "foo"})
|
||||
obj.Finalizers = []string{"final"}
|
||||
return obj
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "and annotations",
|
||||
obj: func() runtime.Object {
|
||||
obj := newPod()
|
||||
obj.OwnerReferences = append(obj.OwnerReferences, metav1.OwnerReference{Name: "foo"})
|
||||
obj.Annotations["foo"] = "bar"
|
||||
return obj
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "and other",
|
||||
obj: func() runtime.Object {
|
||||
obj := newPod()
|
||||
obj.OwnerReferences = append(obj.OwnerReferences, metav1.OwnerReference{Name: "foo"})
|
||||
obj.Spec.RestartPolicy = kapi.RestartPolicyAlways
|
||||
return obj
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return newPod()
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "and nil",
|
||||
obj: func() runtime.Object {
|
||||
obj := newPod()
|
||||
obj.OwnerReferences = append(obj.OwnerReferences, metav1.OwnerReference{Name: "foo"})
|
||||
obj.Spec.RestartPolicy = kapi.RestartPolicyAlways
|
||||
return obj
|
||||
},
|
||||
old: func() runtime.Object {
|
||||
return (*kapi.Pod)(nil)
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
actual := IsOnlyMutatingGCFields(tc.obj(), tc.old(), kapihelper.Semantic)
|
||||
if tc.expected != actual {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, tc.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
62
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/BUILD
generated
vendored
Normal file
62
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/BUILD
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"reconcile_role_test.go",
|
||||
"reconcile_rolebindings_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/reconciliation",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"clusterrole_interfaces.go",
|
||||
"clusterrolebinding_interfaces.go",
|
||||
"reconcile_role.go",
|
||||
"reconcile_rolebindings.go",
|
||||
"role_interfaces.go",
|
||||
"rolebinding_interfaces.go",
|
||||
"zz_generated.deepcopy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/reconciliation",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
104
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/clusterrole_interfaces.go
generated
vendored
Normal file
104
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/clusterrole_interfaces.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/pkg/registry/rbac/reconciliation.RuleOwner
|
||||
// +k8s:deepcopy-gen:nonpointer-interfaces=true
|
||||
type ClusterRoleRuleOwner struct {
|
||||
ClusterRole *rbac.ClusterRole
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) GetObject() runtime.Object {
|
||||
return o.ClusterRole
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) GetNamespace() string {
|
||||
return o.ClusterRole.Namespace
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) GetName() string {
|
||||
return o.ClusterRole.Name
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) GetLabels() map[string]string {
|
||||
return o.ClusterRole.Labels
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) SetLabels(in map[string]string) {
|
||||
o.ClusterRole.Labels = in
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) GetAnnotations() map[string]string {
|
||||
return o.ClusterRole.Annotations
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) SetAnnotations(in map[string]string) {
|
||||
o.ClusterRole.Annotations = in
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) GetRules() []rbac.PolicyRule {
|
||||
return o.ClusterRole.Rules
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) SetRules(in []rbac.PolicyRule) {
|
||||
o.ClusterRole.Rules = in
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) GetAggregationRule() *rbac.AggregationRule {
|
||||
return o.ClusterRole.AggregationRule
|
||||
}
|
||||
|
||||
func (o ClusterRoleRuleOwner) SetAggregationRule(in *rbac.AggregationRule) {
|
||||
o.ClusterRole.AggregationRule = in
|
||||
}
|
||||
|
||||
type ClusterRoleModifier struct {
|
||||
Client internalversion.ClusterRoleInterface
|
||||
}
|
||||
|
||||
func (c ClusterRoleModifier) Get(namespace, name string) (RuleOwner, error) {
|
||||
ret, err := c.Client.Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClusterRoleRuleOwner{ClusterRole: ret}, err
|
||||
}
|
||||
|
||||
func (c ClusterRoleModifier) Create(in RuleOwner) (RuleOwner, error) {
|
||||
ret, err := c.Client.Create(in.(ClusterRoleRuleOwner).ClusterRole)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClusterRoleRuleOwner{ClusterRole: ret}, err
|
||||
}
|
||||
|
||||
func (c ClusterRoleModifier) Update(in RuleOwner) (RuleOwner, error) {
|
||||
ret, err := c.Client.Update(in.(ClusterRoleRuleOwner).ClusterRole)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClusterRoleRuleOwner{ClusterRole: ret}, err
|
||||
|
||||
}
|
109
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/clusterrolebinding_interfaces.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/clusterrolebinding_interfaces.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/pkg/registry/rbac/reconciliation.RoleBinding
|
||||
// +k8s:deepcopy-gen:nonpointer-interfaces=true
|
||||
type ClusterRoleBindingAdapter struct {
|
||||
ClusterRoleBinding *rbac.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetObject() runtime.Object {
|
||||
return o.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetNamespace() string {
|
||||
return o.ClusterRoleBinding.Namespace
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetName() string {
|
||||
return o.ClusterRoleBinding.Name
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetUID() types.UID {
|
||||
return o.ClusterRoleBinding.UID
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetLabels() map[string]string {
|
||||
return o.ClusterRoleBinding.Labels
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) SetLabels(in map[string]string) {
|
||||
o.ClusterRoleBinding.Labels = in
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetAnnotations() map[string]string {
|
||||
return o.ClusterRoleBinding.Annotations
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) SetAnnotations(in map[string]string) {
|
||||
o.ClusterRoleBinding.Annotations = in
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetRoleRef() rbac.RoleRef {
|
||||
return o.ClusterRoleBinding.RoleRef
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) GetSubjects() []rbac.Subject {
|
||||
return o.ClusterRoleBinding.Subjects
|
||||
}
|
||||
|
||||
func (o ClusterRoleBindingAdapter) SetSubjects(in []rbac.Subject) {
|
||||
o.ClusterRoleBinding.Subjects = in
|
||||
}
|
||||
|
||||
type ClusterRoleBindingClientAdapter struct {
|
||||
Client internalversion.ClusterRoleBindingInterface
|
||||
}
|
||||
|
||||
func (c ClusterRoleBindingClientAdapter) Get(namespace, name string) (RoleBinding, error) {
|
||||
ret, err := c.Client.Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClusterRoleBindingAdapter{ClusterRoleBinding: ret}, err
|
||||
}
|
||||
|
||||
func (c ClusterRoleBindingClientAdapter) Create(in RoleBinding) (RoleBinding, error) {
|
||||
ret, err := c.Client.Create(in.(ClusterRoleBindingAdapter).ClusterRoleBinding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClusterRoleBindingAdapter{ClusterRoleBinding: ret}, err
|
||||
}
|
||||
|
||||
func (c ClusterRoleBindingClientAdapter) Update(in RoleBinding) (RoleBinding, error) {
|
||||
ret, err := c.Client.Update(in.(ClusterRoleBindingAdapter).ClusterRoleBinding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClusterRoleBindingAdapter{ClusterRoleBinding: ret}, err
|
||||
|
||||
}
|
||||
|
||||
func (c ClusterRoleBindingClientAdapter) Delete(namespace, name string, uid types.UID) error {
|
||||
return c.Client.Delete(name, &metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &uid}})
|
||||
}
|
281
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_role.go
generated
vendored
Normal file
281
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_role.go
generated
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
type ReconcileOperation string
|
||||
|
||||
var (
|
||||
ReconcileCreate ReconcileOperation = "create"
|
||||
ReconcileUpdate ReconcileOperation = "update"
|
||||
ReconcileRecreate ReconcileOperation = "recreate"
|
||||
ReconcileNone ReconcileOperation = "none"
|
||||
)
|
||||
|
||||
type RuleOwnerModifier interface {
|
||||
Get(namespace, name string) (RuleOwner, error)
|
||||
Create(RuleOwner) (RuleOwner, error)
|
||||
Update(RuleOwner) (RuleOwner, error)
|
||||
}
|
||||
|
||||
type RuleOwner interface {
|
||||
GetObject() runtime.Object
|
||||
GetNamespace() string
|
||||
GetName() string
|
||||
GetLabels() map[string]string
|
||||
SetLabels(map[string]string)
|
||||
GetAnnotations() map[string]string
|
||||
SetAnnotations(map[string]string)
|
||||
GetRules() []rbac.PolicyRule
|
||||
SetRules([]rbac.PolicyRule)
|
||||
GetAggregationRule() *rbac.AggregationRule
|
||||
SetAggregationRule(*rbac.AggregationRule)
|
||||
DeepCopyRuleOwner() RuleOwner
|
||||
}
|
||||
|
||||
type ReconcileRoleOptions struct {
|
||||
// Role is the expected role that will be reconciled
|
||||
Role RuleOwner
|
||||
// Confirm indicates writes should be performed. When false, results are returned as a dry-run.
|
||||
Confirm bool
|
||||
// RemoveExtraPermissions indicates reconciliation should remove extra permissions from an existing role
|
||||
RemoveExtraPermissions bool
|
||||
// Client is used to look up existing roles, and create/update the role when Confirm=true
|
||||
Client RuleOwnerModifier
|
||||
}
|
||||
|
||||
type ReconcileClusterRoleResult struct {
|
||||
// Role is the reconciled role from the reconciliation operation.
|
||||
// If the reconcile was performed as a dry-run, or the existing role was protected, the reconciled role is not persisted.
|
||||
Role RuleOwner
|
||||
|
||||
// MissingRules contains expected rules that were missing from the currently persisted role
|
||||
MissingRules []rbac.PolicyRule
|
||||
// ExtraRules contains extra permissions the currently persisted role had
|
||||
ExtraRules []rbac.PolicyRule
|
||||
|
||||
// MissingAggregationRuleSelectors contains expected selectors that were missing from the currently persisted role
|
||||
MissingAggregationRuleSelectors []metav1.LabelSelector
|
||||
// ExtraAggregationRuleSelectors contains extra selectors the currently persisted role had
|
||||
ExtraAggregationRuleSelectors []metav1.LabelSelector
|
||||
|
||||
// Operation is the API operation required to reconcile.
|
||||
// If no reconciliation was needed, it is set to ReconcileNone.
|
||||
// If options.Confirm == false, the reconcile was in dry-run mode, so the operation was not performed.
|
||||
// If result.Protected == true, the role opted out of reconciliation, so the operation was not performed.
|
||||
// Otherwise, the operation was performed.
|
||||
Operation ReconcileOperation
|
||||
// Protected indicates an existing role prevented reconciliation
|
||||
Protected bool
|
||||
}
|
||||
|
||||
func (o *ReconcileRoleOptions) Run() (*ReconcileClusterRoleResult, error) {
|
||||
return o.run(0)
|
||||
}
|
||||
|
||||
func (o *ReconcileRoleOptions) run(attempts int) (*ReconcileClusterRoleResult, error) {
|
||||
// This keeps us from retrying forever if a role keeps appearing and disappearing as we reconcile.
|
||||
// Conflict errors on update are handled at a higher level.
|
||||
if attempts > 2 {
|
||||
return nil, fmt.Errorf("exceeded maximum attempts")
|
||||
}
|
||||
|
||||
var result *ReconcileClusterRoleResult
|
||||
|
||||
existing, err := o.Client.Get(o.Role.GetNamespace(), o.Role.GetName())
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
aggregationRule := o.Role.GetAggregationRule()
|
||||
if aggregationRule == nil {
|
||||
aggregationRule = &rbac.AggregationRule{}
|
||||
}
|
||||
result = &ReconcileClusterRoleResult{
|
||||
Role: o.Role,
|
||||
MissingRules: o.Role.GetRules(),
|
||||
MissingAggregationRuleSelectors: aggregationRule.ClusterRoleSelectors,
|
||||
Operation: ReconcileCreate,
|
||||
}
|
||||
|
||||
case err != nil:
|
||||
return nil, err
|
||||
|
||||
default:
|
||||
result, err = computeReconciledRole(existing, o.Role, o.RemoveExtraPermissions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If reconcile-protected, short-circuit
|
||||
if result.Protected {
|
||||
return result, nil
|
||||
}
|
||||
// If we're in dry-run mode, short-circuit
|
||||
if !o.Confirm {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
switch result.Operation {
|
||||
case ReconcileCreate:
|
||||
created, err := o.Client.Create(result.Role)
|
||||
// If created since we started this reconcile, re-run
|
||||
if errors.IsAlreadyExists(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Role = created
|
||||
|
||||
case ReconcileUpdate:
|
||||
updated, err := o.Client.Update(result.Role)
|
||||
// If deleted since we started this reconcile, re-run
|
||||
if errors.IsNotFound(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Role = updated
|
||||
|
||||
case ReconcileNone:
|
||||
// no-op
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid operation: %v", result.Operation)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// computeReconciledRole returns the role that must be created and/or updated to make the
|
||||
// existing role's permissions match the expected role's permissions
|
||||
func computeReconciledRole(existing, expected RuleOwner, removeExtraPermissions bool) (*ReconcileClusterRoleResult, error) {
|
||||
result := &ReconcileClusterRoleResult{Operation: ReconcileNone}
|
||||
|
||||
result.Protected = (existing.GetAnnotations()[rbac.AutoUpdateAnnotationKey] == "false")
|
||||
|
||||
// Start with a copy of the existing object
|
||||
result.Role = existing.DeepCopyRuleOwner()
|
||||
|
||||
// Merge expected annotations and labels
|
||||
result.Role.SetAnnotations(merge(expected.GetAnnotations(), result.Role.GetAnnotations()))
|
||||
if !reflect.DeepEqual(result.Role.GetAnnotations(), existing.GetAnnotations()) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
result.Role.SetLabels(merge(expected.GetLabels(), result.Role.GetLabels()))
|
||||
if !reflect.DeepEqual(result.Role.GetLabels(), existing.GetLabels()) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
// Compute extra and missing rules
|
||||
_, result.ExtraRules = validation.Covers(expected.GetRules(), existing.GetRules())
|
||||
_, result.MissingRules = validation.Covers(existing.GetRules(), expected.GetRules())
|
||||
|
||||
switch {
|
||||
case !removeExtraPermissions && len(result.MissingRules) > 0:
|
||||
// add missing rules in the union case
|
||||
result.Role.SetRules(append(result.Role.GetRules(), result.MissingRules...))
|
||||
result.Operation = ReconcileUpdate
|
||||
|
||||
case removeExtraPermissions && (len(result.MissingRules) > 0 || len(result.ExtraRules) > 0):
|
||||
// stomp to expected rules in the non-union case
|
||||
result.Role.SetRules(expected.GetRules())
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
// Compute extra and missing rules
|
||||
_, result.ExtraAggregationRuleSelectors = aggregationRuleCovers(expected.GetAggregationRule(), existing.GetAggregationRule())
|
||||
_, result.MissingAggregationRuleSelectors = aggregationRuleCovers(existing.GetAggregationRule(), expected.GetAggregationRule())
|
||||
|
||||
switch {
|
||||
case !removeExtraPermissions && len(result.MissingAggregationRuleSelectors) > 0:
|
||||
// add missing rules in the union case
|
||||
aggregationRule := result.Role.GetAggregationRule()
|
||||
if aggregationRule == nil {
|
||||
aggregationRule = &rbac.AggregationRule{}
|
||||
}
|
||||
aggregationRule.ClusterRoleSelectors = append(aggregationRule.ClusterRoleSelectors, result.MissingAggregationRuleSelectors...)
|
||||
result.Role.SetAggregationRule(aggregationRule)
|
||||
result.Operation = ReconcileUpdate
|
||||
|
||||
case removeExtraPermissions && (len(result.MissingAggregationRuleSelectors) > 0 || len(result.ExtraAggregationRuleSelectors) > 0):
|
||||
result.Role.SetAggregationRule(expected.GetAggregationRule())
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// merge combines the given maps with the later annotations having higher precedence
|
||||
func merge(maps ...map[string]string) map[string]string {
|
||||
var output map[string]string = nil
|
||||
for _, m := range maps {
|
||||
if m != nil && output == nil {
|
||||
output = map[string]string{}
|
||||
}
|
||||
for k, v := range m {
|
||||
output[k] = v
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// aggregationRuleCovers determines whether or not the ownerSelectors cover the servantSelectors in terms of semantically
|
||||
// equal label selectors.
|
||||
// It returns whether or not the ownerSelectors cover and a list of the rules that the ownerSelectors do not cover.
|
||||
func aggregationRuleCovers(ownerRule, servantRule *rbac.AggregationRule) (bool, []metav1.LabelSelector) {
|
||||
switch {
|
||||
case ownerRule == nil && servantRule == nil:
|
||||
return true, []metav1.LabelSelector{}
|
||||
case ownerRule == nil && servantRule != nil:
|
||||
return false, servantRule.ClusterRoleSelectors
|
||||
case ownerRule != nil && servantRule == nil:
|
||||
return true, []metav1.LabelSelector{}
|
||||
|
||||
}
|
||||
|
||||
ownerSelectors := ownerRule.ClusterRoleSelectors
|
||||
servantSelectors := servantRule.ClusterRoleSelectors
|
||||
uncoveredSelectors := []metav1.LabelSelector{}
|
||||
|
||||
for _, servantSelector := range servantSelectors {
|
||||
covered := false
|
||||
for _, ownerSelector := range ownerSelectors {
|
||||
if equality.Semantic.DeepEqual(ownerSelector, servantSelector) {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !covered {
|
||||
uncoveredSelectors = append(uncoveredSelectors, servantSelector)
|
||||
}
|
||||
}
|
||||
|
||||
return (len(uncoveredSelectors) == 0), uncoveredSelectors
|
||||
}
|
372
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_role_test.go
generated
vendored
Normal file
372
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_role_test.go
generated
vendored
Normal file
@ -0,0 +1,372 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
func role(rules []rbac.PolicyRule, labels map[string]string, annotations map[string]string) *rbac.ClusterRole {
|
||||
return &rbac.ClusterRole{
|
||||
Rules: rules,
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: labels, Annotations: annotations},
|
||||
}
|
||||
}
|
||||
|
||||
func rules(resources ...string) []rbac.PolicyRule {
|
||||
r := []rbac.PolicyRule{}
|
||||
for _, resource := range resources {
|
||||
r = append(r, rbac.PolicyRule{APIGroups: []string{""}, Verbs: []string{"get"}, Resources: []string{resource}})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type ss map[string]string
|
||||
|
||||
func TestComputeReconciledRoleRules(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
expectedRole *rbac.ClusterRole
|
||||
actualRole *rbac.ClusterRole
|
||||
removeExtraPermissions bool
|
||||
|
||||
expectedReconciledRole *rbac.ClusterRole
|
||||
expectedReconciliationNeeded bool
|
||||
}{
|
||||
"empty": {
|
||||
expectedRole: role(rules(), nil, nil),
|
||||
actualRole: role(rules(), nil, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match without union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("a"), nil, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match with union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("a"), nil, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"different rules without union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("b"), nil, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different rules with union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("b"), nil, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), nil, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"match labels without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match labels with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"different labels without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different labels with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different labels and rules without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different labels and rules with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels and rules without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"1": "b"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels and rules with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"1": "b"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), ss{"1": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"match annotations without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match annotations with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"different annotations without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different annotations with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different annotations and rules without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different annotations and rules with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting annotations and rules without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"1": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting annotations and rules with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"1": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), nil, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels/annotations and rules without union": {
|
||||
expectedRole: role(rules("a"), ss{"3": "d"}, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), ss{"4": "e"}, ss{"1": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"3": "d", "4": "e"}, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels/annotations and rules with union": {
|
||||
expectedRole: role(rules("a"), ss{"3": "d"}, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), ss{"4": "e"}, ss{"1": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), ss{"3": "d", "4": "e"}, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"complex labels/annotations and rules without union": {
|
||||
expectedRole: role(rules("pods", "nodes", "secrets"), ss{"env": "prod", "color": "blue"}, ss{"description": "fancy", "system": "true"}),
|
||||
actualRole: role(rules("nodes", "images", "projects"), ss{"color": "red", "team": "pm"}, ss{"system": "false", "owner": "admin", "vip": "yes"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(
|
||||
rules("pods", "nodes", "secrets"),
|
||||
ss{"env": "prod", "color": "red", "team": "pm"},
|
||||
ss{"description": "fancy", "system": "false", "owner": "admin", "vip": "yes"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"complex labels/annotations and rules with union": {
|
||||
expectedRole: role(rules("pods", "nodes", "secrets"), ss{"env": "prod", "color": "blue", "manager": "randy"}, ss{"description": "fancy", "system": "true", "up": "true"}),
|
||||
actualRole: role(rules("nodes", "images", "projects"), ss{"color": "red", "team": "pm"}, ss{"system": "false", "owner": "admin", "vip": "yes", "rate": "down"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(
|
||||
rules("nodes", "images", "projects", "pods", "secrets"),
|
||||
ss{"env": "prod", "manager": "randy", "color": "red", "team": "pm"},
|
||||
ss{"description": "fancy", "system": "false", "owner": "admin", "vip": "yes", "rate": "down", "up": "true"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range tests {
|
||||
actualRole := ClusterRoleRuleOwner{ClusterRole: tc.actualRole}
|
||||
expectedRole := ClusterRoleRuleOwner{ClusterRole: tc.expectedRole}
|
||||
result, err := computeReconciledRole(actualRole, expectedRole, tc.removeExtraPermissions)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
reconciliationNeeded := result.Operation != ReconcileNone
|
||||
if reconciliationNeeded != tc.expectedReconciliationNeeded {
|
||||
t.Errorf("%s: Expected\n\t%v\ngot\n\t%v", k, tc.expectedReconciliationNeeded, reconciliationNeeded)
|
||||
continue
|
||||
}
|
||||
if reconciliationNeeded && !helper.Semantic.DeepEqual(result.Role.(ClusterRoleRuleOwner).ClusterRole, tc.expectedReconciledRole) {
|
||||
t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.expectedReconciledRole, result.Role)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func aggregatedRole(aggregationRule *rbac.AggregationRule) *rbac.ClusterRole {
|
||||
return &rbac.ClusterRole{
|
||||
AggregationRule: aggregationRule,
|
||||
}
|
||||
}
|
||||
|
||||
func aggregationrule(selectors []map[string]string) *rbac.AggregationRule {
|
||||
ret := &rbac.AggregationRule{}
|
||||
for _, selector := range selectors {
|
||||
ret.ClusterRoleSelectors = append(ret.ClusterRoleSelectors,
|
||||
metav1.LabelSelector{MatchLabels: selector})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func TestComputeReconciledRoleAggregationRules(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
expectedRole *rbac.ClusterRole
|
||||
actualRole *rbac.ClusterRole
|
||||
removeExtraPermissions bool
|
||||
|
||||
expectedReconciledRole *rbac.ClusterRole
|
||||
expectedReconciliationNeeded bool
|
||||
}{
|
||||
"empty": {
|
||||
expectedRole: aggregatedRole(&rbac.AggregationRule{}),
|
||||
actualRole: aggregatedRole(nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"empty-2": {
|
||||
expectedRole: aggregatedRole(&rbac.AggregationRule{}),
|
||||
actualRole: aggregatedRole(&rbac.AggregationRule{}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match without union": {
|
||||
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
|
||||
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match with union": {
|
||||
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
|
||||
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"different rules without union": {
|
||||
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
|
||||
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}})),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different rules with union": {
|
||||
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
|
||||
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}})),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}, {"foo": "bar"}})),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range tests {
|
||||
actualRole := ClusterRoleRuleOwner{ClusterRole: tc.actualRole}
|
||||
expectedRole := ClusterRoleRuleOwner{ClusterRole: tc.expectedRole}
|
||||
result, err := computeReconciledRole(actualRole, expectedRole, tc.removeExtraPermissions)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
reconciliationNeeded := result.Operation != ReconcileNone
|
||||
if reconciliationNeeded != tc.expectedReconciliationNeeded {
|
||||
t.Errorf("%s: Expected\n\t%v\ngot\n\t%v", k, tc.expectedReconciliationNeeded, reconciliationNeeded)
|
||||
continue
|
||||
}
|
||||
if reconciliationNeeded && !helper.Semantic.DeepEqual(result.Role.(ClusterRoleRuleOwner).ClusterRole, tc.expectedReconciledRole) {
|
||||
t.Errorf("%s: %v", k, diff.ObjectDiff(tc.expectedReconciledRole, result.Role.(ClusterRoleRuleOwner).ClusterRole))
|
||||
}
|
||||
}
|
||||
}
|
248
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go
generated
vendored
Normal file
248
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go
generated
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
type RoleBindingModifier interface {
|
||||
Get(namespace, name string) (RoleBinding, error)
|
||||
Delete(namespace, name string, uid types.UID) error
|
||||
Create(RoleBinding) (RoleBinding, error)
|
||||
Update(RoleBinding) (RoleBinding, error)
|
||||
}
|
||||
|
||||
type RoleBinding interface {
|
||||
GetObject() runtime.Object
|
||||
GetNamespace() string
|
||||
GetName() string
|
||||
GetUID() types.UID
|
||||
GetLabels() map[string]string
|
||||
SetLabels(map[string]string)
|
||||
GetAnnotations() map[string]string
|
||||
SetAnnotations(map[string]string)
|
||||
GetRoleRef() rbac.RoleRef
|
||||
GetSubjects() []rbac.Subject
|
||||
SetSubjects([]rbac.Subject)
|
||||
DeepCopyRoleBinding() RoleBinding
|
||||
}
|
||||
|
||||
// ReconcileRoleBindingOptions holds options for running a role binding reconciliation
|
||||
type ReconcileRoleBindingOptions struct {
|
||||
// RoleBinding is the expected rolebinding that will be reconciled
|
||||
RoleBinding RoleBinding
|
||||
// Confirm indicates writes should be performed. When false, results are returned as a dry-run.
|
||||
Confirm bool
|
||||
// RemoveExtraSubjects indicates reconciliation should remove extra subjects from an existing role binding
|
||||
RemoveExtraSubjects bool
|
||||
// Client is used to look up existing rolebindings, and create/update the rolebinding when Confirm=true
|
||||
Client RoleBindingModifier
|
||||
}
|
||||
|
||||
// ReconcileClusterRoleBindingResult holds the result of a reconciliation operation.
|
||||
type ReconcileClusterRoleBindingResult struct {
|
||||
// RoleBinding is the reconciled rolebinding from the reconciliation operation.
|
||||
// If the reconcile was performed as a dry-run, or the existing rolebinding was protected, the reconciled rolebinding is not persisted.
|
||||
RoleBinding RoleBinding
|
||||
|
||||
// MissingSubjects contains expected subjects that were missing from the currently persisted rolebinding
|
||||
MissingSubjects []rbac.Subject
|
||||
// ExtraSubjects contains extra subjects the currently persisted rolebinding had
|
||||
ExtraSubjects []rbac.Subject
|
||||
|
||||
// Operation is the API operation required to reconcile.
|
||||
// If no reconciliation was needed, it is set to ReconcileNone.
|
||||
// If options.Confirm == false, the reconcile was in dry-run mode, so the operation was not performed.
|
||||
// If result.Protected == true, the rolebinding opted out of reconciliation, so the operation was not performed.
|
||||
// Otherwise, the operation was performed.
|
||||
Operation ReconcileOperation
|
||||
// Protected indicates an existing role prevented reconciliation
|
||||
Protected bool
|
||||
}
|
||||
|
||||
func (o *ReconcileRoleBindingOptions) Run() (*ReconcileClusterRoleBindingResult, error) {
|
||||
return o.run(0)
|
||||
}
|
||||
|
||||
func (o *ReconcileRoleBindingOptions) run(attempts int) (*ReconcileClusterRoleBindingResult, error) {
|
||||
// This keeps us from retrying forever if a rolebinding keeps appearing and disappearing as we reconcile.
|
||||
// Conflict errors on update are handled at a higher level.
|
||||
if attempts > 3 {
|
||||
return nil, fmt.Errorf("exceeded maximum attempts")
|
||||
}
|
||||
|
||||
var result *ReconcileClusterRoleBindingResult
|
||||
|
||||
existingBinding, err := o.Client.Get(o.RoleBinding.GetNamespace(), o.RoleBinding.GetName())
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
result = &ReconcileClusterRoleBindingResult{
|
||||
RoleBinding: o.RoleBinding,
|
||||
MissingSubjects: o.RoleBinding.GetSubjects(),
|
||||
Operation: ReconcileCreate,
|
||||
}
|
||||
|
||||
case err != nil:
|
||||
return nil, err
|
||||
|
||||
default:
|
||||
result, err = computeReconciledRoleBinding(existingBinding, o.RoleBinding, o.RemoveExtraSubjects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If reconcile-protected, short-circuit
|
||||
if result.Protected {
|
||||
return result, nil
|
||||
}
|
||||
// If we're in dry-run mode, short-circuit
|
||||
if !o.Confirm {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
switch result.Operation {
|
||||
case ReconcileRecreate:
|
||||
// Try deleting
|
||||
err := o.Client.Delete(existingBinding.GetNamespace(), existingBinding.GetName(), existingBinding.GetUID())
|
||||
switch {
|
||||
case err == nil, errors.IsNotFound(err):
|
||||
// object no longer exists, as desired
|
||||
case errors.IsConflict(err):
|
||||
// delete failed because our UID precondition conflicted
|
||||
// this could mean another object exists with a different UID, re-run
|
||||
return o.run(attempts + 1)
|
||||
default:
|
||||
// return other errors
|
||||
return nil, err
|
||||
}
|
||||
// continue to create
|
||||
fallthrough
|
||||
case ReconcileCreate:
|
||||
created, err := o.Client.Create(result.RoleBinding)
|
||||
// If created since we started this reconcile, re-run
|
||||
if errors.IsAlreadyExists(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.RoleBinding = created
|
||||
|
||||
case ReconcileUpdate:
|
||||
updated, err := o.Client.Update(result.RoleBinding)
|
||||
// If deleted since we started this reconcile, re-run
|
||||
if errors.IsNotFound(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.RoleBinding = updated
|
||||
|
||||
case ReconcileNone:
|
||||
// no-op
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid operation: %v", result.Operation)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// computeReconciledRoleBinding returns the rolebinding that must be created and/or updated to make the
|
||||
// existing rolebinding's subjects, roleref, labels, and annotations match the expected rolebinding
|
||||
func computeReconciledRoleBinding(existing, expected RoleBinding, removeExtraSubjects bool) (*ReconcileClusterRoleBindingResult, error) {
|
||||
result := &ReconcileClusterRoleBindingResult{Operation: ReconcileNone}
|
||||
|
||||
result.Protected = (existing.GetAnnotations()[rbac.AutoUpdateAnnotationKey] == "false")
|
||||
|
||||
// Reset the binding completely if the roleRef is different
|
||||
if expected.GetRoleRef() != existing.GetRoleRef() {
|
||||
result.RoleBinding = expected
|
||||
result.Operation = ReconcileRecreate
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Start with a copy of the existing object
|
||||
result.RoleBinding = existing.DeepCopyRoleBinding()
|
||||
|
||||
// Merge expected annotations and labels
|
||||
result.RoleBinding.SetAnnotations(merge(expected.GetAnnotations(), result.RoleBinding.GetAnnotations()))
|
||||
if !reflect.DeepEqual(result.RoleBinding.GetAnnotations(), existing.GetAnnotations()) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
result.RoleBinding.SetLabels(merge(expected.GetLabels(), result.RoleBinding.GetLabels()))
|
||||
if !reflect.DeepEqual(result.RoleBinding.GetLabels(), existing.GetLabels()) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
// Compute extra and missing subjects
|
||||
result.MissingSubjects, result.ExtraSubjects = diffSubjectLists(expected.GetSubjects(), existing.GetSubjects())
|
||||
|
||||
switch {
|
||||
case !removeExtraSubjects && len(result.MissingSubjects) > 0:
|
||||
// add missing subjects in the union case
|
||||
result.RoleBinding.SetSubjects(append(result.RoleBinding.GetSubjects(), result.MissingSubjects...))
|
||||
result.Operation = ReconcileUpdate
|
||||
|
||||
case removeExtraSubjects && (len(result.MissingSubjects) > 0 || len(result.ExtraSubjects) > 0):
|
||||
// stomp to expected subjects in the non-union case
|
||||
result.RoleBinding.SetSubjects(expected.GetSubjects())
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func contains(list []rbac.Subject, item rbac.Subject) bool {
|
||||
for _, listItem := range list {
|
||||
if listItem == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// diffSubjectLists returns lists containing the items unique to each provided list:
|
||||
// list1Only = list1 - list2
|
||||
// list2Only = list2 - list1
|
||||
// if both returned lists are empty, the provided lists are equal
|
||||
func diffSubjectLists(list1 []rbac.Subject, list2 []rbac.Subject) (list1Only []rbac.Subject, list2Only []rbac.Subject) {
|
||||
for _, list1Item := range list1 {
|
||||
if !contains(list2, list1Item) {
|
||||
if !contains(list1Only, list1Item) {
|
||||
list1Only = append(list1Only, list1Item)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, list2Item := range list2 {
|
||||
if !contains(list1, list2Item) {
|
||||
if !contains(list2Only, list2Item) {
|
||||
list2Only = append(list2Only, list2Item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
181
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings_test.go
generated
vendored
Normal file
181
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings_test.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
func binding(roleRef rbac.RoleRef, subjects []rbac.Subject) *rbac.ClusterRoleBinding {
|
||||
return &rbac.ClusterRoleBinding{RoleRef: roleRef, Subjects: subjects}
|
||||
}
|
||||
|
||||
func ref(name string) rbac.RoleRef {
|
||||
return rbac.RoleRef{Name: name}
|
||||
}
|
||||
|
||||
func subject(name string) rbac.Subject {
|
||||
return rbac.Subject{Name: name}
|
||||
}
|
||||
|
||||
func subjects(names ...string) []rbac.Subject {
|
||||
r := []rbac.Subject{}
|
||||
for _, name := range names {
|
||||
r = append(r, subject(name))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestDiffObjectReferenceLists(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
A []rbac.Subject
|
||||
B []rbac.Subject
|
||||
ExpectedOnlyA []rbac.Subject
|
||||
ExpectedOnlyB []rbac.Subject
|
||||
}{
|
||||
"empty": {},
|
||||
|
||||
"matching, order-independent": {
|
||||
A: subjects("foo", "bar"),
|
||||
B: subjects("bar", "foo"),
|
||||
},
|
||||
|
||||
"partial match": {
|
||||
A: subjects("foo", "bar"),
|
||||
B: subjects("foo", "baz"),
|
||||
ExpectedOnlyA: subjects("bar"),
|
||||
ExpectedOnlyB: subjects("baz"),
|
||||
},
|
||||
|
||||
"missing": {
|
||||
A: subjects("foo"),
|
||||
B: subjects("bar"),
|
||||
ExpectedOnlyA: subjects("foo"),
|
||||
ExpectedOnlyB: subjects("bar"),
|
||||
},
|
||||
|
||||
"remove duplicates": {
|
||||
A: subjects("foo", "foo"),
|
||||
B: subjects("bar", "bar"),
|
||||
ExpectedOnlyA: subjects("foo"),
|
||||
ExpectedOnlyB: subjects("bar"),
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range tests {
|
||||
onlyA, onlyB := diffSubjectLists(tc.A, tc.B)
|
||||
if !helper.Semantic.DeepEqual(onlyA, tc.ExpectedOnlyA) {
|
||||
t.Errorf("%s: Expected %#v, got %#v", k, tc.ExpectedOnlyA, onlyA)
|
||||
}
|
||||
if !helper.Semantic.DeepEqual(onlyB, tc.ExpectedOnlyB) {
|
||||
t.Errorf("%s: Expected %#v, got %#v", k, tc.ExpectedOnlyB, onlyB)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeUpdate(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
ExpectedBinding *rbac.ClusterRoleBinding
|
||||
ActualBinding *rbac.ClusterRoleBinding
|
||||
RemoveExtraSubjects bool
|
||||
|
||||
ExpectedUpdatedBinding *rbac.ClusterRoleBinding
|
||||
ExpectedUpdateNeeded bool
|
||||
}{
|
||||
"match without union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a")),
|
||||
RemoveExtraSubjects: true,
|
||||
|
||||
ExpectedUpdatedBinding: nil,
|
||||
ExpectedUpdateNeeded: false,
|
||||
},
|
||||
"match with union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: nil,
|
||||
ExpectedUpdateNeeded: false,
|
||||
},
|
||||
|
||||
"different roleref with identical subjects": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("differentRole"), subjects("a")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
|
||||
"extra subjects without union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: true,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
"extra subjects with union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: nil,
|
||||
ExpectedUpdateNeeded: false,
|
||||
},
|
||||
|
||||
"missing subjects without union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a", "c")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: true,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a", "c")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
"missing subjects with union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a", "c")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a", "b", "c")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range tests {
|
||||
actualRoleBinding := ClusterRoleBindingAdapter{ClusterRoleBinding: tc.ActualBinding}
|
||||
expectedRoleBinding := ClusterRoleBindingAdapter{ClusterRoleBinding: tc.ExpectedBinding}
|
||||
result, err := computeReconciledRoleBinding(actualRoleBinding, expectedRoleBinding, tc.RemoveExtraSubjects)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
updateNeeded := result.Operation != ReconcileNone
|
||||
updatedBinding := result.RoleBinding.(ClusterRoleBindingAdapter).ClusterRoleBinding
|
||||
if updateNeeded != tc.ExpectedUpdateNeeded {
|
||||
t.Errorf("%s: Expected\n\t%v\ngot\n\t%v (%v)", k, tc.ExpectedUpdateNeeded, updateNeeded, result.Operation)
|
||||
continue
|
||||
}
|
||||
if updateNeeded && !helper.Semantic.DeepEqual(updatedBinding, tc.ExpectedUpdatedBinding) {
|
||||
t.Errorf("%s: Expected\n\t%v %v\ngot\n\t%v %v", k, tc.ExpectedUpdatedBinding.RoleRef, tc.ExpectedUpdatedBinding.Subjects, updatedBinding.RoleRef, updatedBinding.Subjects)
|
||||
}
|
||||
}
|
||||
}
|
112
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/role_interfaces.go
generated
vendored
Normal file
112
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/role_interfaces.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
core "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/pkg/registry/rbac/reconciliation.RuleOwner
|
||||
// +k8s:deepcopy-gen:nonpointer-interfaces=true
|
||||
type RoleRuleOwner struct {
|
||||
Role *rbac.Role
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) GetObject() runtime.Object {
|
||||
return o.Role
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) GetNamespace() string {
|
||||
return o.Role.Namespace
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) GetName() string {
|
||||
return o.Role.Name
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) GetLabels() map[string]string {
|
||||
return o.Role.Labels
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) SetLabels(in map[string]string) {
|
||||
o.Role.Labels = in
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) GetAnnotations() map[string]string {
|
||||
return o.Role.Annotations
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) SetAnnotations(in map[string]string) {
|
||||
o.Role.Annotations = in
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) GetRules() []rbac.PolicyRule {
|
||||
return o.Role.Rules
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) SetRules(in []rbac.PolicyRule) {
|
||||
o.Role.Rules = in
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) GetAggregationRule() *rbac.AggregationRule {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o RoleRuleOwner) SetAggregationRule(in *rbac.AggregationRule) {
|
||||
}
|
||||
|
||||
type RoleModifier struct {
|
||||
Client internalversion.RolesGetter
|
||||
NamespaceClient core.NamespaceInterface
|
||||
}
|
||||
|
||||
func (c RoleModifier) Get(namespace, name string) (RuleOwner, error) {
|
||||
ret, err := c.Client.Roles(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RoleRuleOwner{Role: ret}, err
|
||||
}
|
||||
|
||||
func (c RoleModifier) Create(in RuleOwner) (RuleOwner, error) {
|
||||
ns := &api.Namespace{ObjectMeta: metav1.ObjectMeta{Name: in.GetNamespace()}}
|
||||
if _, err := c.NamespaceClient.Create(ns); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := c.Client.Roles(in.GetNamespace()).Create(in.(RoleRuleOwner).Role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RoleRuleOwner{Role: ret}, err
|
||||
}
|
||||
|
||||
func (c RoleModifier) Update(in RuleOwner) (RuleOwner, error) {
|
||||
ret, err := c.Client.Roles(in.GetNamespace()).Update(in.(RoleRuleOwner).Role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RoleRuleOwner{Role: ret}, err
|
||||
|
||||
}
|
118
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/rolebinding_interfaces.go
generated
vendored
Normal file
118
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/rolebinding_interfaces.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
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 reconciliation
|
||||
|
||||
import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
core "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/pkg/registry/rbac/reconciliation.RoleBinding
|
||||
// +k8s:deepcopy-gen:nonpointer-interfaces=true
|
||||
type RoleBindingAdapter struct {
|
||||
RoleBinding *rbac.RoleBinding
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetObject() runtime.Object {
|
||||
return o.RoleBinding
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetNamespace() string {
|
||||
return o.RoleBinding.Namespace
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetName() string {
|
||||
return o.RoleBinding.Name
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetUID() types.UID {
|
||||
return o.RoleBinding.UID
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetLabels() map[string]string {
|
||||
return o.RoleBinding.Labels
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) SetLabels(in map[string]string) {
|
||||
o.RoleBinding.Labels = in
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetAnnotations() map[string]string {
|
||||
return o.RoleBinding.Annotations
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) SetAnnotations(in map[string]string) {
|
||||
o.RoleBinding.Annotations = in
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetRoleRef() rbac.RoleRef {
|
||||
return o.RoleBinding.RoleRef
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) GetSubjects() []rbac.Subject {
|
||||
return o.RoleBinding.Subjects
|
||||
}
|
||||
|
||||
func (o RoleBindingAdapter) SetSubjects(in []rbac.Subject) {
|
||||
o.RoleBinding.Subjects = in
|
||||
}
|
||||
|
||||
type RoleBindingClientAdapter struct {
|
||||
Client internalversion.RoleBindingsGetter
|
||||
NamespaceClient core.NamespaceInterface
|
||||
}
|
||||
|
||||
func (c RoleBindingClientAdapter) Get(namespace, name string) (RoleBinding, error) {
|
||||
ret, err := c.Client.RoleBindings(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RoleBindingAdapter{RoleBinding: ret}, err
|
||||
}
|
||||
|
||||
func (c RoleBindingClientAdapter) Create(in RoleBinding) (RoleBinding, error) {
|
||||
ns := &api.Namespace{ObjectMeta: metav1.ObjectMeta{Name: in.GetNamespace()}}
|
||||
if _, err := c.NamespaceClient.Create(ns); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := c.Client.RoleBindings(in.GetNamespace()).Create(in.(RoleBindingAdapter).RoleBinding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RoleBindingAdapter{RoleBinding: ret}, err
|
||||
}
|
||||
|
||||
func (c RoleBindingClientAdapter) Update(in RoleBinding) (RoleBinding, error) {
|
||||
ret, err := c.Client.RoleBindings(in.GetNamespace()).Update(in.(RoleBindingAdapter).RoleBinding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RoleBindingAdapter{RoleBinding: ret}, err
|
||||
|
||||
}
|
||||
|
||||
func (c RoleBindingClientAdapter) Delete(namespace, name string, uid types.UID) error {
|
||||
return c.Client.RoleBindings(namespace).Delete(name, &metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &uid}})
|
||||
}
|
145
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/zz_generated.deepcopy.go
generated
vendored
Normal file
145
vendor/k8s.io/kubernetes/pkg/registry/rbac/reconciliation/zz_generated.deepcopy.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2018 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.
|
||||
*/
|
||||
|
||||
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
|
||||
|
||||
package reconciliation
|
||||
|
||||
import (
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterRoleBindingAdapter) DeepCopyInto(out *ClusterRoleBindingAdapter) {
|
||||
*out = *in
|
||||
if in.ClusterRoleBinding != nil {
|
||||
in, out := &in.ClusterRoleBinding, &out.ClusterRoleBinding
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(rbac.ClusterRoleBinding)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterRoleBindingAdapter.
|
||||
func (in *ClusterRoleBindingAdapter) DeepCopy() *ClusterRoleBindingAdapter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterRoleBindingAdapter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyRoleBinding is an autogenerated deepcopy function, copying the receiver, creating a new RoleBinding.
|
||||
func (in ClusterRoleBindingAdapter) DeepCopyRoleBinding() RoleBinding {
|
||||
return *in.DeepCopy()
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterRoleRuleOwner) DeepCopyInto(out *ClusterRoleRuleOwner) {
|
||||
*out = *in
|
||||
if in.ClusterRole != nil {
|
||||
in, out := &in.ClusterRole, &out.ClusterRole
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(rbac.ClusterRole)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterRoleRuleOwner.
|
||||
func (in *ClusterRoleRuleOwner) DeepCopy() *ClusterRoleRuleOwner {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterRoleRuleOwner)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyRuleOwner is an autogenerated deepcopy function, copying the receiver, creating a new RuleOwner.
|
||||
func (in ClusterRoleRuleOwner) DeepCopyRuleOwner() RuleOwner {
|
||||
return *in.DeepCopy()
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RoleBindingAdapter) DeepCopyInto(out *RoleBindingAdapter) {
|
||||
*out = *in
|
||||
if in.RoleBinding != nil {
|
||||
in, out := &in.RoleBinding, &out.RoleBinding
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(rbac.RoleBinding)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBindingAdapter.
|
||||
func (in *RoleBindingAdapter) DeepCopy() *RoleBindingAdapter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RoleBindingAdapter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyRoleBinding is an autogenerated deepcopy function, copying the receiver, creating a new RoleBinding.
|
||||
func (in RoleBindingAdapter) DeepCopyRoleBinding() RoleBinding {
|
||||
return *in.DeepCopy()
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RoleRuleOwner) DeepCopyInto(out *RoleRuleOwner) {
|
||||
*out = *in
|
||||
if in.Role != nil {
|
||||
in, out := &in.Role, &out.Role
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(rbac.Role)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleRuleOwner.
|
||||
func (in *RoleRuleOwner) DeepCopy() *RoleRuleOwner {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RoleRuleOwner)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyRuleOwner is an autogenerated deepcopy function, copying the receiver, creating a new RuleOwner.
|
||||
func (in RoleRuleOwner) DeepCopyRuleOwner() RuleOwner {
|
||||
return *in.DeepCopy()
|
||||
}
|
61
vendor/k8s.io/kubernetes/pkg/registry/rbac/rest/BUILD
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/registry/rbac/rest/BUILD
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage_rbac.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/rest",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole/policybased:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole/storage:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding/policybased:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding/storage:go_default_library",
|
||||
"//pkg/registry/rbac/reconciliation:go_default_library",
|
||||
"//pkg/registry/rbac/role:go_default_library",
|
||||
"//pkg/registry/rbac/role/policybased:go_default_library",
|
||||
"//pkg/registry/rbac/role/storage:go_default_library",
|
||||
"//pkg/registry/rbac/rolebinding:go_default_library",
|
||||
"//pkg/registry/rbac/rolebinding/policybased:go_default_library",
|
||||
"//pkg/registry/rbac/rolebinding/storage:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/retry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
353
vendor/k8s.io/kubernetes/pkg/registry/rbac/rest/storage_rbac.go
generated
vendored
Normal file
353
vendor/k8s.io/kubernetes/pkg/registry/rbac/rest/storage_rbac.go
generated
vendored
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
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 rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
rbacapiv1 "k8s.io/api/rbac/v1"
|
||||
rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
|
||||
rbacapiv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
|
||||
clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased"
|
||||
clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
|
||||
clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased"
|
||||
clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/reconciliation"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/role"
|
||||
rolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/role/policybased"
|
||||
rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
|
||||
rolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased"
|
||||
rolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
|
||||
)
|
||||
|
||||
const PostStartHookName = "rbac/bootstrap-roles"
|
||||
|
||||
type RESTStorageProvider struct {
|
||||
Authorizer authorizer.Authorizer
|
||||
}
|
||||
|
||||
var _ genericapiserver.PostStartHookProvider = RESTStorageProvider{}
|
||||
|
||||
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(rbac.GroupName, legacyscheme.Registry, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
|
||||
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
|
||||
// TODO refactor the plumbing to provide the information in the APIGroupInfo
|
||||
|
||||
if apiResourceConfigSource.AnyResourcesForVersionEnabled(rbacapiv1alpha1.SchemeGroupVersion) {
|
||||
apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1alpha1.SchemeGroupVersion.Version] = p.storage(rbacapiv1alpha1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter)
|
||||
apiGroupInfo.GroupMeta.GroupVersion = rbacapiv1alpha1.SchemeGroupVersion
|
||||
}
|
||||
if apiResourceConfigSource.AnyResourcesForVersionEnabled(rbacapiv1beta1.SchemeGroupVersion) {
|
||||
apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1beta1.SchemeGroupVersion.Version] = p.storage(rbacapiv1beta1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter)
|
||||
apiGroupInfo.GroupMeta.GroupVersion = rbacapiv1beta1.SchemeGroupVersion
|
||||
}
|
||||
if apiResourceConfigSource.AnyResourcesForVersionEnabled(rbacapiv1.SchemeGroupVersion) {
|
||||
apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1.SchemeGroupVersion.Version] = p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter)
|
||||
apiGroupInfo.GroupMeta.GroupVersion = rbacapiv1.SchemeGroupVersion
|
||||
}
|
||||
|
||||
return apiGroupInfo, true
|
||||
}
|
||||
|
||||
func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
|
||||
once := new(sync.Once)
|
||||
var (
|
||||
authorizationRuleResolver rbacregistryvalidation.AuthorizationRuleResolver
|
||||
rolesStorage rest.StandardStorage
|
||||
roleBindingsStorage rest.StandardStorage
|
||||
clusterRolesStorage rest.StandardStorage
|
||||
clusterRoleBindingsStorage rest.StandardStorage
|
||||
)
|
||||
|
||||
initializeStorage := func() {
|
||||
once.Do(func() {
|
||||
rolesStorage = rolestore.NewREST(restOptionsGetter)
|
||||
roleBindingsStorage = rolebindingstore.NewREST(restOptionsGetter)
|
||||
clusterRolesStorage = clusterrolestore.NewREST(restOptionsGetter)
|
||||
clusterRoleBindingsStorage = clusterrolebindingstore.NewREST(restOptionsGetter)
|
||||
|
||||
authorizationRuleResolver = rbacregistryvalidation.NewDefaultRuleResolver(
|
||||
role.AuthorizerAdapter{Registry: role.NewRegistry(rolesStorage)},
|
||||
rolebinding.AuthorizerAdapter{Registry: rolebinding.NewRegistry(roleBindingsStorage)},
|
||||
clusterrole.AuthorizerAdapter{Registry: clusterrole.NewRegistry(clusterRolesStorage)},
|
||||
clusterrolebinding.AuthorizerAdapter{Registry: clusterrolebinding.NewRegistry(clusterRoleBindingsStorage)},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
storage := map[string]rest.Storage{}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("roles")) {
|
||||
initializeStorage()
|
||||
storage["roles"] = rolepolicybased.NewStorage(rolesStorage, authorizationRuleResolver)
|
||||
}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("rolebindings")) {
|
||||
initializeStorage()
|
||||
storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, p.Authorizer, authorizationRuleResolver)
|
||||
}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("clusterroles")) {
|
||||
initializeStorage()
|
||||
storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, authorizationRuleResolver)
|
||||
}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("clusterrolebindings")) {
|
||||
initializeStorage()
|
||||
storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, p.Authorizer, authorizationRuleResolver)
|
||||
}
|
||||
return storage
|
||||
}
|
||||
|
||||
func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
|
||||
policy := &PolicyData{
|
||||
ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...),
|
||||
ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...),
|
||||
Roles: bootstrappolicy.NamespaceRoles(),
|
||||
RoleBindings: bootstrappolicy.NamespaceRoleBindings(),
|
||||
ClusterRolesToAggregate: bootstrappolicy.ClusterRolesToAggregate(),
|
||||
}
|
||||
return PostStartHookName, policy.EnsureRBACPolicy(), nil
|
||||
}
|
||||
|
||||
type PolicyData struct {
|
||||
ClusterRoles []rbac.ClusterRole
|
||||
ClusterRoleBindings []rbac.ClusterRoleBinding
|
||||
Roles map[string][]rbac.Role
|
||||
RoleBindings map[string][]rbac.RoleBinding
|
||||
// ClusterRolesToAggregate maps from previous clusterrole name to the new clusterrole name
|
||||
ClusterRolesToAggregate map[string]string
|
||||
}
|
||||
|
||||
func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
|
||||
return func(hookContext genericapiserver.PostStartHookContext) error {
|
||||
// intializing roles is really important. On some e2e runs, we've seen cases where etcd is down when the server
|
||||
// starts, the roles don't initialize, and nothing works.
|
||||
err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) {
|
||||
|
||||
coreclientset, err := coreclient.NewForConfig(hookContext.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
clientset, err := rbacclient.NewForConfig(hookContext.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
// Make sure etcd is responding before we start reconciling
|
||||
if _, err := clientset.ClusterRoles().List(metav1.ListOptions{}); err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
if _, err := clientset.ClusterRoleBindings().List(metav1.ListOptions{}); err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// if the new cluster roles to aggregate do not yet exist, then we need to copy the old roles if they don't exist
|
||||
// in new locations
|
||||
if err := primeAggregatedClusterRoles(p.ClusterRolesToAggregate, clientset); err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to prime aggregated clusterroles: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ensure bootstrap roles are created or reconciled
|
||||
for _, clusterRole := range p.ClusterRoles {
|
||||
opts := reconciliation.ReconcileRoleOptions{
|
||||
Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: &clusterRole},
|
||||
Client: reconciliation.ClusterRoleModifier{Client: clientset.ClusterRoles()},
|
||||
Confirm: true,
|
||||
}
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
result, err := opts.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case result.Protected && result.Operation != reconciliation.ReconcileNone:
|
||||
glog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
|
||||
case result.Operation == reconciliation.ReconcileUpdate:
|
||||
glog.Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
|
||||
case result.Operation == reconciliation.ReconcileCreate:
|
||||
glog.Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
// don't fail on failures, try to create as many as you can
|
||||
utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrole.%s/%s: %v", rbac.GroupName, clusterRole.Name, err))
|
||||
}
|
||||
}
|
||||
|
||||
// ensure bootstrap rolebindings are created or reconciled
|
||||
for _, clusterRoleBinding := range p.ClusterRoleBindings {
|
||||
opts := reconciliation.ReconcileRoleBindingOptions{
|
||||
RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: &clusterRoleBinding},
|
||||
Client: reconciliation.ClusterRoleBindingClientAdapter{Client: clientset.ClusterRoleBindings()},
|
||||
Confirm: true,
|
||||
}
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
result, err := opts.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case result.Protected && result.Operation != reconciliation.ReconcileNone:
|
||||
glog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
|
||||
case result.Operation == reconciliation.ReconcileUpdate:
|
||||
glog.Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
|
||||
case result.Operation == reconciliation.ReconcileCreate:
|
||||
glog.Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
|
||||
case result.Operation == reconciliation.ReconcileRecreate:
|
||||
glog.Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
// don't fail on failures, try to create as many as you can
|
||||
utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrolebinding.%s/%s: %v", rbac.GroupName, clusterRoleBinding.Name, err))
|
||||
}
|
||||
}
|
||||
|
||||
// ensure bootstrap namespaced roles are created or reconciled
|
||||
for namespace, roles := range p.Roles {
|
||||
for _, role := range roles {
|
||||
opts := reconciliation.ReconcileRoleOptions{
|
||||
Role: reconciliation.RoleRuleOwner{Role: &role},
|
||||
Client: reconciliation.RoleModifier{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
|
||||
Confirm: true,
|
||||
}
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
result, err := opts.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case result.Protected && result.Operation != reconciliation.ReconcileNone:
|
||||
glog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
|
||||
case result.Operation == reconciliation.ReconcileUpdate:
|
||||
glog.Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
|
||||
case result.Operation == reconciliation.ReconcileCreate:
|
||||
glog.Infof("created role.%s/%s in %v ", rbac.GroupName, role.Name, namespace)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
// don't fail on failures, try to create as many as you can
|
||||
utilruntime.HandleError(fmt.Errorf("unable to reconcile role.%s/%s in %v: %v", rbac.GroupName, role.Name, namespace, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure bootstrap namespaced rolebindings are created or reconciled
|
||||
for namespace, roleBindings := range p.RoleBindings {
|
||||
for _, roleBinding := range roleBindings {
|
||||
opts := reconciliation.ReconcileRoleBindingOptions{
|
||||
RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: &roleBinding},
|
||||
Client: reconciliation.RoleBindingClientAdapter{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
|
||||
Confirm: true,
|
||||
}
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
result, err := opts.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case result.Protected && result.Operation != reconciliation.ReconcileNone:
|
||||
glog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
|
||||
case result.Operation == reconciliation.ReconcileUpdate:
|
||||
glog.Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
|
||||
case result.Operation == reconciliation.ReconcileCreate:
|
||||
glog.Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
|
||||
case result.Operation == reconciliation.ReconcileRecreate:
|
||||
glog.Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
// don't fail on failures, try to create as many as you can
|
||||
utilruntime.HandleError(fmt.Errorf("unable to reconcile rolebinding.%s/%s in %v: %v", rbac.GroupName, roleBinding.Name, namespace, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
// if we're never able to make it through intialization, kill the API server
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize roles: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p RESTStorageProvider) GroupName() string {
|
||||
return rbac.GroupName
|
||||
}
|
||||
|
||||
// primeAggregatedClusterRoles copies roles that have transitioned to aggregated roles and may need to pick up changes
|
||||
// that were done to the legacy roles.
|
||||
func primeAggregatedClusterRoles(clusterRolesToAggregate map[string]string, clusterRoleClient rbacclient.ClusterRolesGetter) error {
|
||||
for oldName, newName := range clusterRolesToAggregate {
|
||||
_, err := clusterRoleClient.ClusterRoles().Get(newName, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
existingRole, err := clusterRoleClient.ClusterRoles().Get(oldName, metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(1).Infof("migrating %v to %v", existingRole.Name, newName)
|
||||
existingRole.Name = newName
|
||||
existingRole.ResourceVersion = "" // clear this so the object can be created.
|
||||
if _, err := clusterRoleClient.ClusterRoles().Create(existingRole); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/BUILD
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"registry.go",
|
||||
"strategy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/role",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/registry/rbac/role/policybased:all-srcs",
|
||||
"//pkg/registry/rbac/role/storage:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing Role objects.
|
||||
package role // import "k8s.io/kubernetes/pkg/registry/rbac/role"
|
35
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/policybased/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/policybased/BUILD
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/role/policybased",
|
||||
deps = [
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
77
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/policybased/storage.go
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/policybased/storage.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 policybased implements a standard storage for Role that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("roles")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
|
||||
return &Storage{s, ruleResolver}
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
role := obj.(*rbac.Role)
|
||||
rules := role.Rules
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, role.Name, err)
|
||||
}
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
|
||||
}
|
||||
|
||||
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
role := obj.(*rbac.Role)
|
||||
|
||||
// if we're only mutating fields needed for the GC to eventually delete this obj, return
|
||||
if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
rules := role.Rules
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, role.Name, err)
|
||||
}
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
|
||||
}
|
93
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/registry.go
generated
vendored
Normal file
93
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/registry.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
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 role
|
||||
|
||||
import (
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store Roles.
|
||||
type Registry interface {
|
||||
ListRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.RoleList, error)
|
||||
CreateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc) error
|
||||
UpdateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error
|
||||
GetRole(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.Role, error)
|
||||
DeleteRole(ctx genericapirequest.Context, name string) error
|
||||
WatchRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.RoleList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc) error {
|
||||
_, err := s.Create(ctx, role, createValidation, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateRole(ctx genericapirequest.Context, role *rbac.Role, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error {
|
||||
// TODO: any admission?
|
||||
_, _, err := s.Update(ctx, role.Name, rest.DefaultUpdatedObjectInfo(role), createValidation, updateValidation)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchRoles(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetRole(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.Role, error) {
|
||||
obj, err := s.Get(ctx, name, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.Role), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteRole(ctx genericapirequest.Context, name string) error {
|
||||
_, _, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// AuthorizerAdapter adapts the registry to the authorizer interface
|
||||
type AuthorizerAdapter struct {
|
||||
Registry Registry
|
||||
}
|
||||
|
||||
func (a AuthorizerAdapter) GetRole(namespace, name string) (*rbac.Role, error) {
|
||||
return a.Registry.GetRole(genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace), name, &metav1.GetOptions{})
|
||||
}
|
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/storage/BUILD
generated
vendored
Normal file
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/storage/BUILD
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/role/storage",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/role:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/storage/storage.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/storage/storage.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/role"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for Role
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against Role objects.
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.Role{} },
|
||||
NewListFunc: func() runtime.Object { return &rbac.RoleList{} },
|
||||
DefaultQualifiedResource: rbac.Resource("roles"),
|
||||
|
||||
CreateStrategy: role.Strategy,
|
||||
UpdateStrategy: role.Strategy,
|
||||
DeleteStrategy: role.Strategy,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/strategy.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/role/strategy.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 role
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
)
|
||||
|
||||
// strategy implements behavior for Roles
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// Role objects.
|
||||
var Strategy = strategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for Roles.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for Roles.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
|
||||
_ = obj.(*rbac.Role)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
|
||||
newRole := obj.(*rbac.Role)
|
||||
oldRole := old.(*rbac.Role)
|
||||
|
||||
_, _ = newRole, oldRole
|
||||
}
|
||||
|
||||
// Validate validates a new Role. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
|
||||
role := obj.(*rbac.Role)
|
||||
return validation.ValidateRole(role)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.Role)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.Role)
|
||||
errorList := validation.ValidateRole(newObj)
|
||||
return append(errorList, validation.ValidateRoleUpdate(newObj, old.(*rbac.Role))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/BUILD
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"registry.go",
|
||||
"strategy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/rolebinding",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/registry/rbac/rolebinding/policybased:all-srcs",
|
||||
"//pkg/registry/rbac/rolebinding/storage:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing RoleBinding objects.
|
||||
package rolebinding // import "k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
|
36
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased/BUILD
generated
vendored
Normal file
36
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased/BUILD
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased",
|
||||
deps = [
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
110
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased/storage.go
generated
vendored
Normal file
110
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased/storage.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 policybased implements a standard storage for RoleBinding that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
|
||||
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("rolebindings")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
authorizer authorizer.Authorizer
|
||||
|
||||
ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
|
||||
return &Storage{s, authorizer, ruleResolver}
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
// Get the namespace from the context (populated from the URL).
|
||||
// The namespace in the object can be empty until StandardStorage.Create()->BeforeCreate() populates it from the context.
|
||||
namespace, ok := genericapirequest.NamespaceFrom(ctx)
|
||||
if !ok {
|
||||
return nil, errors.NewBadRequest("namespace is required")
|
||||
}
|
||||
|
||||
roleBinding := obj.(*rbac.RoleBinding)
|
||||
if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) {
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
|
||||
}
|
||||
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
|
||||
if rbacregistry.EscalationAllowed(ctx) {
|
||||
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
|
||||
}
|
||||
|
||||
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
// Get the namespace from the context (populated from the URL).
|
||||
// The namespace in the object can be empty until StandardStorage.Update()->BeforeUpdate() populates it from the context.
|
||||
namespace, ok := genericapirequest.NamespaceFrom(ctx)
|
||||
if !ok {
|
||||
return nil, errors.NewBadRequest("namespace is required")
|
||||
}
|
||||
|
||||
roleBinding := obj.(*rbac.RoleBinding)
|
||||
|
||||
// if we're only mutating fields needed for the GC to eventually delete this obj, return
|
||||
if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// if we're explicitly authorized to bind this role, return
|
||||
if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// Otherwise, see if we already have all the permissions contained in the referenced role
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
|
||||
}
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
|
||||
}
|
102
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/registry.go
generated
vendored
Normal file
102
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/registry.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
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 rolebinding
|
||||
|
||||
import (
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store RoleBindings.
|
||||
type Registry interface {
|
||||
ListRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.RoleBindingList, error)
|
||||
CreateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc) error
|
||||
UpdateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error
|
||||
GetRoleBinding(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.RoleBinding, error)
|
||||
DeleteRoleBinding(ctx genericapirequest.Context, name string) error
|
||||
WatchRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*rbac.RoleBindingList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleBindingList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc) error {
|
||||
// TODO(ericchiang): add additional validation
|
||||
_, err := s.Create(ctx, roleBinding, createValidation, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateRoleBinding(ctx genericapirequest.Context, roleBinding *rbac.RoleBinding, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) error {
|
||||
_, _, err := s.Update(ctx, roleBinding.Name, rest.DefaultUpdatedObjectInfo(roleBinding), createValidation, updateValidation)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchRoleBindings(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetRoleBinding(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*rbac.RoleBinding, error) {
|
||||
obj, err := s.Get(ctx, name, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.RoleBinding), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteRoleBinding(ctx genericapirequest.Context, name string) error {
|
||||
_, _, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// AuthorizerAdapter adapts the registry to the authorizer interface
|
||||
type AuthorizerAdapter struct {
|
||||
Registry Registry
|
||||
}
|
||||
|
||||
func (a AuthorizerAdapter) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
|
||||
list, err := a.Registry.ListRoleBindings(genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace), &metainternalversion.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := []*rbac.RoleBinding{}
|
||||
for i := range list.Items {
|
||||
ret = append(ret, &list.Items[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage/BUILD
generated
vendored
Normal file
32
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage/BUILD
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/registry/rbac/rolebinding:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage/storage.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage/storage.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for RoleBinding
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against RoleBinding objects.
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.RoleBinding{} },
|
||||
NewListFunc: func() runtime.Object { return &rbac.RoleBindingList{} },
|
||||
DefaultQualifiedResource: rbac.Resource("rolebindings"),
|
||||
|
||||
CreateStrategy: rolebinding.Strategy,
|
||||
UpdateStrategy: rolebinding.Strategy,
|
||||
DeleteStrategy: rolebinding.Strategy,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/strategy.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/registry/rbac/rolebinding/strategy.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 rolebinding
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
)
|
||||
|
||||
// strategy implements behavior for RoleBindings
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// RoleBinding objects.
|
||||
var Strategy = strategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for RoleBindings.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for RoleBindings.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
|
||||
_ = obj.(*rbac.RoleBinding)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
|
||||
newRoleBinding := obj.(*rbac.RoleBinding)
|
||||
oldRoleBinding := old.(*rbac.RoleBinding)
|
||||
|
||||
_, _ = newRoleBinding, oldRoleBinding
|
||||
}
|
||||
|
||||
// Validate validates a new RoleBinding. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
|
||||
roleBinding := obj.(*rbac.RoleBinding)
|
||||
return validation.ValidateRoleBinding(roleBinding)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.RoleBinding)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.RoleBinding)
|
||||
errorList := validation.ValidateRoleBinding(newObj)
|
||||
return append(errorList, validation.ValidateRoleBindingUpdate(newObj, old.(*rbac.RoleBinding))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
56
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/BUILD
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/BUILD
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"policy_compact_test.go",
|
||||
"policy_comparator_test.go",
|
||||
"rule_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/validation",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"policy_compact.go",
|
||||
"policy_comparator.go",
|
||||
"rule.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/rbac/validation",
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
89
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_compact.go
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_compact.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
type simpleResource struct {
|
||||
Group string
|
||||
Resource string
|
||||
ResourceNameExist bool
|
||||
ResourceName string
|
||||
}
|
||||
|
||||
// CompactRules combines rules that contain a single APIGroup/Resource, differ only by verb, and contain no other attributes.
|
||||
// this is a fast check, and works well with the decomposed "missing rules" list from a Covers check.
|
||||
func CompactRules(rules []rbac.PolicyRule) ([]rbac.PolicyRule, error) {
|
||||
compacted := make([]rbac.PolicyRule, 0, len(rules))
|
||||
|
||||
simpleRules := map[simpleResource]*rbac.PolicyRule{}
|
||||
for _, rule := range rules {
|
||||
if resource, isSimple := isSimpleResourceRule(&rule); isSimple {
|
||||
if existingRule, ok := simpleRules[resource]; ok {
|
||||
// Add the new verbs to the existing simple resource rule
|
||||
if existingRule.Verbs == nil {
|
||||
existingRule.Verbs = []string{}
|
||||
}
|
||||
existingRule.Verbs = append(existingRule.Verbs, rule.Verbs...)
|
||||
} else {
|
||||
// Copy the rule to accumulate matching simple resource rules into
|
||||
simpleRules[resource] = rule.DeepCopy()
|
||||
}
|
||||
} else {
|
||||
compacted = append(compacted, rule)
|
||||
}
|
||||
}
|
||||
|
||||
// Once we've consolidated the simple resource rules, add them to the compacted list
|
||||
for _, simpleRule := range simpleRules {
|
||||
compacted = append(compacted, *simpleRule)
|
||||
}
|
||||
|
||||
return compacted, nil
|
||||
}
|
||||
|
||||
// isSimpleResourceRule returns true if the given rule contains verbs, a single resource, a single API group, at most one Resource Name, and no other values
|
||||
func isSimpleResourceRule(rule *rbac.PolicyRule) (simpleResource, bool) {
|
||||
resource := simpleResource{}
|
||||
|
||||
// If we have "complex" rule attributes, return early without allocations or expensive comparisons
|
||||
if len(rule.ResourceNames) > 1 || len(rule.NonResourceURLs) > 0 {
|
||||
return resource, false
|
||||
}
|
||||
// If we have multiple api groups or resources, return early
|
||||
if len(rule.APIGroups) != 1 || len(rule.Resources) != 1 {
|
||||
return resource, false
|
||||
}
|
||||
|
||||
// Test if this rule only contains APIGroups/Resources/Verbs/ResourceNames
|
||||
simpleRule := &rbac.PolicyRule{APIGroups: rule.APIGroups, Resources: rule.Resources, Verbs: rule.Verbs, ResourceNames: rule.ResourceNames}
|
||||
if !reflect.DeepEqual(simpleRule, rule) {
|
||||
return resource, false
|
||||
}
|
||||
|
||||
if len(rule.ResourceNames) == 0 {
|
||||
resource = simpleResource{Group: rule.APIGroups[0], Resource: rule.Resources[0], ResourceNameExist: false}
|
||||
} else {
|
||||
resource = simpleResource{Group: rule.APIGroups[0], Resource: rule.Resources[0], ResourceNameExist: true, ResourceName: rule.ResourceNames[0]}
|
||||
}
|
||||
|
||||
return resource, true
|
||||
}
|
226
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_compact_test.go
generated
vendored
Normal file
226
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_compact_test.go
generated
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
func TestCompactRules(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
Rules []rbac.PolicyRule
|
||||
Expected []rbac.PolicyRule
|
||||
}{
|
||||
"empty": {
|
||||
Rules: []rbac.PolicyRule{},
|
||||
Expected: []rbac.PolicyRule{},
|
||||
},
|
||||
"simple": {
|
||||
Rules: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{""}, Resources: []string{"builds"}},
|
||||
{Verbs: []string{"update", "patch"}, APIGroups: []string{""}, Resources: []string{"builds"}},
|
||||
|
||||
{Verbs: []string{"create"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}},
|
||||
{Verbs: []string{"delete"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}},
|
||||
{Verbs: []string{"patch"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}, ResourceNames: []string{""}},
|
||||
{Verbs: []string{"get"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}, ResourceNames: []string{"foo"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}, ResourceNames: []string{"foo"}},
|
||||
|
||||
{Verbs: []string{"educate"}, APIGroups: []string{""}, Resources: []string{"dolphins"}},
|
||||
|
||||
// nil verbs are preserved in non-merge cases.
|
||||
// these are the pirates who don't do anything.
|
||||
{Verbs: nil, APIGroups: []string{""}, Resources: []string{"pirates"}},
|
||||
|
||||
// Test merging into a nil Verbs string set
|
||||
{Verbs: nil, APIGroups: []string{""}, Resources: []string{"pods"}},
|
||||
{Verbs: []string{"create"}, APIGroups: []string{""}, Resources: []string{"pods"}},
|
||||
},
|
||||
Expected: []rbac.PolicyRule{
|
||||
{Verbs: []string{"create", "delete"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}},
|
||||
{Verbs: []string{"patch"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}, ResourceNames: []string{""}},
|
||||
{Verbs: []string{"get", "list"}, APIGroups: []string{"extensions"}, Resources: []string{"daemonsets"}, ResourceNames: []string{"foo"}},
|
||||
{Verbs: []string{"get", "list", "update", "patch"}, APIGroups: []string{""}, Resources: []string{"builds"}},
|
||||
{Verbs: []string{"educate"}, APIGroups: []string{""}, Resources: []string{"dolphins"}},
|
||||
{Verbs: nil, APIGroups: []string{""}, Resources: []string{"pirates"}},
|
||||
{Verbs: []string{"create"}, APIGroups: []string{""}, Resources: []string{"pods"}},
|
||||
},
|
||||
},
|
||||
"complex multi-group": {
|
||||
Rules: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{"", "builds.openshift.io"}, Resources: []string{"builds"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{"", "builds.openshift.io"}, Resources: []string{"builds"}},
|
||||
},
|
||||
Expected: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{"", "builds.openshift.io"}, Resources: []string{"builds"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{"", "builds.openshift.io"}, Resources: []string{"builds"}},
|
||||
},
|
||||
},
|
||||
|
||||
"complex multi-resource": {
|
||||
Rules: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds", "images"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{""}, Resources: []string{"builds", "images"}},
|
||||
},
|
||||
Expected: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds", "images"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{""}, Resources: []string{"builds", "images"}},
|
||||
},
|
||||
},
|
||||
|
||||
"complex named-resource": {
|
||||
Rules: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, ResourceNames: []string{"mybuild"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{""}, Resources: []string{"builds"}, ResourceNames: []string{"mybuild2"}},
|
||||
},
|
||||
Expected: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, ResourceNames: []string{"mybuild"}},
|
||||
{Verbs: []string{"list"}, APIGroups: []string{""}, Resources: []string{"builds"}, ResourceNames: []string{"mybuild2"}},
|
||||
},
|
||||
},
|
||||
|
||||
"complex non-resource": {
|
||||
Rules: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, NonResourceURLs: []string{"/"}},
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, NonResourceURLs: []string{"/foo"}},
|
||||
},
|
||||
Expected: []rbac.PolicyRule{
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, NonResourceURLs: []string{"/"}},
|
||||
{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, NonResourceURLs: []string{"/foo"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range testcases {
|
||||
rules := tc.Rules
|
||||
originalRules := make([]rbac.PolicyRule, len(tc.Rules))
|
||||
for i := range tc.Rules {
|
||||
originalRules[i] = *tc.Rules[i].DeepCopy()
|
||||
}
|
||||
compacted, err := CompactRules(tc.Rules)
|
||||
if err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", k, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(rules, originalRules) {
|
||||
t.Errorf("%s: CompactRules mutated rules. Expected\n%#v\ngot\n%#v", k, originalRules, rules)
|
||||
continue
|
||||
}
|
||||
if covers, missing := Covers(compacted, rules); !covers {
|
||||
t.Errorf("%s: compacted rules did not cover original rules. missing: %#v", k, missing)
|
||||
continue
|
||||
}
|
||||
if covers, missing := Covers(rules, compacted); !covers {
|
||||
t.Errorf("%s: original rules did not cover compacted rules. missing: %#v", k, missing)
|
||||
continue
|
||||
}
|
||||
|
||||
sort.Stable(rbac.SortableRuleSlice(compacted))
|
||||
sort.Stable(rbac.SortableRuleSlice(tc.Expected))
|
||||
if !reflect.DeepEqual(compacted, tc.Expected) {
|
||||
t.Errorf("%s: Expected\n%#v\ngot\n%#v", k, tc.Expected, compacted)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSimpleResourceRule(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
Rule rbac.PolicyRule
|
||||
Simple bool
|
||||
Resource simpleResource
|
||||
}{
|
||||
"simple, no verbs": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{}, APIGroups: []string{""}, Resources: []string{"builds"}},
|
||||
Simple: true,
|
||||
Resource: simpleResource{Group: "", Resource: "builds"},
|
||||
},
|
||||
"simple, one verb": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}},
|
||||
Simple: true,
|
||||
Resource: simpleResource{Group: "", Resource: "builds"},
|
||||
},
|
||||
"simple, one empty resource name": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, ResourceNames: []string{""}},
|
||||
Simple: true,
|
||||
Resource: simpleResource{Group: "", Resource: "builds", ResourceNameExist: true, ResourceName: ""},
|
||||
},
|
||||
"simple, one resource name": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, ResourceNames: []string{"foo"}},
|
||||
Simple: true,
|
||||
Resource: simpleResource{Group: "", Resource: "builds", ResourceNameExist: true, ResourceName: "foo"},
|
||||
},
|
||||
"simple, multi verb": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get", "list"}, APIGroups: []string{""}, Resources: []string{"builds"}},
|
||||
Simple: true,
|
||||
Resource: simpleResource{Group: "", Resource: "builds"},
|
||||
},
|
||||
|
||||
"complex, empty": {
|
||||
Rule: rbac.PolicyRule{},
|
||||
Simple: false,
|
||||
Resource: simpleResource{},
|
||||
},
|
||||
"complex, no group": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{}, Resources: []string{"builds"}},
|
||||
Simple: false,
|
||||
Resource: simpleResource{},
|
||||
},
|
||||
"complex, multi group": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{"a", "b"}, Resources: []string{"builds"}},
|
||||
Simple: false,
|
||||
Resource: simpleResource{},
|
||||
},
|
||||
"complex, no resource": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{}},
|
||||
Simple: false,
|
||||
Resource: simpleResource{},
|
||||
},
|
||||
"complex, multi resource": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds", "images"}},
|
||||
Simple: false,
|
||||
Resource: simpleResource{},
|
||||
},
|
||||
"complex, resource names": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, ResourceNames: []string{"foo", "bar"}},
|
||||
Simple: false,
|
||||
Resource: simpleResource{},
|
||||
},
|
||||
"complex, non-resource urls": {
|
||||
Rule: rbac.PolicyRule{Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"builds"}, NonResourceURLs: []string{"/"}},
|
||||
Simple: false,
|
||||
Resource: simpleResource{},
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range testcases {
|
||||
resource, simple := isSimpleResourceRule(&tc.Rule)
|
||||
if simple != tc.Simple {
|
||||
t.Errorf("%s: expected simple=%v, got simple=%v", k, tc.Simple, simple)
|
||||
continue
|
||||
}
|
||||
if resource != tc.Resource {
|
||||
t.Errorf("%s: expected resource=%v, got resource=%v", k, tc.Resource, resource)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
173
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_comparator.go
generated
vendored
Normal file
173
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_comparator.go
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// Covers determines whether or not the ownerRules cover the servantRules in terms of allowed actions.
|
||||
// It returns whether or not the ownerRules cover and a list of the rules that the ownerRules do not cover.
|
||||
func Covers(ownerRules, servantRules []rbac.PolicyRule) (bool, []rbac.PolicyRule) {
|
||||
// 1. Break every servantRule into individual rule tuples: group, verb, resource, resourceName
|
||||
// 2. Compare the mini-rules against each owner rule. Because the breakdown is down to the most atomic level, we're guaranteed that each mini-servant rule will be either fully covered or not covered by a single owner rule
|
||||
// 3. Any left over mini-rules means that we are not covered and we have a nice list of them.
|
||||
// TODO: it might be nice to collapse the list down into something more human readable
|
||||
|
||||
subrules := []rbac.PolicyRule{}
|
||||
for _, servantRule := range servantRules {
|
||||
subrules = append(subrules, BreakdownRule(servantRule)...)
|
||||
}
|
||||
|
||||
uncoveredRules := []rbac.PolicyRule{}
|
||||
for _, subrule := range subrules {
|
||||
covered := false
|
||||
for _, ownerRule := range ownerRules {
|
||||
if ruleCovers(ownerRule, subrule) {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !covered {
|
||||
uncoveredRules = append(uncoveredRules, subrule)
|
||||
}
|
||||
}
|
||||
|
||||
return (len(uncoveredRules) == 0), uncoveredRules
|
||||
}
|
||||
|
||||
// BreadownRule takes a rule and builds an equivalent list of rules that each have at most one verb, one
|
||||
// resource, and one resource name
|
||||
func BreakdownRule(rule rbac.PolicyRule) []rbac.PolicyRule {
|
||||
subrules := []rbac.PolicyRule{}
|
||||
for _, group := range rule.APIGroups {
|
||||
for _, resource := range rule.Resources {
|
||||
for _, verb := range rule.Verbs {
|
||||
if len(rule.ResourceNames) > 0 {
|
||||
for _, resourceName := range rule.ResourceNames {
|
||||
subrules = append(subrules, rbac.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}, ResourceNames: []string{resourceName}})
|
||||
}
|
||||
|
||||
} else {
|
||||
subrules = append(subrules, rbac.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Non-resource URLs are unique because they only combine with verbs.
|
||||
for _, nonResourceURL := range rule.NonResourceURLs {
|
||||
for _, verb := range rule.Verbs {
|
||||
subrules = append(subrules, rbac.PolicyRule{NonResourceURLs: []string{nonResourceURL}, Verbs: []string{verb}})
|
||||
}
|
||||
}
|
||||
|
||||
return subrules
|
||||
}
|
||||
|
||||
func has(set []string, ele string) bool {
|
||||
for _, s := range set {
|
||||
if s == ele {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasAll(set, contains []string) bool {
|
||||
owning := make(map[string]struct{}, len(set))
|
||||
for _, ele := range set {
|
||||
owning[ele] = struct{}{}
|
||||
}
|
||||
for _, ele := range contains {
|
||||
if _, ok := owning[ele]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func resourceCoversAll(setResources, coversResources []string) bool {
|
||||
// if we have a star or an exact match on all resources, then we match
|
||||
if has(setResources, rbac.ResourceAll) || hasAll(setResources, coversResources) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, path := range coversResources {
|
||||
// if we have an exact match, then we match.
|
||||
if has(setResources, path) {
|
||||
continue
|
||||
}
|
||||
// if we're not a subresource, then we definitely don't match. fail.
|
||||
if !strings.Contains(path, "/") {
|
||||
return false
|
||||
}
|
||||
tokens := strings.SplitN(path, "/", 2)
|
||||
resourceToCheck := "*/" + tokens[1]
|
||||
if !has(setResources, resourceToCheck) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func nonResourceURLsCoversAll(set, covers []string) bool {
|
||||
for _, path := range covers {
|
||||
covered := false
|
||||
for _, owner := range set {
|
||||
if nonResourceURLCovers(owner, path) {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !covered {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func nonResourceURLCovers(ownerPath, subPath string) bool {
|
||||
if ownerPath == subPath {
|
||||
return true
|
||||
}
|
||||
return strings.HasSuffix(ownerPath, "*") && strings.HasPrefix(subPath, strings.TrimRight(ownerPath, "*"))
|
||||
}
|
||||
|
||||
// ruleCovers determines whether the ownerRule (which may have multiple verbs, resources, and resourceNames) covers
|
||||
// the subrule (which may only contain at most one verb, resource, and resourceName)
|
||||
func ruleCovers(ownerRule, subRule rbac.PolicyRule) bool {
|
||||
verbMatches := has(ownerRule.Verbs, rbac.VerbAll) || hasAll(ownerRule.Verbs, subRule.Verbs)
|
||||
groupMatches := has(ownerRule.APIGroups, rbac.APIGroupAll) || hasAll(ownerRule.APIGroups, subRule.APIGroups)
|
||||
resourceMatches := resourceCoversAll(ownerRule.Resources, subRule.Resources)
|
||||
nonResourceURLMatches := nonResourceURLsCoversAll(ownerRule.NonResourceURLs, subRule.NonResourceURLs)
|
||||
|
||||
resourceNameMatches := false
|
||||
|
||||
if len(subRule.ResourceNames) == 0 {
|
||||
resourceNameMatches = (len(ownerRule.ResourceNames) == 0)
|
||||
} else {
|
||||
resourceNameMatches = (len(ownerRule.ResourceNames) == 0) || hasAll(ownerRule.ResourceNames, subRule.ResourceNames)
|
||||
}
|
||||
|
||||
return verbMatches && groupMatches && resourceMatches && resourceNameMatches && nonResourceURLMatches
|
||||
}
|
446
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_comparator_test.go
generated
vendored
Normal file
446
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_comparator_test.go
generated
vendored
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
type escalationTest struct {
|
||||
ownerRules []rbac.PolicyRule
|
||||
servantRules []rbac.PolicyRule
|
||||
|
||||
expectedCovered bool
|
||||
expectedUncoveredRules []rbac.PolicyRule
|
||||
}
|
||||
|
||||
func TestCoversExactMatch(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversSubresourceWildcard(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*/scale"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"foo/scale"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversMultipleRulesCoveringSingleRule(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
|
||||
}
|
||||
|
||||
func TestCoversMultipleAPIGroupsCoveringSingleRule(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
|
||||
}
|
||||
|
||||
func TestCoversSingleAPIGroupsCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
|
||||
}
|
||||
|
||||
func TestCoversMultipleRulesMissingSingleVerbResourceCombination(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"pods"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments", "pods"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"update"}, Resources: []string{"pods"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversAPIGroupStarCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringAPIGroupStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"dummy-group"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversAPIGroupStarCoveringStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversVerbStarCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"watch", "list"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringVerbStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get", "list", "watch", "create", "update", "delete", "exec"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversVerbStarCoveringStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversResourceStarCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"resourcegroup:deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringResourceStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"roles", "resourcegroup:deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversResourceStarCoveringStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversResourceNameEmptyCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{"foo", "bar"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringResourceNameEmpty(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{"foo", "bar"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLs(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis"}, Verbs: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis"}, Verbs: []string{"*"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"*"}, Verbs: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis", "/apis/v1", "/"}, Verbs: []string{"*"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsStarAfterPrefixDoesntCover(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis/*"}, Verbs: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis", "/apis/v1"}, Verbs: []string{"get"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis"}, Verbs: []string{"get"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsStarAfterPrefix(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis/*"}, Verbs: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis/v1/foo", "/apis/v1"}, Verbs: []string{"get"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsWithOtherFields(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsWithOtherFieldsFailure(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{{NonResourceURLs: []string{"/apis"}, Verbs: []string{"get"}}},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func (test escalationTest) test(t *testing.T) {
|
||||
actualCovered, actualUncoveredRules := Covers(test.ownerRules, test.servantRules)
|
||||
|
||||
if actualCovered != test.expectedCovered {
|
||||
t.Errorf("expected %v, but got %v", test.expectedCovered, actualCovered)
|
||||
}
|
||||
|
||||
if !rulesMatch(test.expectedUncoveredRules, actualUncoveredRules) {
|
||||
t.Errorf("expected %v, but got %v", test.expectedUncoveredRules, actualUncoveredRules)
|
||||
}
|
||||
}
|
||||
|
||||
func rulesMatch(expectedRules, actualRules []rbac.PolicyRule) bool {
|
||||
if len(expectedRules) != len(actualRules) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, expectedRule := range expectedRules {
|
||||
found := false
|
||||
for _, actualRule := range actualRules {
|
||||
if reflect.DeepEqual(expectedRule, actualRule) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestNonResourceURLCovers(t *testing.T) {
|
||||
tests := []struct {
|
||||
owner string
|
||||
requested string
|
||||
want bool
|
||||
}{
|
||||
{"*", "", true},
|
||||
{"*", "/", true},
|
||||
{"*", "/api", true},
|
||||
{"/*", "", false},
|
||||
{"/*", "/", true},
|
||||
{"/*", "/foo", true},
|
||||
{"/api", "/api", true},
|
||||
{"/apis", "/api", false},
|
||||
{"/api/v1", "/api", false},
|
||||
{"/api/v1", "/api/v1", true},
|
||||
{"/api/*", "/api/v1", true},
|
||||
{"/api/*", "/api", false},
|
||||
{"/api/*/*", "/api/v1", false},
|
||||
{"/*/v1/*", "/api/v1", false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
got := nonResourceURLCovers(tc.owner, tc.requested)
|
||||
if got != tc.want {
|
||||
t.Errorf("nonResourceURLCovers(%q, %q): want=(%t), got=(%t)", tc.owner, tc.requested, tc.want, got)
|
||||
}
|
||||
}
|
||||
}
|
288
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/rule.go
generated
vendored
Normal file
288
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/rule.go
generated
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
type AuthorizationRuleResolver interface {
|
||||
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namepsace
|
||||
// of the role binding, the empty string if a cluster role binding.
|
||||
GetRoleReferenceRules(roleRef rbac.RoleRef, namespace string) ([]rbac.PolicyRule, error)
|
||||
|
||||
// RulesFor returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of
|
||||
// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations
|
||||
// can be made on the basis of those rules that are found.
|
||||
RulesFor(user user.Info, namespace string) ([]rbac.PolicyRule, error)
|
||||
|
||||
// VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace, and each error encountered resolving those rules.
|
||||
// If visitor() returns false, visiting is short-circuited.
|
||||
VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, err error) bool)
|
||||
}
|
||||
|
||||
// ConfirmNoEscalation determines if the roles for a given user in a given namespace encompass the provided role.
|
||||
func ConfirmNoEscalation(ctx genericapirequest.Context, ruleResolver AuthorizationRuleResolver, rules []rbac.PolicyRule) error {
|
||||
ruleResolutionErrors := []error{}
|
||||
|
||||
user, ok := genericapirequest.UserFrom(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("no user on context")
|
||||
}
|
||||
namespace, _ := genericapirequest.NamespaceFrom(ctx)
|
||||
|
||||
ownerRules, err := ruleResolver.RulesFor(user, namespace)
|
||||
if err != nil {
|
||||
// As per AuthorizationRuleResolver contract, this may return a non fatal error with an incomplete list of policies. Log the error and continue.
|
||||
glog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err)
|
||||
ruleResolutionErrors = append(ruleResolutionErrors, err)
|
||||
}
|
||||
|
||||
ownerRightsCover, missingRights := Covers(ownerRules, rules)
|
||||
if !ownerRightsCover {
|
||||
return apierrors.NewUnauthorized(fmt.Sprintf("attempt to grant extra privileges: %v user=%v ownerrules=%v ruleResolutionErrors=%v", missingRights, user, ownerRules, ruleResolutionErrors))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DefaultRuleResolver struct {
|
||||
roleGetter RoleGetter
|
||||
roleBindingLister RoleBindingLister
|
||||
clusterRoleGetter ClusterRoleGetter
|
||||
clusterRoleBindingLister ClusterRoleBindingLister
|
||||
}
|
||||
|
||||
func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBindingLister, clusterRoleGetter ClusterRoleGetter, clusterRoleBindingLister ClusterRoleBindingLister) *DefaultRuleResolver {
|
||||
return &DefaultRuleResolver{roleGetter, roleBindingLister, clusterRoleGetter, clusterRoleBindingLister}
|
||||
}
|
||||
|
||||
type RoleGetter interface {
|
||||
GetRole(namespace, name string) (*rbac.Role, error)
|
||||
}
|
||||
|
||||
type RoleBindingLister interface {
|
||||
ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error)
|
||||
}
|
||||
|
||||
type ClusterRoleGetter interface {
|
||||
GetClusterRole(name string) (*rbac.ClusterRole, error)
|
||||
}
|
||||
|
||||
type ClusterRoleBindingLister interface {
|
||||
ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error)
|
||||
}
|
||||
|
||||
func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbac.PolicyRule, error) {
|
||||
visitor := &ruleAccumulator{}
|
||||
r.VisitRulesFor(user, namespace, visitor.visit)
|
||||
return visitor.rules, utilerrors.NewAggregate(visitor.errors)
|
||||
}
|
||||
|
||||
type ruleAccumulator struct {
|
||||
rules []rbac.PolicyRule
|
||||
errors []error
|
||||
}
|
||||
|
||||
func (r *ruleAccumulator) visit(rule *rbac.PolicyRule, err error) bool {
|
||||
if rule != nil {
|
||||
r.rules = append(r.rules, *rule)
|
||||
}
|
||||
if err != nil {
|
||||
r.errors = append(r.errors, err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, err error) bool) {
|
||||
if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil {
|
||||
if !visitor(nil, err) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
for _, clusterRoleBinding := range clusterRoleBindings {
|
||||
if !appliesTo(user, clusterRoleBinding.Subjects, "") {
|
||||
continue
|
||||
}
|
||||
rules, err := r.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
|
||||
if err != nil {
|
||||
if !visitor(nil, err) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
for i := range rules {
|
||||
if !visitor(&rules[i], nil) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(namespace) > 0 {
|
||||
if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil {
|
||||
if !visitor(nil, err) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
for _, roleBinding := range roleBindings {
|
||||
if !appliesTo(user, roleBinding.Subjects, namespace) {
|
||||
continue
|
||||
}
|
||||
rules, err := r.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
|
||||
if err != nil {
|
||||
if !visitor(nil, err) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
for i := range rules {
|
||||
if !visitor(&rules[i], nil) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding.
|
||||
func (r *DefaultRuleResolver) GetRoleReferenceRules(roleRef rbac.RoleRef, bindingNamespace string) ([]rbac.PolicyRule, error) {
|
||||
switch kind := rbac.RoleRefGroupKind(roleRef); kind {
|
||||
case rbac.Kind("Role"):
|
||||
role, err := r.roleGetter.GetRole(bindingNamespace, roleRef.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return role.Rules, nil
|
||||
|
||||
case rbac.Kind("ClusterRole"):
|
||||
clusterRole, err := r.clusterRoleGetter.GetClusterRole(roleRef.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clusterRole.Rules, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported role reference kind: %q", kind)
|
||||
}
|
||||
}
|
||||
func appliesTo(user user.Info, bindingSubjects []rbac.Subject, namespace string) bool {
|
||||
for _, bindingSubject := range bindingSubjects {
|
||||
if appliesToUser(user, bindingSubject, namespace) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func appliesToUser(user user.Info, subject rbac.Subject, namespace string) bool {
|
||||
switch subject.Kind {
|
||||
case rbac.UserKind:
|
||||
return user.GetName() == subject.Name
|
||||
|
||||
case rbac.GroupKind:
|
||||
return has(user.GetGroups(), subject.Name)
|
||||
|
||||
case rbac.ServiceAccountKind:
|
||||
// default the namespace to namespace we're working in if its available. This allows rolebindings that reference
|
||||
// SAs in th local namespace to avoid having to qualify them.
|
||||
saNamespace := namespace
|
||||
if len(subject.Namespace) > 0 {
|
||||
saNamespace = subject.Namespace
|
||||
}
|
||||
if len(saNamespace) == 0 {
|
||||
return false
|
||||
}
|
||||
return serviceaccount.MakeUsername(saNamespace, subject.Name) == user.GetName()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestRuleResolver returns a rule resolver from lists of role objects.
|
||||
func NewTestRuleResolver(roles []*rbac.Role, roleBindings []*rbac.RoleBinding, clusterRoles []*rbac.ClusterRole, clusterRoleBindings []*rbac.ClusterRoleBinding) (AuthorizationRuleResolver, *StaticRoles) {
|
||||
r := StaticRoles{
|
||||
roles: roles,
|
||||
roleBindings: roleBindings,
|
||||
clusterRoles: clusterRoles,
|
||||
clusterRoleBindings: clusterRoleBindings,
|
||||
}
|
||||
return newMockRuleResolver(&r), &r
|
||||
}
|
||||
|
||||
func newMockRuleResolver(r *StaticRoles) AuthorizationRuleResolver {
|
||||
return NewDefaultRuleResolver(r, r, r, r)
|
||||
}
|
||||
|
||||
// StaticRoles is a rule resolver that resolves from lists of role objects.
|
||||
type StaticRoles struct {
|
||||
roles []*rbac.Role
|
||||
roleBindings []*rbac.RoleBinding
|
||||
clusterRoles []*rbac.ClusterRole
|
||||
clusterRoleBindings []*rbac.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (r *StaticRoles) GetRole(namespace, name string) (*rbac.Role, error) {
|
||||
if len(namespace) == 0 {
|
||||
return nil, errors.New("must provide namespace when getting role")
|
||||
}
|
||||
for _, role := range r.roles {
|
||||
if role.Namespace == namespace && role.Name == name {
|
||||
return role, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
func (r *StaticRoles) GetClusterRole(name string) (*rbac.ClusterRole, error) {
|
||||
for _, clusterRole := range r.clusterRoles {
|
||||
if clusterRole.Name == name {
|
||||
return clusterRole, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
|
||||
if len(namespace) == 0 {
|
||||
return nil, errors.New("must provide namespace when listing role bindings")
|
||||
}
|
||||
|
||||
roleBindingList := []*rbac.RoleBinding{}
|
||||
for _, roleBinding := range r.roleBindings {
|
||||
if roleBinding.Namespace != namespace {
|
||||
continue
|
||||
}
|
||||
// TODO(ericchiang): need to implement label selectors?
|
||||
roleBindingList = append(roleBindingList, roleBinding)
|
||||
}
|
||||
return roleBindingList, nil
|
||||
}
|
||||
|
||||
func (r *StaticRoles) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
|
||||
return r.clusterRoleBindings, nil
|
||||
}
|
266
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/rule_test.go
generated
vendored
Normal file
266
vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/rule_test.go
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// compute a hash of a policy rule so we can sort in a deterministic order
|
||||
func hashOf(p rbac.PolicyRule) string {
|
||||
hash := fnv.New32()
|
||||
writeStrings := func(slis ...[]string) {
|
||||
for _, sli := range slis {
|
||||
for _, s := range sli {
|
||||
io.WriteString(hash, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs)
|
||||
return string(hash.Sum(nil))
|
||||
}
|
||||
|
||||
// byHash sorts a set of policy rules by a hash of its fields
|
||||
type byHash []rbac.PolicyRule
|
||||
|
||||
func (b byHash) Len() int { return len(b) }
|
||||
func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) }
|
||||
func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
|
||||
func TestDefaultRuleResolver(t *testing.T) {
|
||||
ruleReadPods := rbac.PolicyRule{
|
||||
Verbs: []string{"GET", "WATCH"},
|
||||
APIGroups: []string{"v1"},
|
||||
Resources: []string{"pods"},
|
||||
}
|
||||
ruleReadServices := rbac.PolicyRule{
|
||||
Verbs: []string{"GET", "WATCH"},
|
||||
APIGroups: []string{"v1"},
|
||||
Resources: []string{"services"},
|
||||
}
|
||||
ruleWriteNodes := rbac.PolicyRule{
|
||||
Verbs: []string{"PUT", "CREATE", "UPDATE"},
|
||||
APIGroups: []string{"v1"},
|
||||
Resources: []string{"nodes"},
|
||||
}
|
||||
ruleAdmin := rbac.PolicyRule{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"*"},
|
||||
Resources: []string{"*"},
|
||||
}
|
||||
|
||||
staticRoles1 := StaticRoles{
|
||||
roles: []*rbac.Role{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
|
||||
Rules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
|
||||
},
|
||||
},
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "cluster-admin"},
|
||||
Rules: []rbac.PolicyRule{ruleAdmin},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "write-nodes"},
|
||||
Rules: []rbac.PolicyRule{ruleWriteNodes},
|
||||
},
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1"},
|
||||
Subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
{Kind: rbac.GroupKind, Name: "group1"},
|
||||
},
|
||||
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "readthings"},
|
||||
},
|
||||
},
|
||||
clusterRoleBindings: []*rbac.ClusterRoleBinding{
|
||||
{
|
||||
Subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "admin"},
|
||||
{Kind: rbac.GroupKind, Name: "admin"},
|
||||
},
|
||||
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "cluster-admin"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
StaticRoles
|
||||
|
||||
// For a given context, what are the rules that apply?
|
||||
user user.Info
|
||||
namespace string
|
||||
effectiveRules []rbac.PolicyRule
|
||||
}{
|
||||
{
|
||||
StaticRoles: staticRoles1,
|
||||
user: &user.DefaultInfo{Name: "foobar"},
|
||||
namespace: "namespace1",
|
||||
effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
|
||||
},
|
||||
{
|
||||
StaticRoles: staticRoles1,
|
||||
user: &user.DefaultInfo{Name: "foobar"},
|
||||
namespace: "namespace2",
|
||||
effectiveRules: nil,
|
||||
},
|
||||
{
|
||||
StaticRoles: staticRoles1,
|
||||
// Same as above but without a namespace. Only cluster rules should apply.
|
||||
user: &user.DefaultInfo{Name: "foobar", Groups: []string{"admin"}},
|
||||
effectiveRules: []rbac.PolicyRule{ruleAdmin},
|
||||
},
|
||||
{
|
||||
StaticRoles: staticRoles1,
|
||||
user: &user.DefaultInfo{},
|
||||
effectiveRules: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
ruleResolver := newMockRuleResolver(&tc.StaticRoles)
|
||||
rules, err := ruleResolver.RulesFor(tc.user, tc.namespace)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Sort for deep equals
|
||||
sort.Sort(byHash(rules))
|
||||
sort.Sort(byHash(tc.effectiveRules))
|
||||
|
||||
if !reflect.DeepEqual(rules, tc.effectiveRules) {
|
||||
ruleDiff := diff.ObjectDiff(rules, tc.effectiveRules)
|
||||
t.Errorf("case %d: %s", i, ruleDiff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppliesTo(t *testing.T) {
|
||||
tests := []struct {
|
||||
subjects []rbac.Subject
|
||||
user user.Info
|
||||
namespace string
|
||||
appliesTo bool
|
||||
testCase string
|
||||
}{
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "foobar"},
|
||||
appliesTo: true,
|
||||
testCase: "single subject that matches username",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "foobar"},
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects, one that matches username",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "zimzam"},
|
||||
appliesTo: false,
|
||||
testCase: "multiple subjects, none that match username",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.GroupKind, Name: "foobar"},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}},
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects, one that match group",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.GroupKind, Name: "foobar"},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}},
|
||||
namespace: "namespace1",
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects, one that match group, should ignore namespace",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.GroupKind, Name: "foobar"},
|
||||
{Kind: rbac.ServiceAccountKind, Namespace: "kube-system", Name: "default"},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"},
|
||||
namespace: "default",
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects with a service account that matches",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "*"},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "foobar"},
|
||||
namespace: "default",
|
||||
appliesTo: false,
|
||||
testCase: "* user subject name doesn't match all users",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.AllAuthenticated},
|
||||
{Kind: rbac.GroupKind, Name: user.AllUnauthenticated},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "foobar", Groups: []string{user.AllAuthenticated}},
|
||||
namespace: "default",
|
||||
appliesTo: true,
|
||||
testCase: "binding to all authenticated and unauthenticated subjects matches authenticated user",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.AllAuthenticated},
|
||||
{Kind: rbac.GroupKind, Name: user.AllUnauthenticated},
|
||||
},
|
||||
user: &user.DefaultInfo{Name: "system:anonymous", Groups: []string{user.AllUnauthenticated}},
|
||||
namespace: "default",
|
||||
appliesTo: true,
|
||||
testCase: "binding to all authenticated and unauthenticated subjects matches anonymous user",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
got := appliesTo(tc.user, tc.subjects, tc.namespace)
|
||||
if got != tc.appliesTo {
|
||||
t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user