Repository: kubernetes-sigs/controller-tools (go)

Issue:
RBAC generation with specific resourceNames
The regular RBAC generation annotation works great if you want to give permissions over all instances of a certain type. Say you want to give permission to get all statefulsets, that's as easy as

`// +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get`

But what if you want to give permissions only over a few instances? For example, when dealing with PodSecurityPolicies, you'll probably want to generate an RBAC rule that looks like

```
- apiGroups:
  - policy
  resources:
  - podsecuritypolicies
  verbs:
  - use
  resourceNames:
  - psp.1
```

But there's no way to do that with the RBAC generation is there?
`// +kubebuilder:rbac:groups="policy",resources=podsecuritypolicies,verbs=use,resourceNames=psp.1`

ends up generating 

```
- apiGroups:
  - policy
  resources:
  - podsecuritypolicies
  verbs:
  - use
```

So how can you generate RBAC rules for specific resource instances?

Pull request:
✨ Add new resourceNames field in RBAC marker
This makes it possible to generate policy rules restricted to specific
instances.

Fixes #401 

Patch:
diff --git a/pkg/rbac/parser.go b/pkg/rbac/parser.go
index 88ae805d..8adfb366 100644
--- a/pkg/rbac/parser.go
+++ b/pkg/rbac/parser.go
@@ -19,7 +19,7 @@ limitations under the License.
 //
 // The markers take the form:
 //
-//  +kubebuilder:rbac:groups=<groups>,resources=<resources>,verbs=<verbs>,urls=<non resource urls>
+//  +kubebuilder:rbac:groups=<groups>,resources=<resources>,resourceNames=<resource names>,verbs=<verbs>,urls=<non resource urls>
 package rbac
 
 import (
@@ -48,6 +48,11 @@ type Rule struct {
 	Groups []string `marker:",optional"`
 	// Resources specifies the API resources that this rule encompasses.
 	Resources []string `marker:",optional"`
+	// ResourceNames specifies the names of the API resources that this rule encompasses.
+	//
+	// Create requests cannot be restricted by resourcename, as the object's name
+	// is not known at authorization time.
+	ResourceNames []string `marker:",optional"`
 	// Verbs specifies the (lowercase) kubernetes API verbs that this rule encompasses.
 	Verbs []string
 	// URL specifies the non-resource URLs that this rule encompasses.
@@ -60,13 +65,14 @@ type Rule struct {
 
 // ruleKey represents the resources and non-resources a Rule applies.
 type ruleKey struct {
-	Groups    string
-	Resources string
-	URLs      string
+	Groups        string
+	Resources     string
+	ResourceNames string
+	URLs          string
 }
 
 func (key ruleKey) String() string {
-	return fmt.Sprintf("%s + %s + %s", key.Groups, key.Resources, key.URLs)
+	return fmt.Sprintf("%s + %s + %s + %s", key.Groups, key.Resources, key.ResourceNames, key.URLs)
 }
 
 // ruleKeys implements sort.Interface
@@ -80,9 +86,10 @@ func (keys ruleKeys) Less(i, j int) bool { return keys[i].String() < keys[j].Str
 func (r *Rule) key() ruleKey {
 	r.normalize()
 	return ruleKey{
-		Groups:    strings.Join(r.Groups, "&"),
-		Resources: strings.Join(r.Resources, "&"),
-		URLs:      strings.Join(r.URLs, "&"),
+		Groups:        strings.Join(r.Groups, "&"),
+		Resources:     strings.Join(r.Resources, "&"),
+		ResourceNames: strings.Join(r.ResourceNames, "&"),
+		URLs:          strings.Join(r.URLs, "&"),
 	}
 }
 
@@ -96,6 +103,7 @@ func (r *Rule) addVerbs(verbs []string) {
 func (r *Rule) normalize() {
 	r.Groups = removeDupAndSort(r.Groups)
 	r.Resources = removeDupAndSort(r.Resources)
+	r.ResourceNames = removeDupAndSort(r.ResourceNames)
 	r.Verbs = removeDupAndSort(r.Verbs)
 	r.URLs = removeDupAndSort(r.URLs)
 }
@@ -130,6 +138,7 @@ func (r *Rule) ToRule() rbacv1.PolicyRule {
 		APIGroups:       r.Groups,
 		Verbs:           r.Verbs,
 		Resources:       r.Resources,
+		ResourceNames:   r.ResourceNames,
 		NonResourceURLs: r.URLs,
 	}
 }
diff --git a/pkg/rbac/zz_generated.markerhelp.go b/pkg/rbac/zz_generated.markerhelp.go
index f58a9ed9..8418dcb1 100644
--- a/pkg/rbac/zz_generated.markerhelp.go
+++ b/pkg/rbac/zz_generated.markerhelp.go
@@ -56,6 +56,10 @@ func (Rule) Help() *markers.DefinitionHelp {
 				Summary: "specifies the API resources that this rule encompasses.",
 				Details: "",
 			},
+			"ResourceNames": markers.DetailedHelp{
+				Summary: "specifies the names of the API resources that this rule encompasses. Create requests cannot be restricted by resourcename, as the object's name is not known at authorization time.",
+				Details: "",
+			},
 			"Verbs": markers.DetailedHelp{
 				Summary: "specifies the (lowercase) kubernetes API verbs that this rule encompasses.",
 				Details: "",


Test patch:
diff --git a/pkg/rbac/testdata/controller.go b/pkg/rbac/testdata/controller.go
index 6c2964dd..27800d72 100644
--- a/pkg/rbac/testdata/controller.go
+++ b/pkg/rbac/testdata/controller.go
@@ -10,3 +10,4 @@ package controller
 // +kubebuilder:rbac:groups=cron;batch,resources=jobs/status,verbs=get;create
 // +kubebuilder:rbac:groups=batch,resources=jobs/status,verbs=watch;watch
 // +kubebuilder:rbac:groups=art,resources=jobs,verbs=get,namespace=park
+// +kubebuilder:rbac:groups=batch.io,resources=cronjobs,resourceNames=foo;bar;baz,verbs=get;watch
diff --git a/pkg/rbac/testdata/role.yaml b/pkg/rbac/testdata/role.yaml
index d7afab43..892f09d6 100644
--- a/pkg/rbac/testdata/role.yaml
+++ b/pkg/rbac/testdata/role.yaml
@@ -34,6 +34,17 @@ rules:
   - create
   - get
   - watch
+- apiGroups:
+  - batch.io
+  resourceNames:
+  - bar
+  - baz
+  - foo
+  resources:
+  - cronjobs
+  verbs:
+  - get
+  - watch
 - apiGroups:
   - batch.io
   resources: