123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- /*
- 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 (
- "k8s.io/apimachinery/pkg/api/validation/path"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
- "k8s.io/apimachinery/pkg/util/validation/field"
- "k8s.io/kubernetes/pkg/apis/core/validation"
- "k8s.io/kubernetes/pkg/apis/rbac"
- )
- // ValidateRBACName is exported to allow types outside of the RBAC API group to reuse this validation logic
- // Minimal validation of names for roles and bindings. Identical to the validation for Openshift. See:
- // * https://github.com/kubernetes/kubernetes/blob/60db50/pkg/api/validation/name.go
- // * https://github.com/openshift/origin/blob/388478/pkg/api/helpers.go
- func ValidateRBACName(name string, prefix bool) []string {
- return path.IsValidPathSegmentName(name)
- }
- func ValidateRole(role *rbac.Role) field.ErrorList {
- allErrs := field.ErrorList{}
- allErrs = append(allErrs, validation.ValidateObjectMeta(&role.ObjectMeta, true, ValidateRBACName, field.NewPath("metadata"))...)
- for i, rule := range role.Rules {
- if err := ValidatePolicyRule(rule, true, field.NewPath("rules").Index(i)); err != nil {
- allErrs = append(allErrs, err...)
- }
- }
- if len(allErrs) != 0 {
- return allErrs
- }
- return nil
- }
- func ValidateRoleUpdate(role *rbac.Role, oldRole *rbac.Role) field.ErrorList {
- allErrs := ValidateRole(role)
- allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&role.ObjectMeta, &oldRole.ObjectMeta, field.NewPath("metadata"))...)
- return allErrs
- }
- func ValidateClusterRole(role *rbac.ClusterRole) field.ErrorList {
- allErrs := field.ErrorList{}
- allErrs = append(allErrs, validation.ValidateObjectMeta(&role.ObjectMeta, false, ValidateRBACName, field.NewPath("metadata"))...)
- for i, rule := range role.Rules {
- if err := ValidatePolicyRule(rule, false, field.NewPath("rules").Index(i)); err != nil {
- allErrs = append(allErrs, err...)
- }
- }
- if role.AggregationRule != nil {
- if len(role.AggregationRule.ClusterRoleSelectors) == 0 {
- allErrs = append(allErrs, field.Required(field.NewPath("aggregationRule", "clusterRoleSelectors"), "at least one clusterRoleSelector required if aggregationRule is non-nil"))
- }
- for i, selector := range role.AggregationRule.ClusterRoleSelectors {
- fieldPath := field.NewPath("aggregationRule", "clusterRoleSelectors").Index(i)
- allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&selector, fieldPath)...)
- selector, err := metav1.LabelSelectorAsSelector(&selector)
- if err != nil {
- allErrs = append(allErrs, field.Invalid(fieldPath, selector, "invalid label selector."))
- }
- }
- }
- if len(allErrs) != 0 {
- return allErrs
- }
- return nil
- }
- func ValidateClusterRoleUpdate(role *rbac.ClusterRole, oldRole *rbac.ClusterRole) field.ErrorList {
- allErrs := ValidateClusterRole(role)
- allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&role.ObjectMeta, &oldRole.ObjectMeta, field.NewPath("metadata"))...)
- return allErrs
- }
- // ValidatePolicyRule is exported to allow types outside of the RBAC API group to embed a rbac.PolicyRule and reuse this validation logic
- func ValidatePolicyRule(rule rbac.PolicyRule, isNamespaced bool, fldPath *field.Path) field.ErrorList {
- allErrs := field.ErrorList{}
- if len(rule.Verbs) == 0 {
- allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
- }
- if len(rule.NonResourceURLs) > 0 {
- if isNamespaced {
- allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "namespaced rules cannot apply to non-resource URLs"))
- }
- if len(rule.APIGroups) > 0 || len(rule.Resources) > 0 || len(rule.ResourceNames) > 0 {
- allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "rules cannot apply to both regular resources and non-resource URLs"))
- }
- return allErrs
- }
- if len(rule.APIGroups) == 0 {
- allErrs = append(allErrs, field.Required(fldPath.Child("apiGroups"), "resource rules must supply at least one api group"))
- }
- if len(rule.Resources) == 0 {
- allErrs = append(allErrs, field.Required(fldPath.Child("resources"), "resource rules must supply at least one resource"))
- }
- return allErrs
- }
- func ValidateRoleBinding(roleBinding *rbac.RoleBinding) field.ErrorList {
- allErrs := field.ErrorList{}
- allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, true, ValidateRBACName, field.NewPath("metadata"))...)
- // TODO allow multiple API groups. For now, restrict to one, but I can envision other experimental roles in other groups taking
- // advantage of the binding infrastructure
- if roleBinding.RoleRef.APIGroup != rbac.GroupName {
- allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName}))
- }
- switch roleBinding.RoleRef.Kind {
- case "Role", "ClusterRole":
- default:
- allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"Role", "ClusterRole"}))
- }
- if len(roleBinding.RoleRef.Name) == 0 {
- allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), ""))
- } else {
- for _, msg := range ValidateRBACName(roleBinding.RoleRef.Name, false) {
- allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg))
- }
- }
- subjectsPath := field.NewPath("subjects")
- for i, subject := range roleBinding.Subjects {
- allErrs = append(allErrs, ValidateRoleBindingSubject(subject, true, subjectsPath.Index(i))...)
- }
- return allErrs
- }
- func ValidateRoleBindingUpdate(roleBinding *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding) field.ErrorList {
- allErrs := ValidateRoleBinding(roleBinding)
- allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...)
- if oldRoleBinding.RoleRef != roleBinding.RoleRef {
- allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef"), roleBinding.RoleRef, "cannot change roleRef"))
- }
- return allErrs
- }
- func ValidateClusterRoleBinding(roleBinding *rbac.ClusterRoleBinding) field.ErrorList {
- allErrs := field.ErrorList{}
- allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, false, ValidateRBACName, field.NewPath("metadata"))...)
- // TODO allow multiple API groups. For now, restrict to one, but I can envision other experimental roles in other groups taking
- // advantage of the binding infrastructure
- if roleBinding.RoleRef.APIGroup != rbac.GroupName {
- allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName}))
- }
- switch roleBinding.RoleRef.Kind {
- case "ClusterRole":
- default:
- allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"ClusterRole"}))
- }
- if len(roleBinding.RoleRef.Name) == 0 {
- allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), ""))
- } else {
- for _, msg := range ValidateRBACName(roleBinding.RoleRef.Name, false) {
- allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg))
- }
- }
- subjectsPath := field.NewPath("subjects")
- for i, subject := range roleBinding.Subjects {
- allErrs = append(allErrs, ValidateRoleBindingSubject(subject, false, subjectsPath.Index(i))...)
- }
- return allErrs
- }
- func ValidateClusterRoleBindingUpdate(roleBinding *rbac.ClusterRoleBinding, oldRoleBinding *rbac.ClusterRoleBinding) field.ErrorList {
- allErrs := ValidateClusterRoleBinding(roleBinding)
- allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...)
- if oldRoleBinding.RoleRef != roleBinding.RoleRef {
- allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef"), roleBinding.RoleRef, "cannot change roleRef"))
- }
- return allErrs
- }
- // ValidateRoleBindingSubject is exported to allow types outside of the RBAC API group to embed a rbac.Subject and reuse this validation logic
- func ValidateRoleBindingSubject(subject rbac.Subject, isNamespaced bool, fldPath *field.Path) field.ErrorList {
- allErrs := field.ErrorList{}
- if len(subject.Name) == 0 {
- allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
- }
- switch subject.Kind {
- case rbac.ServiceAccountKind:
- if len(subject.Name) > 0 {
- for _, msg := range validation.ValidateServiceAccountName(subject.Name, false) {
- allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg))
- }
- }
- if len(subject.APIGroup) > 0 {
- allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{""}))
- }
- if !isNamespaced && len(subject.Namespace) == 0 {
- allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
- }
- case rbac.UserKind:
- // TODO(ericchiang): What other restrictions on user name are there?
- if subject.APIGroup != rbac.GroupName {
- allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{rbac.GroupName}))
- }
- case rbac.GroupKind:
- // TODO(ericchiang): What other restrictions on group name are there?
- if subject.APIGroup != rbac.GroupName {
- allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{rbac.GroupName}))
- }
- default:
- allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, []string{rbac.ServiceAccountKind, rbac.UserKind, rbac.GroupKind}))
- }
- return allErrs
- }
|