Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: permissions refactoring #1202

Merged
merged 5 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 10 additions & 103 deletions controllers/che/checluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ package che

import (
"context"
"strings"
"time"

"github.com/eclipse-che/che-operator/pkg/deploy"
Expand All @@ -27,6 +26,7 @@ import (
openshiftoauth "github.com/eclipse-che/che-operator/pkg/deploy/openshift-oauth"
"github.com/eclipse-che/che-operator/pkg/deploy/pluginregistry"
"github.com/eclipse-che/che-operator/pkg/deploy/postgres"
"github.com/eclipse-che/che-operator/pkg/deploy/rbac"
"github.com/eclipse-che/che-operator/pkg/deploy/server"
"github.com/eclipse-che/che-operator/pkg/deploy/tls"

Expand All @@ -37,7 +37,7 @@ import (
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
rbacv1 "k8s.io/api/rbac/v1"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/discovery"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -99,6 +99,10 @@ func NewReconciler(
reconcileManager.RegisterReconciler(openshiftoauth.NewOpenShiftOAuth(openShiftOAuthUser))
reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler())
reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler())
reconcileManager.RegisterReconciler(devworkspace.NewDevWorkspaceReconciler())
reconcileManager.RegisterReconciler(rbac.NewCheServerPermissionsReconciler())
reconcileManager.RegisterReconciler(rbac.NewGatewayPermissionsReconciler())
reconcileManager.RegisterReconciler(rbac.NewWorkspacePermissionsReconciler())

return &CheClusterReconciler{
Scheme: scheme,
Expand Down Expand Up @@ -163,11 +167,11 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
IsController: true,
OwnerType: &orgv1.CheCluster{},
}).
Watches(&source.Kind{Type: &rbac.Role{}}, &handler.EnqueueRequestForOwner{
Watches(&source.Kind{Type: &rbacv1.Role{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &orgv1.CheCluster{},
}).
Watches(&source.Kind{Type: &rbac.RoleBinding{}}, &handler.EnqueueRequestForOwner{
Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &orgv1.CheCluster{},
}).
Expand Down Expand Up @@ -225,7 +229,6 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = r.Log.WithValues("checluster", req.NamespacedName)

tests := r.tests
clusterAPI := deploy.ClusterAPI{
Client: r.client,
NonCachingClient: r.nonCachedClient,
Expand Down Expand Up @@ -308,83 +311,14 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
// TODO remove in favor of r.reconcileManager.FinalizeAll(deployContext)
r.reconcileFinalizers(deployContext)

// Reconcile Dev Workspace Operator
done, err := devworkspace.ReconcileDevWorkspace(deployContext)
if !done {
if err != nil {
r.Log.Error(err, "")
}
// We should `Requeue` since we don't watch Dev Workspace controller objects
return ctrl.Result{RequeueAfter: time.Second}, err
}

// Create service account "che" for che-server component.
// "che" is the one which token is used to create workspace objects.
// Notice: Also we have on more "che-workspace" SA used by plugins like exec, terminal, metrics with limited privileges.
done, err = deploy.SyncServiceAccountToCluster(deployContext, deploy.CheServiceAccountName)
if !done {
if err != nil {
logrus.Error(err)
}
return ctrl.Result{RequeueAfter: time.Second}, err
}

if done, err = r.reconcileGatewayPermissions(deployContext); !done {
if err != nil {
logrus.Error(err)
}
// reconcile after 1 seconds since we deal with cluster objects
return reconcile.Result{RequeueAfter: time.Second}, err
}

done, err = r.reconcileWorkspacePermissions(deployContext)
if !done {
if err != nil {
logrus.Error(err)
}
// reconcile after 1 seconds since we deal with cluster objects
return ctrl.Result{RequeueAfter: time.Second}, err
}

if len(checluster.Spec.Server.CheClusterRoles) > 0 {
cheClusterRoles := strings.Split(checluster.Spec.Server.CheClusterRoles, ",")
for _, cheClusterRole := range cheClusterRoles {
cheClusterRole := strings.TrimSpace(cheClusterRole)
cheClusterRoleBindingName := cheClusterRole
done, err := deploy.SyncClusterRoleBindingAndAddFinalizerToCluster(deployContext, cheClusterRoleBindingName, deploy.CheServiceAccountName, cheClusterRole)
if !tests {
if !done {
logrus.Infof("Waiting on cluster role binding '%s' to be created", cheClusterRoleBindingName)
if err != nil {
logrus.Error(err)
}
return ctrl.Result{RequeueAfter: time.Second}, err
}
}
}
}

// If the user specified an additional cluster role to use for the Che workspace, create a role binding for it
// Use a role binding instead of a cluster role binding to keep the additional access scoped to the workspace's namespace
workspaceClusterRole := checluster.Spec.Server.CheWorkspaceClusterRole
if workspaceClusterRole != "" {
done, err := deploy.SyncRoleBindingToCluster(deployContext, "che-workspace-custom", "view", workspaceClusterRole, "ClusterRole")
if !done {
if err != nil {
logrus.Error(err)
}
return ctrl.Result{RequeueAfter: time.Second}, err
}
}

if err := r.GenerateAndSaveFields(deployContext); err != nil {
_ = deploy.ReloadCheClusterCR(deployContext)
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 1}, err
}

if !deployContext.CheCluster.Spec.Database.ExternalDb {
postgres := postgres.NewPostgres(deployContext)
done, err = postgres.SyncAll()
done, err := postgres.SyncAll()
if !done {
if err != nil {
logrus.Error(err)
Expand All @@ -396,7 +330,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
// we have to expose che endpoint independently of syncing other server
// resources since che host is used for dashboard deployment and che config map
server := server.NewServer(deployContext)
done, err = server.ExposeCheServiceAndEndpoint()
done, err := server.ExposeCheServiceAndEndpoint()
if !done {
if err != nil {
logrus.Error(err)
Expand Down Expand Up @@ -523,16 +457,6 @@ func (r *CheClusterReconciler) reconcileFinalizers(deployContext *deploy.DeployC
}
}

if deployContext.CheCluster.IsNativeUserModeEnabled() {
if _, err := r.reconcileGatewayPermissionsFinalizers(deployContext); err != nil {
logrus.Error(err)
}
}

if _, err := r.reconcileWorkspacePermissionsFinalizers(deployContext); err != nil {
logrus.Error(err)
}

if err := deploy.ReconcileConsoleLinkFinalizer(deployContext); err != nil {
logrus.Error(err)
}
Expand All @@ -543,23 +467,6 @@ func (r *CheClusterReconciler) reconcileFinalizers(deployContext *deploy.DeployC
logrus.Error(err)
}
}

if len(deployContext.CheCluster.Spec.Server.CheClusterRoles) > 0 {
cheClusterRoles := strings.Split(deployContext.CheCluster.Spec.Server.CheClusterRoles, ",")
for _, cheClusterRole := range cheClusterRoles {
cheClusterRole := strings.TrimSpace(cheClusterRole)
cheClusterRoleBindingName := cheClusterRole
if err := deploy.ReconcileClusterRoleBindingFinalizer(deployContext, cheClusterRoleBindingName); err != nil {
logrus.Error(err)
}

// Removes any legacy CRB https://github.com/eclipse/che/issues/19506
cheClusterRoleBindingName = deploy.GetLegacyUniqueClusterRoleBindingName(deployContext, deploy.CheServiceAccountName, cheClusterRole)
if err := deploy.ReconcileLegacyClusterRoleBindingFinalizer(deployContext, cheClusterRoleBindingName); err != nil {
logrus.Error(err)
}
}
}
}

func (r *CheClusterReconciler) GetCR(request ctrl.Request) (instance *orgv1.CheCluster, err error) {
Expand Down
137 changes: 2 additions & 135 deletions controllers/che/checluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,13 @@ package che

import (
"context"
"fmt"
"os"
"strconv"

mocks "github.com/eclipse-che/che-operator/mocks"

"reflect"
"time"

chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
"github.com/golang/mock/gomock"
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"

devworkspace "github.com/eclipse-che/che-operator/pkg/deploy/dev-workspace"
Expand All @@ -47,7 +43,7 @@ import (
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/utils/pointer"

"k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -481,7 +477,7 @@ func TestCheController(t *testing.T) {
}

// Get the custom role binding that should have been created for the role we passed in
rb := &rbac.RoleBinding{}
rb := &rbacv1.RoleBinding{}
if err := cl.Get(context.TODO(), types.NamespacedName{Name: "che-workspace-custom", Namespace: cheCR.Namespace}, rb); err != nil {
t.Errorf("Custom role binding %s not found: %s", rb.Name, err)
}
Expand Down Expand Up @@ -693,135 +689,6 @@ func TestConfiguringLabelsForRoutes(t *testing.T) {
}
}

func TestShouldDelegatePermissionsForCheWorkspaces(t *testing.T) {
util.IsOpenShift = true

type testCase struct {
name string
initObjects []runtime.Object

clusterRole bool
checluster *orgv1.CheCluster
}

// the same namespace with Che
crWsInTheSameNs1 := InitCheWithSimpleCR().DeepCopy()
crWsInTheSameNs1.Spec.Server.WorkspaceNamespaceDefault = crWsInTheSameNs1.Namespace

crWsInTheSameNs2 := InitCheWithSimpleCR().DeepCopy()
crWsInTheSameNs2.Spec.Server.WorkspaceNamespaceDefault = ""

crWsInTheSameNs3 := InitCheWithSimpleCR().DeepCopy()
crWsInTheSameNs3.Spec.Server.CustomCheProperties = make(map[string]string)
crWsInTheSameNs3.Spec.Server.CustomCheProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"] = ""

crWsInTheSameNs4 := InitCheWithSimpleCR().DeepCopy()
crWsInTheSameNs4.Spec.Server.CustomCheProperties = make(map[string]string)
crWsInTheSameNs4.Spec.Server.CustomCheProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"] = crWsInTheSameNs1.Namespace

// differ namespace with Che
crWsInAnotherNs1 := InitCheWithSimpleCR().DeepCopy()
crWsInAnotherNs1.Spec.Server.WorkspaceNamespaceDefault = "some-test-namespace"

crWsInAnotherNs2 := InitCheWithSimpleCR().DeepCopy()
crWsInAnotherNs2.Spec.Server.CustomCheProperties = make(map[string]string)
crWsInAnotherNs2.Spec.Server.CustomCheProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"] = "some-test-namespace"

crWsInAnotherNs3 := InitCheWithSimpleCR().DeepCopy()
crWsInAnotherNs3.Spec.Server.CustomCheProperties = make(map[string]string)
crWsInAnotherNs3.Spec.Server.CustomCheProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"] = crWsInTheSameNs1.Namespace
crWsInAnotherNs3.Spec.Server.WorkspaceNamespaceDefault = "some-test-namespace"

testCases := []testCase{
{
name: "che-operator should delegate permission for workspaces in differ namespace than Che. WorkspaceNamespaceDefault = 'some-test-namespace'",
initObjects: []runtime.Object{},
clusterRole: true,
checluster: crWsInAnotherNs1,
},
{
name: "che-operator should delegate permission for workspaces in differ namespace than Che. Property CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT = 'some-test-namespace'",
initObjects: []runtime.Object{},
clusterRole: true,
checluster: crWsInAnotherNs2,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))

scheme := scheme.Scheme
orgv1.SchemeBuilder.AddToScheme(scheme)
scheme.AddKnownTypes(oauthv1.SchemeGroupVersion, &oauthv1.OAuthClient{})
scheme.AddKnownTypes(userv1.SchemeGroupVersion, &userv1.UserList{}, &userv1.User{})
scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.OAuth{}, &configv1.Proxy{})
scheme.AddKnownTypes(routev1.GroupVersion, &routev1.Route{})

initCR := testCase.checluster
initCR.Spec.Auth.OpenShiftoAuth = pointer.BoolPtr(false)
testCase.initObjects = append(testCase.initObjects, initCR)

cli := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
nonCachedClient := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
clientSet := fakeclientset.NewSimpleClientset()
// todo do we need fake discovery
fakeDiscovery, ok := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery)
fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{}

if !ok {
t.Fatal("Error creating fake discovery client")
}

var m *mocks.MockPermissionChecker
if testCase.clusterRole {
ctrl := gomock.NewController(t)
m = mocks.NewMockPermissionChecker(ctrl)
m.EXPECT().GetNotPermittedPolicyRules(gomock.Any(), "").Return([]rbac.PolicyRule{}, nil).MaxTimes(2)
defer ctrl.Finish()
}

r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
r.tests = true

req := reconcile.Request{
NamespacedName: types.NamespacedName{
Name: os.Getenv("CHE_FLAVOR"),
Namespace: namespace,
},
}

_, err := r.Reconcile(context.TODO(), req)
if err != nil {
t.Fatalf("Error reconciling: %v", err)
}
_, err = r.Reconcile(context.TODO(), req)
if err != nil {
t.Fatalf("Error reconciling: %v", err)
}

manageNamespacesClusterRoleName := fmt.Sprintf(CheNamespaceEditorClusterRoleNameTemplate, namespace)
cheManageNamespaceClusterRole := &rbac.ClusterRole{}
if err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Name: manageNamespacesClusterRoleName}, cheManageNamespaceClusterRole); err != nil {
t.Errorf("role '%s' not found", manageNamespacesClusterRoleName)
}
cheManageNamespaceClusterRoleBinding := &rbac.ClusterRoleBinding{}
if err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Name: manageNamespacesClusterRoleName}, cheManageNamespaceClusterRoleBinding); err != nil {
t.Errorf("rolebinding '%s' not found", manageNamespacesClusterRoleName)
}

cheWorkspacesClusterRoleName := fmt.Sprintf(CheWorkspacesClusterRoleNameTemplate, namespace)
cheWorkspacesClusterRole := &rbac.ClusterRole{}
if err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Name: cheWorkspacesClusterRoleName}, cheWorkspacesClusterRole); err != nil {
t.Errorf("role '%s' not found", cheWorkspacesClusterRole)
}
cheWorkspacesClusterRoleBinding := &rbac.ClusterRoleBinding{}
if err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Name: cheWorkspacesClusterRoleName}, cheWorkspacesClusterRoleBinding); err != nil {
t.Errorf("rolebinding '%s' not found", cheWorkspacesClusterRole)
}
})
}
}

func Init() (client.Client, discovery.DiscoveryInterface, runtime.Scheme) {
objs, ds, scheme := createAPIObjects()

Expand Down
Loading