From ff48065d285bd6d493938708a6f4d5ecdda1bb72 Mon Sep 17 00:00:00 2001
From: Joel Takvorian <jtakvori@redhat.com>
Date: Mon, 6 Feb 2023 09:24:09 +0100
Subject: [PATCH] NETOBSERV-870 console plugin token review

Need to create CR/CRB for console plugin, to allow token reviews;
  using same reconcile impl as in FLP
---
 ...observ-operator.clusterserviceversion.yaml |  8 ++-
 config/rbac/role.yaml                         |  6 ++
 .../consoleplugin/consoleplugin_objects.go    | 62 +++++++++++++++----
 .../consoleplugin/consoleplugin_reconciler.go | 22 ++++++-
 4 files changed, 83 insertions(+), 15 deletions(-)

diff --git a/bundle/manifests/netobserv-operator.clusterserviceversion.yaml b/bundle/manifests/netobserv-operator.clusterserviceversion.yaml
index 294df9282..fbad7849d 100644
--- a/bundle/manifests/netobserv-operator.clusterserviceversion.yaml
+++ b/bundle/manifests/netobserv-operator.clusterserviceversion.yaml
@@ -169,7 +169,7 @@ metadata:
     categories: Monitoring
     console.openshift.io/plugins: '["netobserv-plugin"]'
     containerImage: quay.io/netobserv/network-observability-operator:1.0.1
-    createdAt: "2023-01-30T08:10:44Z"
+    createdAt: "2023-02-06T08:00:22Z"
     description: Network flows collector and monitoring solution
     operators.operatorframework.io/builder: operator-sdk-v1.25.3
     operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
@@ -292,6 +292,12 @@ spec:
           - get
           - list
           - watch
+        - apiGroups:
+          - authentication
+          resources:
+          - tokenreviews
+          verbs:
+          - create
         - apiGroups:
           - autoscaling
           resources:
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 045114750..a492d12d4 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -35,6 +35,12 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - authentication
+  resources:
+  - tokenreviews
+  verbs:
+  - create
 - apiGroups:
   - autoscaling
   resources:
