1
16
17 package validation
18
19 import (
20 "k8s.io/apimachinery/pkg/api/validation/path"
21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22 unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
23 "k8s.io/apimachinery/pkg/util/validation/field"
24 "k8s.io/kubernetes/pkg/apis/core/validation"
25 "k8s.io/kubernetes/pkg/apis/rbac"
26 )
27
28
29
30
31
32 func ValidateRBACName(name string, prefix bool) []string {
33 return path.IsValidPathSegmentName(name)
34 }
35
36 func ValidateRole(role *rbac.Role) field.ErrorList {
37 allErrs := field.ErrorList{}
38 allErrs = append(allErrs, validation.ValidateObjectMeta(&role.ObjectMeta, true, ValidateRBACName, field.NewPath("metadata"))...)
39
40 for i, rule := range role.Rules {
41 if err := ValidatePolicyRule(rule, true, field.NewPath("rules").Index(i)); err != nil {
42 allErrs = append(allErrs, err...)
43 }
44 }
45 if len(allErrs) != 0 {
46 return allErrs
47 }
48 return nil
49 }
50
51 func ValidateRoleUpdate(role *rbac.Role, oldRole *rbac.Role) field.ErrorList {
52 allErrs := ValidateRole(role)
53 allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&role.ObjectMeta, &oldRole.ObjectMeta, field.NewPath("metadata"))...)
54
55 return allErrs
56 }
57
58 type ClusterRoleValidationOptions struct {
59 AllowInvalidLabelValueInSelector bool
60 }
61
62 func ValidateClusterRole(role *rbac.ClusterRole, opts ClusterRoleValidationOptions) field.ErrorList {
63 allErrs := field.ErrorList{}
64 allErrs = append(allErrs, validation.ValidateObjectMeta(&role.ObjectMeta, false, ValidateRBACName, field.NewPath("metadata"))...)
65
66 for i, rule := range role.Rules {
67 if err := ValidatePolicyRule(rule, false, field.NewPath("rules").Index(i)); err != nil {
68 allErrs = append(allErrs, err...)
69 }
70 }
71
72 labelSelectorValidationOptions := unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector}
73
74 if role.AggregationRule != nil {
75 if len(role.AggregationRule.ClusterRoleSelectors) == 0 {
76 allErrs = append(allErrs, field.Required(field.NewPath("aggregationRule", "clusterRoleSelectors"), "at least one clusterRoleSelector required if aggregationRule is non-nil"))
77 }
78 for i, selector := range role.AggregationRule.ClusterRoleSelectors {
79 fieldPath := field.NewPath("aggregationRule", "clusterRoleSelectors").Index(i)
80 allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&selector, labelSelectorValidationOptions, fieldPath)...)
81
82 selector, err := metav1.LabelSelectorAsSelector(&selector)
83 if err != nil {
84 allErrs = append(allErrs, field.Invalid(fieldPath, selector, "invalid label selector."))
85 }
86 }
87 }
88
89 if len(allErrs) != 0 {
90 return allErrs
91 }
92 return nil
93 }
94
95 func ValidateClusterRoleUpdate(role *rbac.ClusterRole, oldRole *rbac.ClusterRole, opts ClusterRoleValidationOptions) field.ErrorList {
96 allErrs := ValidateClusterRole(role, opts)
97 allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&role.ObjectMeta, &oldRole.ObjectMeta, field.NewPath("metadata"))...)
98
99 return allErrs
100 }
101
102
103 func ValidatePolicyRule(rule rbac.PolicyRule, isNamespaced bool, fldPath *field.Path) field.ErrorList {
104 allErrs := field.ErrorList{}
105 if len(rule.Verbs) == 0 {
106 allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
107 }
108
109 if len(rule.NonResourceURLs) > 0 {
110 if isNamespaced {
111 allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "namespaced rules cannot apply to non-resource URLs"))
112 }
113 if len(rule.APIGroups) > 0 || len(rule.Resources) > 0 || len(rule.ResourceNames) > 0 {
114 allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "rules cannot apply to both regular resources and non-resource URLs"))
115 }
116 return allErrs
117 }
118
119 if len(rule.APIGroups) == 0 {
120 allErrs = append(allErrs, field.Required(fldPath.Child("apiGroups"), "resource rules must supply at least one api group"))
121 }
122 if len(rule.Resources) == 0 {
123 allErrs = append(allErrs, field.Required(fldPath.Child("resources"), "resource rules must supply at least one resource"))
124 }
125 return allErrs
126 }
127
128 func ValidateRoleBinding(roleBinding *rbac.RoleBinding) field.ErrorList {
129 allErrs := field.ErrorList{}
130 allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, true, ValidateRBACName, field.NewPath("metadata"))...)
131
132
133
134 if roleBinding.RoleRef.APIGroup != rbac.GroupName {
135 allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName}))
136 }
137
138 switch roleBinding.RoleRef.Kind {
139 case "Role", "ClusterRole":
140 default:
141 allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"Role", "ClusterRole"}))
142
143 }
144
145 if len(roleBinding.RoleRef.Name) == 0 {
146 allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), ""))
147 } else {
148 for _, msg := range ValidateRBACName(roleBinding.RoleRef.Name, false) {
149 allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg))
150 }
151 }
152
153 subjectsPath := field.NewPath("subjects")
154 for i, subject := range roleBinding.Subjects {
155 allErrs = append(allErrs, ValidateRoleBindingSubject(subject, true, subjectsPath.Index(i))...)
156 }
157
158 return allErrs
159 }
160
161 func ValidateRoleBindingUpdate(roleBinding *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding) field.ErrorList {
162 allErrs := ValidateRoleBinding(roleBinding)
163 allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...)
164
165 if oldRoleBinding.RoleRef != roleBinding.RoleRef {
166 allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef"), roleBinding.RoleRef, "cannot change roleRef"))
167 }
168
169 return allErrs
170 }
171
172 func ValidateClusterRoleBinding(roleBinding *rbac.ClusterRoleBinding) field.ErrorList {
173 allErrs := field.ErrorList{}
174 allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, false, ValidateRBACName, field.NewPath("metadata"))...)
175
176
177
178 if roleBinding.RoleRef.APIGroup != rbac.GroupName {
179 allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName}))
180 }
181
182 switch roleBinding.RoleRef.Kind {
183 case "ClusterRole":
184 default:
185 allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"ClusterRole"}))
186
187 }
188
189 if len(roleBinding.RoleRef.Name) == 0 {
190 allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), ""))
191 } else {
192 for _, msg := range ValidateRBACName(roleBinding.RoleRef.Name, false) {
193 allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg))
194 }
195 }
196
197 subjectsPath := field.NewPath("subjects")
198 for i, subject := range roleBinding.Subjects {
199 allErrs = append(allErrs, ValidateRoleBindingSubject(subject, false, subjectsPath.Index(i))...)
200 }
201
202 return allErrs
203 }
204
205 func ValidateClusterRoleBindingUpdate(roleBinding *rbac.ClusterRoleBinding, oldRoleBinding *rbac.ClusterRoleBinding) field.ErrorList {
206 allErrs := ValidateClusterRoleBinding(roleBinding)
207 allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...)
208
209 if oldRoleBinding.RoleRef != roleBinding.RoleRef {
210 allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef"), roleBinding.RoleRef, "cannot change roleRef"))
211 }
212
213 return allErrs
214 }
215
216
217 func ValidateRoleBindingSubject(subject rbac.Subject, isNamespaced bool, fldPath *field.Path) field.ErrorList {
218 allErrs := field.ErrorList{}
219
220 if len(subject.Name) == 0 {
221 allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
222 }
223
224 switch subject.Kind {
225 case rbac.ServiceAccountKind:
226 if len(subject.Name) > 0 {
227 for _, msg := range validation.ValidateServiceAccountName(subject.Name, false) {
228 allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg))
229 }
230 }
231 if len(subject.APIGroup) > 0 {
232 allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{""}))
233 }
234 if !isNamespaced && len(subject.Namespace) == 0 {
235 allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
236 }
237
238 case rbac.UserKind:
239
240 if subject.APIGroup != rbac.GroupName {
241 allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{rbac.GroupName}))
242 }
243
244 case rbac.GroupKind:
245
246 if subject.APIGroup != rbac.GroupName {
247 allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{rbac.GroupName}))
248 }
249
250 default:
251 allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, []string{rbac.ServiceAccountKind, rbac.UserKind, rbac.GroupKind}))
252 }
253
254 return allErrs
255 }
256
View as plain text