@@ -134,6 +134,10 @@ func ValidateClusterPolicyUpdate(policy *authorizationapi.ClusterPolicy, oldPoli
134
134
}
135
135
136
136
func ValidatePolicy (policy * authorizationapi.Policy , isNamespaced bool ) field.ErrorList {
137
+ return validatePolicy (policy , isNamespaced , false )
138
+ }
139
+
140
+ func validatePolicy (policy * authorizationapi.Policy , isNamespaced , skipRoleValidation bool ) field.ErrorList {
137
141
allErrs := validation .ValidateObjectMeta (& policy .ObjectMeta , isNamespaced , ValidatePolicyName , field .NewPath ("metadata" ))
138
142
139
143
rolePath := field .NewPath ("roles" )
@@ -147,16 +151,32 @@ func ValidatePolicy(policy *authorizationapi.Policy, isNamespaced bool) field.Er
147
151
allErrs = append (allErrs , field .Invalid (keyPath .Child ("metadata" , "name" ), role .Name , "must be " + roleKey ))
148
152
}
149
153
150
- allErrs = append (allErrs , validateRole (role , isNamespaced , keyPath )... )
154
+ if ! skipRoleValidation {
155
+ allErrs = append (allErrs , validateRole (role , isNamespaced , keyPath )... ) // policy creation validation is more strict
156
+ }
151
157
}
152
158
153
159
return allErrs
154
160
}
155
161
156
162
func ValidatePolicyUpdate (policy * authorizationapi.Policy , oldPolicy * authorizationapi.Policy , isNamespaced bool ) field.ErrorList {
157
- allErrs := ValidatePolicy (policy , isNamespaced )
163
+ // We skip role validation here because we handle it below
164
+ // It needs to based on if the role is an existing one vs. a new one
165
+ allErrs := validatePolicy (policy , isNamespaced , true )
158
166
allErrs = append (allErrs , validation .ValidateObjectMetaUpdate (& policy .ObjectMeta , & oldPolicy .ObjectMeta , field .NewPath ("metadata" ))... )
159
-
167
+ rolePath := field .NewPath ("roles" )
168
+ for roleKey , role := range policy .Roles {
169
+ if role == nil {
170
+ continue // these cause errors in validatePolicy so we do not worry about them
171
+ }
172
+ keyPath := rolePath .Key (roleKey )
173
+ oldRole , isExistingRole := oldPolicy .Roles [roleKey ]
174
+ if isExistingRole {
175
+ allErrs = append (allErrs , validateRoleUpdate (role , oldRole , isNamespaced , keyPath )... )
176
+ } else {
177
+ allErrs = append (allErrs , validateRole (role , isNamespaced , keyPath )... ) // new roles have stricter validation
178
+ }
179
+ }
160
180
return allErrs
161
181
}
162
182
@@ -236,15 +256,15 @@ func ValidateLocalRole(policy *authorizationapi.Role) field.ErrorList {
236
256
}
237
257
238
258
func ValidateLocalRoleUpdate (policy * authorizationapi.Role , oldRole * authorizationapi.Role ) field.ErrorList {
239
- return ValidateRoleUpdate (policy , oldRole , true )
259
+ return ValidateRoleUpdate (policy , oldRole , true , nil )
240
260
}
241
261
242
262
func ValidateClusterRole (policy * authorizationapi.ClusterRole ) field.ErrorList {
243
263
return ValidateRole (authorizationapi .ToRole (policy ), false )
244
264
}
245
265
246
266
func ValidateClusterRoleUpdate (policy * authorizationapi.ClusterRole , oldRole * authorizationapi.ClusterRole ) field.ErrorList {
247
- return ValidateRoleUpdate (authorizationapi .ToRole (policy ), authorizationapi .ToRole (oldRole ), false )
267
+ return ValidateRoleUpdate (authorizationapi .ToRole (policy ), authorizationapi .ToRole (oldRole ), false , nil )
248
268
}
249
269
250
270
func ValidateRole (role * authorizationapi.Role , isNamespaced bool ) field.ErrorList {
@@ -253,21 +273,44 @@ func ValidateRole(role *authorizationapi.Role, isNamespaced bool) field.ErrorLis
253
273
254
274
func validateRole (role * authorizationapi.Role , isNamespaced bool , fldPath * field.Path ) field.ErrorList {
255
275
allErrs := validation .ValidateObjectMeta (& role .ObjectMeta , isNamespaced , path .ValidatePathSegmentName , fldPath .Child ("metadata" ))
276
+ rulesPath := fldPath .Child ("rules" )
256
277
for i , rule := range role .Rules {
257
278
if rule .AttributeRestrictions != nil {
258
- allErrs = append (allErrs , field .Invalid (fldPath . Child ( "rules" ) .Index (i ).Child ("attributeRestrictions" ), rule .AttributeRestrictions , "must be null" ))
279
+ allErrs = append (allErrs , field .Invalid (rulesPath .Index (i ).Child ("attributeRestrictions" ), rule .AttributeRestrictions , "must be null" ))
259
280
}
260
281
}
261
282
return allErrs
262
283
}
263
284
264
- func ValidateRoleUpdate (role * authorizationapi.Role , oldRole * authorizationapi.Role , isNamespaced bool ) field.ErrorList {
265
- allErrs := ValidateRole (role , isNamespaced )
266
- allErrs = append (allErrs , validation .ValidateObjectMetaUpdate (& role .ObjectMeta , & oldRole .ObjectMeta , field .NewPath ("metadata" ))... )
285
+ func ValidateRoleUpdate (role * authorizationapi.Role , oldRole * authorizationapi.Role , isNamespaced bool , fldPath * field.Path ) field.ErrorList {
286
+ allErrs := validateRoleUpdate (role , oldRole , isNamespaced , fldPath )
287
+ // We can use ValidateObjectMetaUpdate here because we know that we are validating a single role, and not a role embedded inside a policy object
288
+ allErrs = append (allErrs , validation .ValidateObjectMetaUpdate (& role .ObjectMeta , & oldRole .ObjectMeta , fldPath .Child ("metadata" ))... )
289
+ return allErrs
290
+ }
267
291
292
+ func validateRoleUpdate (role * authorizationapi.Role , oldRole * authorizationapi.Role , isNamespaced bool , fldPath * field.Path ) field.ErrorList {
293
+ // We use ValidateObjectMeta here because roles embedded inside of policy objects are not guaranteed to
294
+ // have a resource version and thus will fail the policy's validation if ValidateObjectMetaUpdate was used
295
+ allErrs := validation .ValidateObjectMeta (& role .ObjectMeta , isNamespaced , path .ValidatePathSegmentName , fldPath .Child ("metadata" ))
296
+ rulesPath := fldPath .Child ("rules" )
297
+ for i , rule := range role .Rules {
298
+ if rule .AttributeRestrictions != nil && isNewRule (rule , oldRole ) {
299
+ allErrs = append (allErrs , field .Invalid (rulesPath .Index (i ).Child ("attributeRestrictions" ), rule .AttributeRestrictions , "must be null" ))
300
+ }
301
+ }
268
302
return allErrs
269
303
}
270
304
305
+ func isNewRule (rule authorizationapi.PolicyRule , oldRole * authorizationapi.Role ) bool {
306
+ for _ , r := range oldRole .Rules {
307
+ if r .AttributeRestrictions != nil && kapi .Semantic .DeepEqual (rule , r ) { // only do expensive comparision against rules that have attribute restrictions
308
+ return false
309
+ }
310
+ }
311
+ return true
312
+ }
313
+
271
314
func ValidateLocalRoleBinding (policy * authorizationapi.RoleBinding ) field.ErrorList {
272
315
return ValidateRoleBinding (policy , true )
273
316
}
0 commit comments