1/* 2Copyright 2016 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package validation 18 19import ( 20 "testing" 21 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "k8s.io/apimachinery/pkg/util/validation/field" 24 "k8s.io/kubernetes/pkg/apis/rbac" 25) 26 27func TestValidateClusterRoleBinding(t *testing.T) { 28 errs := ValidateClusterRoleBinding( 29 &rbac.ClusterRoleBinding{ 30 ObjectMeta: metav1.ObjectMeta{Name: "master"}, 31 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, 32 Subjects: []rbac.Subject{ 33 {Name: "validsaname", APIGroup: "", Namespace: "foo", Kind: rbac.ServiceAccountKind}, 34 {Name: "valid@username", APIGroup: rbac.GroupName, Kind: rbac.UserKind}, 35 {Name: "valid@groupname", APIGroup: rbac.GroupName, Kind: rbac.GroupKind}, 36 }, 37 }, 38 ) 39 if len(errs) != 0 { 40 t.Errorf("expected success: %v", errs) 41 } 42 43 errorCases := map[string]struct { 44 A rbac.ClusterRoleBinding 45 T field.ErrorType 46 F string 47 }{ 48 "bad group": { 49 A: rbac.ClusterRoleBinding{ 50 ObjectMeta: metav1.ObjectMeta{Name: "default"}, 51 RoleRef: rbac.RoleRef{APIGroup: "rbac.GroupName", Kind: "ClusterRole", Name: "valid"}, 52 }, 53 T: field.ErrorTypeNotSupported, 54 F: "roleRef.apiGroup", 55 }, 56 "bad kind": { 57 A: rbac.ClusterRoleBinding{ 58 ObjectMeta: metav1.ObjectMeta{Name: "default"}, 59 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Type", Name: "valid"}, 60 }, 61 T: field.ErrorTypeNotSupported, 62 F: "roleRef.kind", 63 }, 64 "reference role": { 65 A: rbac.ClusterRoleBinding{ 66 ObjectMeta: metav1.ObjectMeta{Name: "default"}, 67 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 68 }, 69 T: field.ErrorTypeNotSupported, 70 F: "roleRef.kind", 71 }, 72 "zero-length name": { 73 A: rbac.ClusterRoleBinding{ 74 ObjectMeta: metav1.ObjectMeta{}, 75 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, 76 }, 77 T: field.ErrorTypeRequired, 78 F: "metadata.name", 79 }, 80 "bad role": { 81 A: rbac.ClusterRoleBinding{ 82 ObjectMeta: metav1.ObjectMeta{Name: "default"}, 83 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole"}, 84 }, 85 T: field.ErrorTypeRequired, 86 F: "roleRef.name", 87 }, 88 "bad subject kind": { 89 A: rbac.ClusterRoleBinding{ 90 ObjectMeta: metav1.ObjectMeta{Name: "master"}, 91 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, 92 Subjects: []rbac.Subject{{Name: "subject"}}, 93 }, 94 T: field.ErrorTypeNotSupported, 95 F: "subjects[0].kind", 96 }, 97 "bad subject name": { 98 A: rbac.ClusterRoleBinding{ 99 ObjectMeta: metav1.ObjectMeta{Name: "master"}, 100 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, 101 Subjects: []rbac.Subject{{Namespace: "foo", Name: "subject:bad", Kind: rbac.ServiceAccountKind}}, 102 }, 103 T: field.ErrorTypeInvalid, 104 F: "subjects[0].name", 105 }, 106 "missing SA namespace": { 107 A: rbac.ClusterRoleBinding{ 108 ObjectMeta: metav1.ObjectMeta{Name: "master"}, 109 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, 110 Subjects: []rbac.Subject{{Name: "good", Kind: rbac.ServiceAccountKind}}, 111 }, 112 T: field.ErrorTypeRequired, 113 F: "subjects[0].namespace", 114 }, 115 "missing subject name": { 116 A: rbac.ClusterRoleBinding{ 117 ObjectMeta: metav1.ObjectMeta{Name: "master"}, 118 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, 119 Subjects: []rbac.Subject{{Namespace: "foo", Kind: rbac.ServiceAccountKind}}, 120 }, 121 T: field.ErrorTypeRequired, 122 F: "subjects[0].name", 123 }, 124 } 125 for k, v := range errorCases { 126 errs := ValidateClusterRoleBinding(&v.A) 127 if len(errs) == 0 { 128 t.Errorf("expected failure %s for %v", k, v.A) 129 continue 130 } 131 for i := range errs { 132 if errs[i].Type != v.T { 133 t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i]) 134 } 135 if errs[i].Field != v.F { 136 t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i]) 137 } 138 } 139 } 140} 141 142func TestValidateRoleBinding(t *testing.T) { 143 errs := ValidateRoleBinding( 144 &rbac.RoleBinding{ 145 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"}, 146 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 147 Subjects: []rbac.Subject{ 148 {Name: "validsaname", APIGroup: "", Kind: rbac.ServiceAccountKind}, 149 {Name: "valid@username", APIGroup: rbac.GroupName, Kind: rbac.UserKind}, 150 {Name: "valid@groupname", APIGroup: rbac.GroupName, Kind: rbac.GroupKind}, 151 }, 152 }, 153 ) 154 if len(errs) != 0 { 155 t.Errorf("expected success: %v", errs) 156 } 157 158 errorCases := map[string]struct { 159 A rbac.RoleBinding 160 T field.ErrorType 161 F string 162 }{ 163 "bad group": { 164 A: rbac.RoleBinding{ 165 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "default"}, 166 RoleRef: rbac.RoleRef{APIGroup: "rbac.GroupName", Kind: "ClusterRole", Name: "valid"}, 167 }, 168 T: field.ErrorTypeNotSupported, 169 F: "roleRef.apiGroup", 170 }, 171 "bad kind": { 172 A: rbac.RoleBinding{ 173 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "default"}, 174 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Type", Name: "valid"}, 175 }, 176 T: field.ErrorTypeNotSupported, 177 F: "roleRef.kind", 178 }, 179 "zero-length namespace": { 180 A: rbac.RoleBinding{ 181 ObjectMeta: metav1.ObjectMeta{Name: "default"}, 182 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 183 }, 184 T: field.ErrorTypeRequired, 185 F: "metadata.namespace", 186 }, 187 "zero-length name": { 188 A: rbac.RoleBinding{ 189 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault}, 190 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 191 }, 192 T: field.ErrorTypeRequired, 193 F: "metadata.name", 194 }, 195 "bad role": { 196 A: rbac.RoleBinding{ 197 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "default"}, 198 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role"}, 199 }, 200 T: field.ErrorTypeRequired, 201 F: "roleRef.name", 202 }, 203 "bad subject kind": { 204 A: rbac.RoleBinding{ 205 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"}, 206 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 207 Subjects: []rbac.Subject{{Name: "subject"}}, 208 }, 209 T: field.ErrorTypeNotSupported, 210 F: "subjects[0].kind", 211 }, 212 "bad subject name": { 213 A: rbac.RoleBinding{ 214 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"}, 215 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 216 Subjects: []rbac.Subject{{Name: "subject:bad", Kind: rbac.ServiceAccountKind}}, 217 }, 218 T: field.ErrorTypeInvalid, 219 F: "subjects[0].name", 220 }, 221 "missing subject name": { 222 A: rbac.RoleBinding{ 223 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"}, 224 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 225 Subjects: []rbac.Subject{{Kind: rbac.ServiceAccountKind}}, 226 }, 227 T: field.ErrorTypeRequired, 228 F: "subjects[0].name", 229 }, 230 } 231 for k, v := range errorCases { 232 errs := ValidateRoleBinding(&v.A) 233 if len(errs) == 0 { 234 t.Errorf("expected failure %s for %v", k, v.A) 235 continue 236 } 237 for i := range errs { 238 if errs[i].Type != v.T { 239 t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i]) 240 } 241 if errs[i].Field != v.F { 242 t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i]) 243 } 244 } 245 } 246} 247 248func TestValidateRoleBindingUpdate(t *testing.T) { 249 old := &rbac.RoleBinding{ 250 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master", ResourceVersion: "1"}, 251 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 252 } 253 254 errs := ValidateRoleBindingUpdate( 255 &rbac.RoleBinding{ 256 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master", ResourceVersion: "1"}, 257 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, 258 }, 259 old, 260 ) 261 if len(errs) != 0 { 262 t.Errorf("expected success: %v", errs) 263 } 264 265 errorCases := map[string]struct { 266 A rbac.RoleBinding 267 T field.ErrorType 268 F string 269 }{ 270 "changedRef": { 271 A: rbac.RoleBinding{ 272 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master", ResourceVersion: "1"}, 273 RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "changed"}, 274 }, 275 T: field.ErrorTypeInvalid, 276 F: "roleRef", 277 }, 278 } 279 for k, v := range errorCases { 280 errs := ValidateRoleBindingUpdate(&v.A, old) 281 if len(errs) == 0 { 282 t.Errorf("expected failure %s for %v", k, v.A) 283 continue 284 } 285 for i := range errs { 286 if errs[i].Type != v.T { 287 t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i]) 288 } 289 if errs[i].Field != v.F { 290 t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i]) 291 } 292 } 293 } 294} 295 296type ValidateRoleTest struct { 297 role rbac.Role 298 wantErr bool 299 errType field.ErrorType 300 field string 301} 302 303func (v ValidateRoleTest) test(t *testing.T) { 304 errs := ValidateRole(&v.role) 305 if len(errs) == 0 { 306 if v.wantErr { 307 t.Fatal("expected validation error") 308 } 309 return 310 } 311 if !v.wantErr { 312 t.Errorf("didn't expect error, got %v", errs) 313 return 314 } 315 for i := range errs { 316 if errs[i].Type != v.errType { 317 t.Errorf("expected errors to have type %s: %v", v.errType, errs[i]) 318 } 319 if errs[i].Field != v.field { 320 t.Errorf("expected errors to have field %s: %v", v.field, errs[i]) 321 } 322 } 323} 324 325type ValidateClusterRoleTest struct { 326 role rbac.ClusterRole 327 wantErr bool 328 errType field.ErrorType 329 field string 330} 331 332func (v ValidateClusterRoleTest) test(t *testing.T) { 333 errs := ValidateClusterRole(&v.role) 334 if len(errs) == 0 { 335 if v.wantErr { 336 t.Fatal("expected validation error") 337 } 338 return 339 } 340 if !v.wantErr { 341 t.Errorf("didn't expect error, got %v", errs) 342 return 343 } 344 for i := range errs { 345 if errs[i].Type != v.errType { 346 t.Errorf("expected errors to have type %s: %v", v.errType, errs[i]) 347 } 348 if errs[i].Field != v.field { 349 t.Errorf("expected errors to have field %s: %v", v.field, errs[i]) 350 } 351 } 352} 353 354func TestValidateRoleZeroLengthNamespace(t *testing.T) { 355 ValidateRoleTest{ 356 role: rbac.Role{ 357 ObjectMeta: metav1.ObjectMeta{Name: "default"}, 358 }, 359 wantErr: true, 360 errType: field.ErrorTypeRequired, 361 field: "metadata.namespace", 362 }.test(t) 363} 364 365func TestValidateRoleZeroLengthName(t *testing.T) { 366 ValidateRoleTest{ 367 role: rbac.Role{ 368 ObjectMeta: metav1.ObjectMeta{Namespace: "default"}, 369 }, 370 wantErr: true, 371 errType: field.ErrorTypeRequired, 372 field: "metadata.name", 373 }.test(t) 374} 375 376func TestValidateRoleValidRole(t *testing.T) { 377 ValidateRoleTest{ 378 role: rbac.Role{ 379 ObjectMeta: metav1.ObjectMeta{ 380 Namespace: "default", 381 Name: "default", 382 }, 383 }, 384 wantErr: false, 385 }.test(t) 386} 387 388func TestValidateRoleValidRoleNoNamespace(t *testing.T) { 389 ValidateClusterRoleTest{ 390 role: rbac.ClusterRole{ 391 ObjectMeta: metav1.ObjectMeta{ 392 Name: "default", 393 }, 394 }, 395 wantErr: false, 396 }.test(t) 397} 398 399func TestValidateRoleNonResourceURL(t *testing.T) { 400 ValidateClusterRoleTest{ 401 role: rbac.ClusterRole{ 402 ObjectMeta: metav1.ObjectMeta{ 403 Name: "default", 404 }, 405 Rules: []rbac.PolicyRule{ 406 { 407 Verbs: []string{"get"}, 408 NonResourceURLs: []string{"/*"}, 409 }, 410 }, 411 }, 412 wantErr: false, 413 }.test(t) 414} 415 416func TestValidateRoleNamespacedNonResourceURL(t *testing.T) { 417 ValidateRoleTest{ 418 role: rbac.Role{ 419 ObjectMeta: metav1.ObjectMeta{ 420 Namespace: "default", 421 Name: "default", 422 }, 423 Rules: []rbac.PolicyRule{ 424 { 425 // non-resource URLs are invalid for namespaced rules 426 Verbs: []string{"get"}, 427 NonResourceURLs: []string{"/*"}, 428 }, 429 }, 430 }, 431 wantErr: true, 432 errType: field.ErrorTypeInvalid, 433 field: "rules[0].nonResourceURLs", 434 }.test(t) 435} 436 437func TestValidateRoleNonResourceURLNoVerbs(t *testing.T) { 438 ValidateClusterRoleTest{ 439 role: rbac.ClusterRole{ 440 ObjectMeta: metav1.ObjectMeta{ 441 Name: "default", 442 }, 443 Rules: []rbac.PolicyRule{ 444 { 445 Verbs: []string{}, 446 NonResourceURLs: []string{"/*"}, 447 }, 448 }, 449 }, 450 wantErr: true, 451 errType: field.ErrorTypeRequired, 452 field: "rules[0].verbs", 453 }.test(t) 454} 455 456func TestValidateRoleMixedNonResourceAndResource(t *testing.T) { 457 ValidateRoleTest{ 458 role: rbac.Role{ 459 ObjectMeta: metav1.ObjectMeta{ 460 Name: "default", 461 Namespace: "default", 462 }, 463 Rules: []rbac.PolicyRule{ 464 { 465 Verbs: []string{"get"}, 466 NonResourceURLs: []string{"/*"}, 467 APIGroups: []string{"v1"}, 468 Resources: []string{"pods"}, 469 }, 470 }, 471 }, 472 wantErr: true, 473 errType: field.ErrorTypeInvalid, 474 field: "rules[0].nonResourceURLs", 475 }.test(t) 476} 477 478func TestValidateRoleValidResource(t *testing.T) { 479 ValidateRoleTest{ 480 role: rbac.Role{ 481 ObjectMeta: metav1.ObjectMeta{ 482 Name: "default", 483 Namespace: "default", 484 }, 485 Rules: []rbac.PolicyRule{ 486 { 487 Verbs: []string{"get"}, 488 APIGroups: []string{"v1"}, 489 Resources: []string{"pods"}, 490 }, 491 }, 492 }, 493 wantErr: false, 494 }.test(t) 495} 496 497func TestValidateRoleNoAPIGroup(t *testing.T) { 498 ValidateRoleTest{ 499 role: rbac.Role{ 500 ObjectMeta: metav1.ObjectMeta{ 501 Name: "default", 502 Namespace: "default", 503 }, 504 Rules: []rbac.PolicyRule{ 505 { 506 Verbs: []string{"get"}, 507 Resources: []string{"pods"}, 508 }, 509 }, 510 }, 511 wantErr: true, 512 errType: field.ErrorTypeRequired, 513 field: "rules[0].apiGroups", 514 }.test(t) 515} 516 517func TestValidateRoleNoResources(t *testing.T) { 518 ValidateRoleTest{ 519 role: rbac.Role{ 520 ObjectMeta: metav1.ObjectMeta{ 521 Name: "default", 522 Namespace: "default", 523 }, 524 Rules: []rbac.PolicyRule{ 525 { 526 Verbs: []string{"get"}, 527 APIGroups: []string{"v1"}, 528 }, 529 }, 530 }, 531 wantErr: true, 532 errType: field.ErrorTypeRequired, 533 field: "rules[0].resources", 534 }.test(t) 535} 536