@@ -44,180 +44,116 @@ var (
4444 }
4545)
4646
47- func GenerateInstallerRBAC (objs []client.Object , extensionName string , installNamespace string , watchNamespace string ) []client.Object {
48- generatedObjs := getGeneratedObjs (objs )
49-
50- var (
51- serviceAccountName = extensionName + "-installer"
52- clusterRoleName = extensionName + "-installer-clusterrole"
53- clusterRoleBindingName = extensionName + "-installer-clusterrole-binding"
54- roleName = extensionName + "-installer-role"
55- roleBindingName = extensionName + "-installer-role-binding"
56- )
57-
58- rbacManifests := []client.Object {
59- // installer service account
60- ptr .To (newServiceAccount (
61- installNamespace ,
62- serviceAccountName ,
63- )),
64-
65- // cluster scoped resources
66- ptr .To (newClusterRole (
67- clusterRoleName ,
68- slices .Concat (
69- // finalizer rule
70- []rbacv1.PolicyRule {newClusterExtensionFinalizerPolicyRule (extensionName )},
71- // cluster scoped resource creation and management rules
72- generatePolicyRules (filter .Filter (objs , isClusterScopedResource )),
73- // controller rbac scope
74- collectRBACResourcePolicyRules (filter .Filter (generatedObjs , isClusterRole )),
75- ),
76- )),
77- ptr .To (newClusterRoleBinding (
78- clusterRoleBindingName ,
79- clusterRoleName ,
80- installNamespace ,
81- serviceAccountName ,
82- )),
83-
84- // namespace scoped install namespace resources
85- ptr .To (newRole (
86- installNamespace ,
87- roleName ,
88- slices .Concat (
89- // namespace scoped resource creation and management rules
90- generatePolicyRules (filter .Filter (objs , filter .And (isNamespacedResource , inNamespace (installNamespace )))),
91- // controller rbac scope
92- collectRBACResourcePolicyRules (filter .Filter (generatedObjs , filter .And (isRole , inNamespace (installNamespace )))),
93- ),
94- )),
95- ptr .To (newRoleBinding (
96- installNamespace ,
97- roleBindingName ,
98- roleName ,
99- installNamespace ,
100- serviceAccountName ,
101- )),
102-
103- // namespace scoped watch namespace resources
104- ptr .To (newRole (
105- watchNamespace ,
106- roleName ,
107- slices .Concat (
108- // namespace scoped resource creation and management rules
109- generatePolicyRules (filter .Filter (objs , filter .And (isNamespacedResource , inNamespace (watchNamespace )))),
110- // controller rbac scope
111- collectRBACResourcePolicyRules (filter .Filter (generatedObjs , filter .And (isRole , inNamespace (watchNamespace )))),
112- ),
113- )),
114- ptr .To (newRoleBinding (
115- watchNamespace ,
116- roleBindingName ,
117- roleName ,
118- installNamespace ,
119- serviceAccountName ,
120- )),
121- }
122-
123- // remove any cluster/role(s) without any defined rules and pair cluster/role add manifests
124- return slices .DeleteFunc (rbacManifests , isNoRules )
47+ // GenerateResourceManagerClusterRole generates a ClusterRole with permissions to manage objs resources. The
48+ // permissions also aggregate any permissions from any ClusterRoles in objs allowing the holder to also assign
49+ // the RBAC therein to another service account. Note: currently assumes objs have been created by convert.Convert.
50+ func GenerateResourceManagerClusterRole (objs []client.Object ) * rbacv1.ClusterRole {
51+ return ptr .To (newClusterRole (
52+ "" ,
53+ slices .Concat (
54+ // cluster scoped resource creation and management rules
55+ generatePolicyRules (filter .Filter (objs , isClusterScopedResource )),
56+ // controller rbac scope
57+ collectRBACResourcePolicyRules (filter .Filter (objs , filter .And (isGeneratedResource , isOfKind ("ClusterRole" )))),
58+ ),
59+ ))
12560}
12661
127- func isNoRules (object client.Object ) bool {
128- switch obj := object .(type ) {
129- case * rbacv1.ClusterRole :
130- return len (obj .Rules ) == 0
131- case * rbacv1.Role :
132- return len (obj .Rules ) == 0
62+ // GenerateClusterExtensionFinalizerPolicyRule generates a policy rule that allows the holder to update
63+ // finalizer for a ClusterExtension with clusterExtensionName.
64+ func GenerateClusterExtensionFinalizerPolicyRule (clusterExtensionName string ) rbacv1.PolicyRule {
65+ return rbacv1.PolicyRule {
66+ APIGroups : []string {"olm.operatorframework.io" },
67+ Resources : []string {"clusterextensions/finalizers" },
68+ Verbs : []string {"update" },
69+ ResourceNames : []string {clusterExtensionName },
13370 }
134- return false
13571}
13672
137- func getGeneratedObjs (plainObjs []client.Object ) []client.Object {
138- return filter .Filter (plainObjs , func (obj client.Object ) bool {
139- // this is a hack that abuses an internal implementation detail
140- // we should probably annotate the generated resources coming out of convert.Convert
141- _ , ok := obj .(* unstructured.Unstructured )
142- return ok
143- })
73+ // GenerateResourceManagerRoles generates one or more Roles with permissions to manage objs resources in their
74+ // namespaces. The permissions also include any permissions defined in any Roles in objs within the namespace, allowing
75+ // the holder to also assign the RBAC therein to another service account.
76+ // Note: currently assumes objs have been created by convert.Convert.
77+ func GenerateResourceManagerRoles (objs []client.Object ) []* rbacv1.Role {
78+ return mapToSlice (filter .GroupBy (objs , namespaceName ), generateRole )
14479}
14580
146- var isNamespacedResource filter.Predicate [client.Object ] = func (o client.Object ) bool {
147- return slices .Contains (namespaceScopedResources , o .GetObjectKind ().GroupVersionKind ().Kind )
148- }
149-
150- var isClusterScopedResource filter.Predicate [client.Object ] = func (o client.Object ) bool {
151- return slices .Contains (clusterScopedResources , o .GetObjectKind ().GroupVersionKind ().Kind )
152- }
153-
154- var isClusterRole = isOfKind ("ClusterRole" )
155- var isRole = isOfKind ("Role" )
156-
157- func isOfKind (kind string ) filter.Predicate [client.Object ] {
158- return func (o client.Object ) bool {
159- return o .GetObjectKind ().GroupVersionKind ().Kind == kind
160- }
161- }
162-
163- func inNamespace (namespace string ) filter.Predicate [client.Object ] {
164- return func (o client.Object ) bool {
165- return o .GetNamespace () == namespace
166- }
81+ func generateRole (namespace string , namespaceObjs []client.Object ) * rbacv1.Role {
82+ return ptr .To (newRole (
83+ namespace ,
84+ "" ,
85+ slices .Concat (
86+ // namespace scoped resource creation and management rules
87+ generatePolicyRules (namespaceObjs ),
88+ // controller rbac scope
89+ collectRBACResourcePolicyRules (filter .Filter (namespaceObjs , filter .And (isOfKind ("Role" ), isGeneratedResource ))),
90+ ),
91+ ))
16792}
16893
16994func generatePolicyRules (objs []client.Object ) []rbacv1.PolicyRule {
170- resourceNameMap := groupResourceNamesByGroupKind (objs )
171- policyRules := make ([]rbacv1.PolicyRule , 0 , 2 * len (resourceNameMap ))
172- for groupKind , resourceNames := range resourceNameMap {
173- policyRules = append (policyRules , []rbacv1.PolicyRule {
174- newPolicyRule (groupKind , unnamedResourceVerbs ),
175- newPolicyRule (groupKind , namedResourceVerbs , resourceNames ... ),
176- }... )
177- }
178- return policyRules
95+ return slices .Concat (mapToSlice (filter .GroupBy (objs , groupKind ), func (gk schema.GroupKind , resources []client.Object ) []rbacv1.PolicyRule {
96+ return []rbacv1.PolicyRule {
97+ newPolicyRule (gk , unnamedResourceVerbs ),
98+ newPolicyRule (gk , namedResourceVerbs , filter .Map (resources , toResourceName )... ),
99+ }
100+ })... )
179101}
180102
181103func collectRBACResourcePolicyRules (objs []client.Object ) []rbacv1.PolicyRule {
182- var policyRules []rbacv1.PolicyRule
183- for _ , obj := range objs {
104+ return slices .Concat (filter .Map (objs , func (obj client.Object ) []rbacv1.PolicyRule {
184105 if cr , ok := obj .(* rbacv1.ClusterRole ); ok {
185- policyRules = append ( policyRules , cr .Rules ... )
106+ return cr .Rules
186107 } else if r , ok := obj .(* rbacv1.Role ); ok {
187- policyRules = append ( policyRules , r .Rules ... )
108+ return r .Rules
188109 } else {
189110 panic (fmt .Sprintf ("unexpected type %T" , obj ))
190111 }
191- }
192- return policyRules
112+ })... )
193113}
194114
195- func newClusterExtensionFinalizerPolicyRule ( clusterExtensionName string ) rbacv1.PolicyRule {
115+ func newPolicyRule ( groupKind schema. GroupKind , verbs [] string , resourceNames ... string ) rbacv1.PolicyRule {
196116 return rbacv1.PolicyRule {
197- APIGroups : []string {"olm.operatorframework.io" },
198- Resources : []string {"clusterextensions/finalizers" },
199- Verbs : [] string { "update" } ,
200- ResourceNames : [] string { clusterExtensionName } ,
117+ APIGroups : []string {groupKind . Group },
118+ Resources : []string {fmt . Sprintf ( "%ss" , strings . ToLower ( groupKind . Kind )) },
119+ Verbs : verbs ,
120+ ResourceNames : resourceNames ,
201121 }
202122}
203123
204- func groupResourceNamesByGroupKind (objs []client.Object ) map [schema.GroupKind ][]string {
205- resourceNames := map [schema.GroupKind ][]string {}
206- for _ , obj := range objs {
207- key := obj .GetObjectKind ().GroupVersionKind ().GroupKind ()
208- if _ , ok := resourceNames [key ]; ! ok {
209- resourceNames [key ] = []string {}
210- }
211- resourceNames [key ] = append (resourceNames [key ], obj .GetName ())
124+ func mapToSlice [K comparable , V any , R any ](m map [K ]V , fn func (k K , v V ) R ) []R {
125+ out := make ([]R , 0 , len (m ))
126+ for k , v := range m {
127+ out = append (out , fn (k , v ))
212128 }
213- return resourceNames
129+ return out
214130}
215131
216- func newPolicyRule (groupKind schema.GroupKind , verbs []string , resourceNames ... string ) rbacv1.PolicyRule {
217- return rbacv1.PolicyRule {
218- APIGroups : []string {groupKind .Group },
219- Resources : []string {fmt .Sprintf ("%ss" , strings .ToLower (groupKind .Kind ))},
220- Verbs : verbs ,
221- ResourceNames : resourceNames ,
132+ func isClusterScopedResource (o client.Object ) bool {
133+ return slices .Contains (clusterScopedResources , o .GetObjectKind ().GroupVersionKind ().Kind )
134+ }
135+
136+ func isOfKind (kind string ) filter.Predicate [client.Object ] {
137+ return func (o client.Object ) bool {
138+ return o .GetObjectKind ().GroupVersionKind ().Kind == kind
222139 }
223140}
141+
142+ func isGeneratedResource (o client.Object ) bool {
143+ // TODO: this is a hack that abuses an internal implementation detail
144+ // we should probably annotate the generated resources coming out of convert.Convert
145+ _ , ok := o .(* unstructured.Unstructured )
146+ return ok
147+ }
148+
149+ func groupKind (obj client.Object ) schema.GroupKind {
150+ return obj .GetObjectKind ().GroupVersionKind ().GroupKind ()
151+ }
152+
153+ func namespaceName (obj client.Object ) string {
154+ return obj .GetNamespace ()
155+ }
156+
157+ func toResourceName (o client.Object ) string {
158+ return o .GetName ()
159+ }
0 commit comments