diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties index 8c660ec936..a012ebc0cc 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties @@ -663,3 +663,16 @@ che.oauth2.gitlab.clientid_filepath=NULL # Location of the file with GitLab client secret. che.oauth2.gitlab.clientsecret_filepath=NULL + +### Advanced authorization +# Comma separated list of users allowed to access Che. +che.infra.kubernetes.advanced_authorization.allowed_users=NULL + +# Comma separated list of groups of users allowed to access Che. +che.infra.kubernetes.advanced_authorization.allowed_groups=NULL + +# Comma separated list of users disallowed to access Che. +che.infra.kubernetes.advanced_authorization.disabled_users=NULL + +# Comma separated list of groups of users disallowed to access Che. +che.infra.kubernetes.advanced_authorization.disabled_groups=NULL diff --git a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java index a83fde764f..407d921293 100644 --- a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java +++ b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018 Red Hat, Inc. + * Copyright (c) 2012-2023 Red Hat, Inc. * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ @@ -11,6 +11,13 @@ */ package org.eclipse.che.commons.lang; +import static com.google.common.base.Strings.isNullOrEmpty; + +import com.google.common.base.Splitter; +import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.Set; + /** Set of useful String methods */ public class StringUtils { @@ -91,4 +98,13 @@ public class StringUtils { } return -1; } + + /** Parse string to set of strings. String should be comma separated. Whitespaces are trimmed. */ + public static Set strToSet(String str) { + if (!isNullOrEmpty(str)) { + return Sets.newHashSet(Splitter.on(",").trimResults().omitEmptyStrings().split(str)); + } else { + return Collections.emptySet(); + } + } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java index 1b5678ef1e..8fcc1be4fc 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java @@ -36,6 +36,9 @@ import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiInternalEnvV import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier; import org.eclipse.che.api.workspace.shared.Constants; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.KubernetesNamespaceService; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.KubernetesAuthorizationCheckerImpl; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.cache.jpa.JpaKubernetesRuntimeCacheModule; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.DockerimageComponentToWorkspaceApplier; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesComponentToWorkspaceApplier; @@ -110,6 +113,9 @@ public class KubernetesInfraModule extends AbstractModule { namespaceConfigurators.addBinding().to(SshKeysConfigurator.class); namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class); + bind(AuthorizationChecker.class).to(KubernetesAuthorizationCheckerImpl.class); + bind(PermissionsCleaner.class).asEagerSingleton(); + bind(KubernetesNamespaceService.class); MapBinder factories = diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationChecker.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationChecker.java new file mode 100644 index 0000000000..73b238c1de --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationChecker.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; + +/** This {@link AuthorizationChecker} checks if user is allowed to use Che. */ +public interface AuthorizationChecker { + + boolean isAuthorized(String username) throws InfrastructureException; +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationException.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationException.java new file mode 100644 index 0000000000..3b3eb0e087 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure; + +/** + * An exception thrown by {@link RuntimeInfrastructure} and related components. Indicates that an + * user is not authorized to use Che. + * + * @author Anatolii Bazko + */ +public class AuthorizationException extends InfrastructureException { + public AuthorizationException(String message) { + super(message); + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerImpl.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerImpl.java new file mode 100644 index 0000000000..0f18fc79a0 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import static org.eclipse.che.commons.lang.StringUtils.strToSet; + +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.commons.annotation.Nullable; + +/** This {@link KubernetesAuthorizationCheckerImpl} checks if user is allowed to use Che. */ +@Singleton +public class KubernetesAuthorizationCheckerImpl implements AuthorizationChecker { + + private final Set allowedUsers; + private final Set disabledUsers; + + @Inject + public KubernetesAuthorizationCheckerImpl( + @Nullable @Named("che.infra.kubernetes.advanced_authorization.allowed_users") + String allowedUsers, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.disabled_users") + String disabledUsers) { + this.allowedUsers = strToSet(allowedUsers); + this.disabledUsers = strToSet(disabledUsers); + } + + public boolean isAuthorized(String username) { + return isAllowedUser(username) && !isDisabledUser(username); + } + + private boolean isAllowedUser(String username) { + return allowedUsers.isEmpty() || allowedUsers.contains(username); + } + + private boolean isDisabledUser(String username) { + return !disabledUsers.isEmpty() && disabledUsers.contains(username); + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/PermissionsCleaner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/PermissionsCleaner.java new file mode 100644 index 0000000000..8b4eacaf98 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/PermissionsCleaner.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import static org.eclipse.che.commons.lang.StringUtils.strToSet; + +import io.fabric8.kubernetes.client.KubernetesClient; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; + +/** This {@link PermissionsCleaner} cleans up all user's permissions. */ +@Singleton +public class PermissionsCleaner { + + private final Set userClusterRoles; + private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory; + + @Inject + public PermissionsCleaner( + @Nullable @Named("che.infra.kubernetes.user_cluster_roles") String userClusterRoles, + CheServerKubernetesClientFactory cheServerKubernetesClientFactory) { + this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; + this.userClusterRoles = strToSet(userClusterRoles); + } + + public void cleanUp(String namespaceName) throws InfrastructureException { + KubernetesClient client = cheServerKubernetesClientFactory.create(); + for (String userClusterRole : userClusterRoles) { + client.rbac().roleBindings().inNamespace(namespaceName).withName(userClusterRole).delete(); + } + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java index a76a7b4207..3f7122bd86 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java @@ -57,6 +57,9 @@ import org.eclipse.che.inject.ConfigurationException; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationException; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool; import org.slf4j.Logger; @@ -98,6 +101,8 @@ public class KubernetesNamespaceFactory { private final PreferenceManager preferenceManager; protected final Set namespaceConfigurators; protected final KubernetesSharedPool sharedPool; + protected final AuthorizationChecker authorizationChecker; + protected final PermissionsCleaner permissionsCleaner; @Inject public KubernetesNamespaceFactory( @@ -110,7 +115,9 @@ public class KubernetesNamespaceFactory { Set namespaceConfigurators, CheServerKubernetesClientFactory cheServerKubernetesClientFactory, PreferenceManager preferenceManager, - KubernetesSharedPool sharedPool) + KubernetesSharedPool sharedPool, + AuthorizationChecker authorizationChecker, + PermissionsCleaner permissionsCleaner) throws ConfigurationException { this.namespaceCreationAllowed = namespaceCreationAllowed; this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; @@ -120,6 +127,8 @@ public class KubernetesNamespaceFactory { this.labelNamespaces = labelNamespaces; this.annotateNamespaces = annotateNamespaces; this.namespaceConfigurators = ImmutableSet.copyOf(namespaceConfigurators); + this.authorizationChecker = authorizationChecker; + this.permissionsCleaner = permissionsCleaner; //noinspection UnstableApiUsage Splitter.MapSplitter csvMapSplitter = Splitter.on(",").withKeyValueSeparator("="); @@ -281,6 +290,9 @@ public class KubernetesNamespaceFactory { var subject = EnvironmentContext.getCurrent().getSubject(); var userName = subject.getUserName(); + + validateAuthorization(namespace.getName(), userName); + NamespaceResolutionContext resolutionCtx = new NamespaceResolutionContext(identity.getWorkspaceId(), subject.getUserId(), userName); Map namespaceAnnotationsEvaluated = @@ -573,6 +585,27 @@ public class KubernetesNamespaceFactory { } } + protected void validateAuthorization(String namespaceName, String userName) + throws InfrastructureException { + if (!authorizationChecker.isAuthorized(userName)) { + try { + permissionsCleaner.cleanUp(namespaceName); + } catch (InfrastructureException | KubernetesClientException e) { + LOG.error( + "Failed to clean up permissions for user '{}' in namespace '{}'. Cause: {}", + userName, + namespaceName, + e.getMessage(), + e); + } + + throw new AuthorizationException( + format( + "User '%s' is not authorized to create a project. Please contact your system administrator.", + userName)); + } + } + protected String evalPlaceholders(String namespace, NamespaceResolutionContext ctx) { checkArgument(!isNullOrEmpty(namespace)); String evaluated = namespace; diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerTest.java new file mode 100644 index 0000000000..506b1ac575 --- /dev/null +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class KubernetesAuthorizationCheckerTest { + @Test(dataProvider = "advancedAuthorizationData") + public void advancedAuthorization( + String testUserName, String allowedUsers, String disabledUsers, boolean expectedIsAuthorized) + throws InfrastructureException { + // give + AuthorizationChecker authorizationChecker = + new KubernetesAuthorizationCheckerImpl(allowedUsers, disabledUsers); + + // when + boolean isAuthorized = authorizationChecker.isAuthorized(testUserName); + + // then + Assert.assertEquals(isAuthorized, expectedIsAuthorized); + } + + @DataProvider + public static Object[][] advancedAuthorizationData() { + return new Object[][] { + {"user1", "", "", true}, + {"user1", "user1", "", true}, + {"user1", "user1", "user2", true}, + {"user1", "user1", "user1", false}, + {"user2", "user1", "", false}, + {"user2", "user1", "user2", false}, + }; + } +} diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java index 061c06aaf9..689481e017 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java @@ -87,6 +87,8 @@ import org.eclipse.che.inject.ConfigurationException; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.PreferencesConfigMapConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.WorkspaceServiceAccountConfigurator; @@ -126,6 +128,8 @@ public class KubernetesNamespaceFactoryTest { private KubernetesClient k8sClient; @Mock private PreferenceManager preferenceManager; @Mock Appender mockedAppender; + @Mock AuthorizationChecker authorizationChecker; + @Mock PermissionsCleaner permissionsCleaner; @Mock private NonNamespaceOperation namespaceOperation; @@ -151,6 +155,7 @@ public class KubernetesNamespaceFactoryTest { lenient().when(namespaceOperation.withName(any())).thenReturn(namespaceResource); lenient().when(namespaceResource.get()).thenReturn(mock(Namespace.class)); + lenient().when(authorizationChecker.isAuthorized(anyString())).thenReturn(true); lenient().doReturn(namespaceListResource).when(namespaceOperation).withLabels(anyMap()); lenient().when(namespaceListResource.list()).thenReturn(namespaceList); @@ -179,7 +184,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); namespaceFactory.checkIfNamespaceIsAllowed("jondoe-che"); } @@ -204,7 +211,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); namespaceFactory.checkIfNamespaceIsAllowed("any-namespace"); } @@ -222,7 +231,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); assertEquals("che-kube-admin", namespaceFactory.normalizeNamespaceName("kube:admin")); } @@ -245,7 +256,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); namespaceFactory.checkIfNamespaceIsAllowed("any-namespace"); } @@ -265,7 +278,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); } @Test @@ -313,7 +328,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); // when @@ -354,7 +371,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); // when @@ -382,7 +401,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); // when namespaceFactory.list(); @@ -413,7 +434,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); List availableNamespaces = namespaceFactory.list(); assertEquals(availableNamespaces.size(), 1); @@ -439,7 +462,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); List availableNamespaces = namespaceFactory.list(); assertEquals(availableNamespaces.size(), 1); @@ -467,7 +492,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(new PreferencesConfigMapConfigurator(cheServerKubernetesClientFactory)), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); when(toReturnNamespace.getName()).thenReturn("namespaceName"); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -508,7 +535,9 @@ public class KubernetesNamespaceFactoryTest { namespaceConfigurators, cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); @@ -543,7 +572,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -575,7 +606,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -608,7 +641,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); throwOnTryToGetNamespaceByName( "jondoe-che", new KubernetesClientException("connection refused")); @@ -631,7 +666,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); throwOnTryToGetNamespacesList(new KubernetesClientException("connection refused")); namespaceFactory.list(); @@ -659,7 +696,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -688,7 +727,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -723,7 +764,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(serviceAccountCfg), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -761,7 +804,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(serviceAccountConfigurator), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -836,7 +881,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(serviceAccountConfigurator), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -881,7 +928,9 @@ public class KubernetesNamespaceFactoryTest { "serviceAccount", "", cheServerKubernetesClientFactory)), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -945,7 +994,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); WorkspaceImpl workspace = new WorkspaceImplBuilder().setId("workspace123").setAttributes(emptyMap()).build(); @@ -968,7 +1019,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); Map prefs = new HashMap<>(); prefs.put(WORKSPACE_INFRASTRUCTURE_NAMESPACE_ATTRIBUTE, "che-123"); @@ -996,7 +1049,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); Map prefs = new HashMap<>(); // returned but ignored @@ -1025,7 +1080,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); Map prefs = new HashMap<>(); // returned but ignored @@ -1054,7 +1111,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); doReturn(empty()).when(namespaceFactory).fetchNamespace(anyString()); String namespace = @@ -1079,7 +1138,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); WorkspaceImpl workspace = new WorkspaceImplBuilder() @@ -1106,7 +1167,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); WorkspaceImpl workspace = new WorkspaceImplBuilder() @@ -1154,7 +1217,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); String namespace = namespaceFactory.evaluateNamespaceName( @@ -1178,7 +1243,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("jondoe-che"); @@ -1215,7 +1282,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("jondoe-cha-cha-cha"); @@ -1251,7 +1320,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("jondoe-cha-cha-cha"); @@ -1299,7 +1370,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); namespaceFactory.list(); @@ -1322,7 +1395,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); @@ -1351,7 +1426,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); assertEquals(expected, namespaceFactory.normalizeNamespaceName(raw)); } @@ -1368,7 +1445,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); assertEquals( 63, diff --git a/infrastructures/openshift/pom.xml b/infrastructures/openshift/pom.xml index 838bacde8f..ed6c249a3b 100644 --- a/infrastructures/openshift/pom.xml +++ b/infrastructures/openshift/pom.xml @@ -161,6 +161,17 @@ mockwebserver test + + io.fabric8 + kubernetes-server-mock + test + + + junit-jupiter-api + org.junit.jupiter + + + io.fabric8 openshift-server-mock diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java index 3b7ab5c538..4ec75404dd 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java @@ -40,6 +40,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientTermi import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesEnvironmentProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.StartSynchronizerFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.KubernetesNamespaceService; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.cache.jpa.JpaKubernetesRuntimeCacheModule; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.DockerimageComponentToWorkspaceApplier; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesComponentToWorkspaceApplier; @@ -80,6 +82,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.PluginBroke import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.SidecarToolingProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.brokerphases.BrokerEnvironmentFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService; +import org.eclipse.che.workspace.infrastructure.openshift.authorization.OpenShiftAuthorizationCheckerImpl; import org.eclipse.che.workspace.infrastructure.openshift.devfile.OpenshiftComponentToWorkspaceApplier; import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory; @@ -118,6 +121,9 @@ public class OpenShiftInfraModule extends AbstractModule { namespaceConfigurators.addBinding().to(SshKeysConfigurator.class); namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class); + bind(AuthorizationChecker.class).to(OpenShiftAuthorizationCheckerImpl.class); + bind(PermissionsCleaner.class).asEagerSingleton(); + bind(KubernetesNamespaceService.class); MapBinder factories = diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerImpl.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerImpl.java new file mode 100644 index 0000000000..67a3fbc1ba --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerImpl.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.authorization; + +import static org.eclipse.che.commons.lang.StringUtils.strToSet; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.openshift.api.model.Group; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; + +/** This {@link OpenShiftAuthorizationCheckerImpl} checks if user is allowed to use Che. */ +@Singleton +public class OpenShiftAuthorizationCheckerImpl implements AuthorizationChecker { + + private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory; + + private final Set allowedUsers; + private final Set allowedGroups; + private final Set disabledUsers; + private final Set disabledGroups; + + @Inject + public OpenShiftAuthorizationCheckerImpl( + @Nullable @Named("che.infra.kubernetes.advanced_authorization.allowed_users") + String allowedUsers, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.allowed_groups") + String allowedGroups, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.disabled_users") + String disabledUsers, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.disabled_groups") + String disabledGroups, + CheServerKubernetesClientFactory cheServerKubernetesClientFactory) { + this.allowedUsers = strToSet(allowedUsers); + this.allowedGroups = strToSet(allowedGroups); + this.disabledUsers = strToSet(disabledUsers); + this.disabledGroups = strToSet(disabledGroups); + this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; + } + + public boolean isAuthorized(String username) throws InfrastructureException { + return isAllowedUser(cheServerKubernetesClientFactory.create(), username) + && !isDisabledUser(cheServerKubernetesClientFactory.create(), username); + } + + private boolean isAllowedUser(KubernetesClient client, String username) { + // All users from all groups are allowed by default + if (allowedUsers.isEmpty() && allowedGroups.isEmpty()) { + return true; + } + + if (allowedUsers.contains(username)) { + return true; + } + + for (String groupName : allowedGroups) { + Group group = client.resources(Group.class).withName(groupName).get(); + if (group != null && group.getUsers().contains(username)) { + return true; + } + } + + return false; + } + + private boolean isDisabledUser(KubernetesClient client, String username) { + // All users from all groups are allowed by default + if (disabledUsers.isEmpty() && disabledGroups.isEmpty()) { + return false; + } + + if (disabledUsers.contains(username)) { + return true; + } + + for (String groupName : disabledGroups) { + Group group = client.resources(Group.class).withName(groupName).get(); + if (group != null && group.getUsers().contains(username)) { + return true; + } + } + + return false; + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java index 7ae64d5574..bc1976b2f3 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java @@ -40,6 +40,8 @@ import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool; @@ -81,6 +83,8 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory { CheServerOpenshiftClientFactory cheServerOpenshiftClientFactory, PreferenceManager preferenceManager, KubernetesSharedPool sharedPool, + AuthorizationChecker authorizationChecker, + PermissionsCleaner permissionsCleaner, @Nullable @Named("che.infra.openshift.oauth_identity_provider") String oAuthIdentityProvider) { super( @@ -93,7 +97,9 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory { namespaceConfigurators, cheServerKubernetesClientFactory, preferenceManager, - sharedPool); + sharedPool, + authorizationChecker, + permissionsCleaner); this.initWithCheServerSa = initWithCheServerSa; this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; this.cheServerOpenshiftClientFactory = cheServerOpenshiftClientFactory; @@ -105,6 +111,9 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory { OpenShiftProject osProject = get(identity); var subject = EnvironmentContext.getCurrent().getSubject(); var userName = subject.getUserName(); + + validateAuthorization(osProject.getName(), userName); + NamespaceResolutionContext resolutionCtx = new NamespaceResolutionContext(identity.getWorkspaceId(), subject.getUserId(), userName); Map namespaceAnnotationsEvaluated = diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerTest.java new file mode 100644 index 0000000000..a81a9faeab --- /dev/null +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.authorization; + +import static org.mockito.Mockito.*; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import io.fabric8.openshift.api.model.Group; +import java.util.Collections; +import java.util.List; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class OpenShiftAuthorizationCheckerTest { + + @Mock private CheServerKubernetesClientFactory clientFactory; + private KubernetesClient client; + private KubernetesServer serverMock; + + @BeforeMethod + public void setUp() throws InfrastructureException { + serverMock = new KubernetesServer(true, true); + serverMock.before(); + client = spy(serverMock.getClient()); + lenient().when(clientFactory.create()).thenReturn(client); + } + + @Test(dataProvider = "advancedAuthorizationData") + public void advancedAuthorization( + String testUserName, + List groups, + String allowedUsers, + String allowedGroups, + String disabledUsers, + String disabledGroups, + boolean expectedIsAuthorized) + throws InfrastructureException { + // give + OpenShiftAuthorizationCheckerImpl authorizationChecker = + new OpenShiftAuthorizationCheckerImpl( + allowedUsers, allowedGroups, disabledUsers, disabledGroups, clientFactory); + groups.forEach(group -> client.resources(Group.class).create(group)); + + // when + boolean isAuthorized = authorizationChecker.isAuthorized(testUserName); + + // then + Assert.assertEquals(isAuthorized, expectedIsAuthorized); + } + + @DataProvider + public static Object[][] advancedAuthorizationData() { + Group groupWithUser1 = + new Group( + "v1", + "Group", + new ObjectMetaBuilder().withName("groupWithUser1").build(), + List.of("user1")); + Group groupWithUser2 = + new Group( + "v1", + "Group", + new ObjectMetaBuilder().withName("groupWithUser2").build(), + List.of("user2")); + + return new Object[][] { + {"user1", Collections.emptyList(), "", "", "", "", true}, + {"user1", Collections.emptyList(), "user1", "", "", "", true}, + {"user1", Collections.emptyList(), "user1", "", "user2", "", true}, + {"user1", List.of(groupWithUser2), "user1", "", "", "groupWithUser2", true}, + {"user1", List.of(groupWithUser1), "", "groupWithUser1", "", "", true}, + {"user2", List.of(groupWithUser1), "user2", "groupWithUser1", "", "", true}, + { + "user1", + List.of(groupWithUser1, groupWithUser2), + "", + "groupWithUser1", + "", + "groupWithUser2", + true + }, + {"user1", Collections.emptyList(), "user1", "", "user1", "", false}, + {"user2", Collections.emptyList(), "user1", "", "", "", false}, + {"user2", Collections.emptyList(), "user1", "", "user2", "", false}, + {"user2", List.of(groupWithUser1), "", "groupWithUser1", "", "", false}, + {"user1", Collections.emptyList(), "", "", "user1", "", false}, + {"user1", List.of(groupWithUser1), "", "", "", "groupWithUser1", false}, + {"user1", List.of(groupWithUser1), "", "groupWithUser1", "", "groupWithUser1", false}, + { + "user2", + List.of(groupWithUser1, groupWithUser2), + "", + "groupWithUser1", + "", + "groupWithUser2", + false + }, + }; + } +} diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java index 93f2d36876..c620aab5d8 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java @@ -71,6 +71,8 @@ import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.inject.ConfigurationException; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesSecrets; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; @@ -113,6 +115,8 @@ public class OpenShiftProjectFactoryTest { @Mock private WorkspaceManager workspaceManager; @Mock private PreferenceManager preferenceManager; @Mock private KubernetesSharedPool pool; + @Mock private AuthorizationChecker authorizationChecker; + @Mock private PermissionsCleaner permissionsCleaner; @Mock private ProjectOperation projectOperation; @@ -134,6 +138,7 @@ public class OpenShiftProjectFactoryTest { lenient().when(cheServerOpenshiftClientFactory.createOC()).thenReturn(osClient); lenient().when(cheServerKubernetesClientFactory.create()).thenReturn(osClient); lenient().when(osClient.projects()).thenReturn(projectOperation); + lenient().when(authorizationChecker.isAuthorized(anyString())).thenReturn(true); lenient() .when(workspaceManager.getWorkspace(any())) @@ -174,6 +179,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); projectFactory.checkIfNamespaceIsAllowed(USER_NAME + "-che"); @@ -204,6 +211,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); try { projectFactory.checkIfNamespaceIsAllowed("any-namespace"); @@ -234,6 +243,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); } @@ -269,6 +280,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); @@ -305,6 +318,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "u123", null, false)); @@ -337,6 +352,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); // when @@ -379,6 +396,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); List availableNamespaces = projectFactory.list(); @@ -415,6 +434,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); List availableNamespaces = projectFactory.list(); @@ -451,6 +472,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); projectFactory.list(); @@ -477,6 +500,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); projectFactory.list(); @@ -508,6 +533,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); prepareProject(toReturnProject); @@ -542,6 +569,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any()); @@ -584,6 +613,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); prepareProject(toReturnProject); @@ -628,6 +659,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); when(toReturnProject.getName()).thenReturn("workspace123"); @@ -677,6 +710,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); String namespace = @@ -709,6 +744,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); projectFactory.list(); @@ -735,6 +772,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); @@ -775,6 +814,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false));