@@ -396,7 +396,7 @@ func setupFakeClient(role client.Object) client.Client {
396396func TestPreAuthorize_Success (t * testing.T ) {
397397 t .Run ("preauthorize succeeds with no missing rbac rules" , func (t * testing.T ) {
398398 fakeClient := setupFakeClient (privilegedClusterRole )
399- preAuth := NewRBACPreAuthorizer (fakeClient )
399+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ( "list" , "watch" ), WithNamespacedCollectionVerbs ( "create" ) )
400400 missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
401401 require .NoError (t , err )
402402 require .Equal (t , []ScopedPolicyRules {}, missingRules )
@@ -406,7 +406,7 @@ func TestPreAuthorize_Success(t *testing.T) {
406406func TestPreAuthorize_MissingRBAC (t * testing.T ) {
407407 t .Run ("preauthorize fails and finds missing rbac rules" , func (t * testing.T ) {
408408 fakeClient := setupFakeClient (limitedClusterRole )
409- preAuth := NewRBACPreAuthorizer (fakeClient )
409+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ( "list" , "watch" ), WithNamespacedCollectionVerbs ( "create" ) )
410410 missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
411411 require .NoError (t , err )
412412 require .Equal (t , expectedSingleNamespaceMissingRules , missingRules )
@@ -416,7 +416,7 @@ func TestPreAuthorize_MissingRBAC(t *testing.T) {
416416func TestPreAuthorizeMultiNamespace_MissingRBAC (t * testing.T ) {
417417 t .Run ("preauthorize fails and finds missing rbac rules in multiple namespaces" , func (t * testing.T ) {
418418 fakeClient := setupFakeClient (limitedClusterRole )
419- preAuth := NewRBACPreAuthorizer (fakeClient )
419+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ( "list" , "watch" ), WithNamespacedCollectionVerbs ( "create" ) )
420420 missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifestMultiNamespace ))
421421 require .NoError (t , err )
422422 require .Equal (t , expectedMultiNamespaceMissingRules , missingRules )
@@ -426,7 +426,7 @@ func TestPreAuthorizeMultiNamespace_MissingRBAC(t *testing.T) {
426426func TestPreAuthorize_CheckEscalation (t * testing.T ) {
427427 t .Run ("preauthorize succeeds with no missing rbac rules" , func (t * testing.T ) {
428428 fakeClient := setupFakeClient (escalatingClusterRole )
429- preAuth := NewRBACPreAuthorizer (fakeClient )
429+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ( "list" , "watch" ), WithNamespacedCollectionVerbs ( "create" ) )
430430 missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
431431 require .NoError (t , err )
432432 require .Equal (t , []ScopedPolicyRules {}, missingRules )
@@ -436,7 +436,7 @@ func TestPreAuthorize_CheckEscalation(t *testing.T) {
436436func TestPreAuthorize_AdditionalRequiredPerms_MissingRBAC (t * testing.T ) {
437437 t .Run ("preauthorize fails and finds missing rbac rules coming from the additional required permissions" , func (t * testing.T ) {
438438 fakeClient := setupFakeClient (escalatingClusterRole )
439- preAuth := NewRBACPreAuthorizer (fakeClient )
439+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ( "list" , "watch" ), WithNamespacedCollectionVerbs ( "create" ) )
440440 missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ), func (user user.Info ) []authorizer.AttributesRecord {
441441 return []authorizer.AttributesRecord {
442442 {
@@ -465,6 +465,237 @@ func TestPreAuthorize_AdditionalRequiredPerms_MissingRBAC(t *testing.T) {
465465 })
466466}
467467
468+ func TestPreAuthorize_WithClusterCollectionVerbs (t * testing.T ) {
469+ // expectedNamespacedMissingRules are the missing rules expected in the "test-namespace"
470+ // namespace regardless of cluster collection verb configuration. These come from object
471+ // verbs (get, patch, update, delete), namespaced collection verbs (create), and the
472+ // escalation check for the role/rolebinding in the manifest.
473+ expectedNamespacedMissingRules := ScopedPolicyRules {
474+ Namespace : "test-namespace" ,
475+ MissingRules : []rbacv1.PolicyRule {
476+ {
477+ Verbs : []string {"create" },
478+ APIGroups : []string {"*" },
479+ Resources : []string {"certificates" }},
480+ {
481+ Verbs : []string {"create" },
482+ APIGroups : []string {"" },
483+ Resources : []string {"services" }},
484+ {
485+ Verbs : []string {"create" },
486+ APIGroups : []string {"rbac.authorization.k8s.io" },
487+ Resources : []string {"rolebindings" }},
488+ {
489+ Verbs : []string {"create" },
490+ APIGroups : []string {"rbac.authorization.k8s.io" },
491+ Resources : []string {"roles" }},
492+ {
493+ Verbs : []string {"delete" , "get" , "patch" , "update" },
494+ APIGroups : []string {"" },
495+ Resources : []string {"services" },
496+ ResourceNames : []string {"test-service" }},
497+ {
498+ Verbs : []string {"delete" , "get" , "patch" , "update" },
499+ APIGroups : []string {"rbac.authorization.k8s.io" },
500+ Resources : []string {"rolebindings" },
501+ ResourceNames : []string {"test-extension-binding" }},
502+ {
503+ Verbs : []string {"delete" , "get" , "patch" , "update" },
504+ APIGroups : []string {"rbac.authorization.k8s.io" },
505+ Resources : []string {"roles" },
506+ ResourceNames : []string {"test-extension-role" }},
507+ {
508+ Verbs : []string {"watch" },
509+ APIGroups : []string {"*" },
510+ Resources : []string {"serviceaccounts" },
511+ },
512+ },
513+ }
514+
515+ t .Run ("no cluster collection verbs option omits cluster-scoped collection rules" , func (t * testing.T ) {
516+ fakeClient := setupFakeClient (limitedClusterRole )
517+ preAuth := NewRBACPreAuthorizer (fakeClient , WithNamespacedCollectionVerbs ("create" ))
518+ missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
519+ require .NoError (t , err )
520+ // With no cluster collection verbs, there should be no cluster-scoped (namespace="") missing rules
521+ require .Equal (t , []ScopedPolicyRules {expectedNamespacedMissingRules }, missingRules )
522+ })
523+
524+ t .Run ("cluster verbs option only checks those verbs at cluster scope" , func (t * testing.T ) {
525+ fakeClient := setupFakeClient (limitedClusterRole )
526+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ("get" , "patch" , "update" ), WithNamespacedCollectionVerbs ("create" ))
527+ missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
528+ require .NoError (t , err )
529+ require .Equal (t , []ScopedPolicyRules {
530+ {
531+ Namespace : "" ,
532+ MissingRules : []rbacv1.PolicyRule {
533+ {
534+ Verbs : []string {"get" , "patch" , "update" },
535+ APIGroups : []string {"" },
536+ Resources : []string {"services" },
537+ ResourceNames : []string (nil ),
538+ NonResourceURLs : []string (nil )},
539+ {
540+ Verbs : []string {"get" , "patch" , "update" },
541+ APIGroups : []string {"rbac.authorization.k8s.io" },
542+ Resources : []string {"rolebindings" },
543+ ResourceNames : []string (nil ),
544+ NonResourceURLs : []string (nil )},
545+ {
546+ Verbs : []string {"get" , "patch" , "update" },
547+ APIGroups : []string {"rbac.authorization.k8s.io" },
548+ Resources : []string {"roles" },
549+ ResourceNames : []string (nil ),
550+ NonResourceURLs : []string (nil ),
551+ },
552+ },
553+ },
554+ expectedNamespacedMissingRules ,
555+ }, missingRules )
556+ })
557+
558+ t .Run ("privileged user with no cluster collection verbs succeeds" , func (t * testing.T ) {
559+ fakeClient := setupFakeClient (privilegedClusterRole )
560+ preAuth := NewRBACPreAuthorizer (fakeClient , WithNamespacedCollectionVerbs ("create" ))
561+ missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
562+ require .NoError (t , err )
563+ require .Equal (t , []ScopedPolicyRules {}, missingRules )
564+ })
565+ }
566+
567+ func TestPreAuthorize_WithNamespacedCollectionVerbs (t * testing.T ) {
568+ // expectedClusterMissingRules are the missing rules expected at cluster scope
569+ // when cluster collection verbs are configured as "list", "watch".
570+ expectedClusterMissingRules := ScopedPolicyRules {
571+ Namespace : "" ,
572+ MissingRules : []rbacv1.PolicyRule {
573+ {
574+ Verbs : []string {"list" , "watch" },
575+ APIGroups : []string {"" },
576+ Resources : []string {"services" },
577+ ResourceNames : []string (nil ),
578+ NonResourceURLs : []string (nil )},
579+ {
580+ Verbs : []string {"list" , "watch" },
581+ APIGroups : []string {"rbac.authorization.k8s.io" },
582+ Resources : []string {"rolebindings" },
583+ ResourceNames : []string (nil ),
584+ NonResourceURLs : []string (nil )},
585+ {
586+ Verbs : []string {"list" , "watch" },
587+ APIGroups : []string {"rbac.authorization.k8s.io" },
588+ Resources : []string {"roles" },
589+ ResourceNames : []string (nil ),
590+ NonResourceURLs : []string (nil ),
591+ },
592+ },
593+ }
594+
595+ t .Run ("no namespaced collection verbs option omits namespaced collection rules" , func (t * testing.T ) {
596+ fakeClient := setupFakeClient (limitedClusterRole )
597+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ("list" , "watch" ))
598+ missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
599+ require .NoError (t , err )
600+ // Without namespaced collection verbs, no "create" rules from collection verbs should appear,
601+ // but object verbs (get, patch, update, delete) and escalation checks still apply
602+ require .Equal (t , []ScopedPolicyRules {
603+ expectedClusterMissingRules ,
604+ {
605+ Namespace : "test-namespace" ,
606+ MissingRules : []rbacv1.PolicyRule {
607+ {
608+ Verbs : []string {"create" },
609+ APIGroups : []string {"*" },
610+ Resources : []string {"certificates" }},
611+ {
612+ Verbs : []string {"delete" , "get" , "patch" , "update" },
613+ APIGroups : []string {"" },
614+ Resources : []string {"services" },
615+ ResourceNames : []string {"test-service" }},
616+ {
617+ Verbs : []string {"delete" , "get" , "patch" , "update" },
618+ APIGroups : []string {"rbac.authorization.k8s.io" },
619+ Resources : []string {"rolebindings" },
620+ ResourceNames : []string {"test-extension-binding" }},
621+ {
622+ Verbs : []string {"delete" , "get" , "patch" , "update" },
623+ APIGroups : []string {"rbac.authorization.k8s.io" },
624+ Resources : []string {"roles" },
625+ ResourceNames : []string {"test-extension-role" }},
626+ {
627+ Verbs : []string {"watch" },
628+ APIGroups : []string {"*" },
629+ Resources : []string {"serviceaccounts" },
630+ },
631+ },
632+ },
633+ }, missingRules )
634+ })
635+
636+ t .Run ("namespaced collection verbs option checks those verbs per namespace" , func (t * testing.T ) {
637+ fakeClient := setupFakeClient (limitedClusterRole )
638+ preAuth := NewRBACPreAuthorizer (fakeClient , WithClusterCollectionVerbs ("list" , "watch" ), WithNamespacedCollectionVerbs ("create" , "deletecollection" ))
639+ missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
640+ require .NoError (t , err )
641+ // Should have cluster-scoped missing rules plus namespaced rules with both create and deletecollection.
642+ // Note: "certificates" with apiGroup "*" comes from the escalation check on the Role, not
643+ // from namespaced collection verbs, so it only has "create".
644+ require .Equal (t , []ScopedPolicyRules {
645+ expectedClusterMissingRules ,
646+ {
647+ Namespace : "test-namespace" ,
648+ MissingRules : []rbacv1.PolicyRule {
649+ {
650+ Verbs : []string {"create" , "deletecollection" },
651+ APIGroups : []string {"" },
652+ Resources : []string {"services" }},
653+ {
654+ Verbs : []string {"create" , "deletecollection" },
655+ APIGroups : []string {"rbac.authorization.k8s.io" },
656+ Resources : []string {"rolebindings" }},
657+ {
658+ Verbs : []string {"create" , "deletecollection" },
659+ APIGroups : []string {"rbac.authorization.k8s.io" },
660+ Resources : []string {"roles" }},
661+ {
662+ Verbs : []string {"create" },
663+ APIGroups : []string {"*" },
664+ Resources : []string {"certificates" }},
665+ {
666+ Verbs : []string {"delete" , "get" , "patch" , "update" },
667+ APIGroups : []string {"" },
668+ Resources : []string {"services" },
669+ ResourceNames : []string {"test-service" }},
670+ {
671+ Verbs : []string {"delete" , "get" , "patch" , "update" },
672+ APIGroups : []string {"rbac.authorization.k8s.io" },
673+ Resources : []string {"rolebindings" },
674+ ResourceNames : []string {"test-extension-binding" }},
675+ {
676+ Verbs : []string {"delete" , "get" , "patch" , "update" },
677+ APIGroups : []string {"rbac.authorization.k8s.io" },
678+ Resources : []string {"roles" },
679+ ResourceNames : []string {"test-extension-role" }},
680+ {
681+ Verbs : []string {"watch" },
682+ APIGroups : []string {"*" },
683+ Resources : []string {"serviceaccounts" },
684+ },
685+ },
686+ },
687+ }, missingRules )
688+ })
689+
690+ t .Run ("privileged user with custom namespaced collection verbs succeeds" , func (t * testing.T ) {
691+ fakeClient := setupFakeClient (privilegedClusterRole )
692+ preAuth := NewRBACPreAuthorizer (fakeClient , WithNamespacedCollectionVerbs ("create" , "deletecollection" ))
693+ missingRules , err := preAuth .PreAuthorize (context .TODO (), testUser , strings .NewReader (testManifest ))
694+ require .NoError (t , err )
695+ require .Equal (t , []ScopedPolicyRules {}, missingRules )
696+ })
697+ }
698+
468699// TestParseEscalationErrorForMissingRules Are tests with respect to https://github.com/kubernetes/api/blob/e8d4d542f6a9a16a694bfc8e3b8cd1557eecfc9d/rbac/v1/types.go#L49-L74
469700// Goal is: prove the regex works as planned AND that if the error messages ever change we'll learn about it with these tests
470701func TestParseEscalationErrorForMissingRules_ParsingLogic (t * testing.T ) {
0 commit comments