diff --git a/controllers/consoleplugin/consoleplugin_objects.go b/controllers/consoleplugin/consoleplugin_objects.go
index dbe22b46b..4d0bd6e72 100644
--- a/controllers/consoleplugin/consoleplugin_objects.go
+++ b/controllers/consoleplugin/consoleplugin_objects.go
@@ -12,6 +12,7 @@ import (
 	appsv1 "k8s.io/api/apps/v1"
 	ascv2 "k8s.io/api/autoscaling/v2"
 	corev1 "k8s.io/api/core/v1"
+	rbacv1 "k8s.io/api/rbac/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	flowsv1alpha1 "github.com/netobserv/network-observability-operator/api/v1alpha1"
@@ -297,18 +298,6 @@ func (b *builder) service(old *corev1.Service) *corev1.Service {
 	return newService
 }
 
-func buildServiceAccount(ns string) *corev1.ServiceAccount {
-	return &corev1.ServiceAccount{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      constants.PluginName,
-			Namespace: ns,
-			Labels: map[string]string{
-				"app": constants.PluginName,
-			},
-		},
-	}
-}
-
 // returns a configmap with a digest of its configuration contents, which will be used to
 // detect any configuration change
 func (b *builder) configMap() (*corev1.ConfigMap, string) {
@@ -337,3 +326,52 @@ func (b *builder) configMap() (*corev1.ConfigMap, string) {
 	digest := strconv.FormatUint(hasher.Sum64(), 36)
 	return &configMap, digest
 }
+
+func (b *builder) serviceAccount() *corev1.ServiceAccount {
+	return &corev1.ServiceAccount{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      constants.PluginName,
+			Namespace: b.namespace,
+			Labels: map[string]string{
+				"app": constants.PluginName,
+			},
+		},
+	}
+}
+
+// The operator needs to have at least the same permissions as flowlogs-pipeline in order to grant them
+//+kubebuilder:rbac:groups=authentication,resources=tokenreviews,verbs=create
+
+func buildClusterRole() *rbacv1.ClusterRole {
+	return &rbacv1.ClusterRole{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: constants.PluginName,
+		},
+		Rules: []rbacv1.PolicyRule{{
+			APIGroups: []string{"authentication"},
+			Verbs:     []string{"create"},
+			Resources: []string{"tokenreviews"},
+		}},
+	}
+}
+
+func (b *builder) clusterRoleBinding() *rbacv1.ClusterRoleBinding {
+	return &rbacv1.ClusterRoleBinding{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: constants.PluginName,
+			Labels: map[string]string{
+				"app": constants.PluginName,
+			},
+		},
+		RoleRef: rbacv1.RoleRef{
+			APIGroup: "rbac.authorization.k8s.io",
+			Kind:     "ClusterRole",
+			Name:     constants.PluginName,
+		},
+		Subjects: []rbacv1.Subject{{
+			Kind:      "ServiceAccount",
+			Name:      constants.PluginName,
+			Namespace: b.namespace,
+		}},
+	}
+}
diff --git a/controllers/consoleplugin/consoleplugin_reconciler.go b/controllers/consoleplugin/consoleplugin_reconciler.go
index ec1cf7239..d855a42fe 100644
--- a/controllers/consoleplugin/consoleplugin_reconciler.go
+++ b/controllers/consoleplugin/consoleplugin_reconciler.go
@@ -66,14 +66,16 @@ func NewReconciler(cl reconcilers.ClientHelper, ns, prevNS, imageName string, av
 
 // InitStaticResources inits some "static" / one-shot resources, usually not subject to reconciliation
 func (r *CPReconciler) InitStaticResources(ctx context.Context) error {
-	return r.CreateOwned(ctx, buildServiceAccount(r.nobjMngr.Namespace))
+	cr := buildClusterRole()
+	return r.ReconcileClusterRole(ctx, cr)
 }
 
 // PrepareNamespaceChange cleans up old namespace and restore the relevant "static" resources
 func (r *CPReconciler) PrepareNamespaceChange(ctx context.Context) error {
 	// Switching namespace => delete everything in the previous namespace
 	r.nobjMngr.CleanupPreviousNamespace(ctx)
-	return r.CreateOwned(ctx, buildServiceAccount(r.nobjMngr.Namespace))
+	cr := buildClusterRole()
+	return r.ReconcileClusterRole(ctx, cr)
 }
 
 // Reconcile is the reconciler entry point to reconcile the current plugin state with the desired configuration
@@ -92,6 +94,10 @@ func (r *CPReconciler) Reconcile(ctx context.Context, desired *flowsv1alpha1.Flo
 	// Create object builder
 	builder := newBuilder(ns, r.image, &desired.Spec.ConsolePlugin, &desired.Spec.Loki, r.CertWatcher)
 
+	if err := r.reconcilePermissions(ctx, &builder); err != nil {
+		return err
+	}
+
 	if err = r.reconcilePlugin(ctx, builder, &desired.Spec, ns); err != nil {
 		return err
 	}
@@ -136,6 +142,18 @@ func (r *CPReconciler) checkAutoPatch(ctx context.Context, desired *flowsv1alpha
 	return nil
 }
 
+func (r *CPReconciler) reconcilePermissions(ctx context.Context, builder *builder) error {
+	if !r.nobjMngr.Exists(r.owned.serviceAccount) {
+		return r.CreateOwned(ctx, builder.serviceAccount())
+	} // update not needed for now
+
+	desired := builder.clusterRoleBinding()
+	if err := r.ReconcileClusterRoleBinding(ctx, desired); err != nil {
+		return err
+	}
+	return nil
+}
+
 func (r *CPReconciler) reconcilePlugin(ctx context.Context, builder builder, desired *flowsv1alpha1.FlowCollectorSpec, ns string) error {
 	// Console plugin is cluster-scope (it's not deployed in our namespace) however it must still be updated if our namespace changes
 	oldPlg := osv1alpha1.ConsolePlugin{}