feat: native auth on Kubernetes (#171)
Signed-off-by: Michal Vala <mvala@redhat.com>pull/186/head^2
parent
f289ec619f
commit
388a5183be
|
|
@ -35,6 +35,10 @@
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>jwks-rsa</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
|
|
@ -275,6 +279,10 @@
|
||||||
<groupId>org.eclipse.che.multiuser</groupId>
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
<artifactId>che-multiuser-machine-authentication</artifactId>
|
<artifactId>che-multiuser-machine-authentication</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<artifactId>che-multiuser-oidc</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.che.multiuser</groupId>
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
<artifactId>che-multiuser-permission-devfile</artifactId>
|
<artifactId>che-multiuser-permission-devfile</artifactId>
|
||||||
|
|
@ -454,6 +462,7 @@
|
||||||
<dep>com.google.guava:guava</dep>
|
<dep>com.google.guava:guava</dep>
|
||||||
<dep>org.everrest:everrest-core</dep>
|
<dep>org.everrest:everrest-core</dep>
|
||||||
<dep>io.jsonwebtoken:jjwt-jackson</dep>
|
<dep>io.jsonwebtoken:jjwt-jackson</dep>
|
||||||
|
<dep>io.jsonwebtoken:jjwt-impl</dep>
|
||||||
</ignoredUnusedDeclaredDependencies>
|
</ignoredUnusedDeclaredDependencies>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import static com.google.inject.matcher.Matchers.subclassesOf;
|
||||||
import static org.eclipse.che.inject.Matchers.names;
|
import static org.eclipse.che.inject.Matchers.names;
|
||||||
import static org.eclipse.che.multiuser.api.permission.server.SystemDomain.SYSTEM_DOMAIN_ACTIONS;
|
import static org.eclipse.che.multiuser.api.permission.server.SystemDomain.SYSTEM_DOMAIN_ACTIONS;
|
||||||
|
|
||||||
|
import com.auth0.jwk.JwkProvider;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||||
|
|
@ -22,7 +23,7 @@ import com.google.inject.multibindings.MapBinder;
|
||||||
import com.google.inject.multibindings.Multibinder;
|
import com.google.inject.multibindings.Multibinder;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Names;
|
||||||
import io.jsonwebtoken.JwtParser;
|
import io.jsonwebtoken.JwtParser;
|
||||||
import io.jsonwebtoken.impl.DefaultJwtParser;
|
import io.jsonwebtoken.SigningKeyResolver;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
@ -81,7 +82,6 @@ import org.eclipse.che.commons.observability.deploy.ExecutorWrapperModule;
|
||||||
import org.eclipse.che.core.db.DBTermination;
|
import org.eclipse.che.core.db.DBTermination;
|
||||||
import org.eclipse.che.core.db.schema.SchemaInitializer;
|
import org.eclipse.che.core.db.schema.SchemaInitializer;
|
||||||
import org.eclipse.che.core.tracing.metrics.TracingMetricsModule;
|
import org.eclipse.che.core.tracing.metrics.TracingMetricsModule;
|
||||||
import org.eclipse.che.inject.ConfigurationException;
|
|
||||||
import org.eclipse.che.inject.DynaModule;
|
import org.eclipse.che.inject.DynaModule;
|
||||||
import org.eclipse.che.multiuser.api.authentication.commons.token.ChainedTokenExtractor;
|
import org.eclipse.che.multiuser.api.authentication.commons.token.ChainedTokenExtractor;
|
||||||
import org.eclipse.che.multiuser.api.authentication.commons.token.HeaderRequestTokenExtractor;
|
import org.eclipse.che.multiuser.api.authentication.commons.token.HeaderRequestTokenExtractor;
|
||||||
|
|
@ -93,6 +93,11 @@ import org.eclipse.che.multiuser.api.workspace.activity.MultiUserWorkspaceActivi
|
||||||
import org.eclipse.che.multiuser.keycloak.server.deploy.KeycloakModule;
|
import org.eclipse.che.multiuser.keycloak.server.deploy.KeycloakModule;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.deploy.KeycloakUserRemoverModule;
|
import org.eclipse.che.multiuser.keycloak.server.deploy.KeycloakUserRemoverModule;
|
||||||
import org.eclipse.che.multiuser.machine.authentication.server.MachineAuthModule;
|
import org.eclipse.che.multiuser.machine.authentication.server.MachineAuthModule;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfoProvider;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCJwkProvider;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCJwtParserProvider;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCSigningKeyResolver;
|
||||||
import org.eclipse.che.multiuser.organization.api.OrganizationApiModule;
|
import org.eclipse.che.multiuser.organization.api.OrganizationApiModule;
|
||||||
import org.eclipse.che.multiuser.organization.api.OrganizationJpaModule;
|
import org.eclipse.che.multiuser.organization.api.OrganizationJpaModule;
|
||||||
import org.eclipse.che.multiuser.permission.user.UserServicePermissionsFilter;
|
import org.eclipse.che.multiuser.permission.user.UserServicePermissionsFilter;
|
||||||
|
|
@ -335,18 +340,10 @@ public class WsMasterModule extends AbstractModule {
|
||||||
.to(org.eclipse.che.api.workspace.server.DefaultWorkspaceStatusCache.class);
|
.to(org.eclipse.che.api.workspace.server.DefaultWorkspaceStatusCache.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OpenShiftInfrastructure.NAME.equals(infrastructure)) {
|
if (Boolean.parseBoolean(System.getenv("CHE_AUTH_NATIVEUSER"))) {
|
||||||
if (Boolean.parseBoolean(System.getenv("CHE_AUTH_NATIVEUSER"))) {
|
bind(KubernetesClientConfigFactory.class).to(KubernetesOidcProviderConfigFactory.class);
|
||||||
bind(KubernetesClientConfigFactory.class).to(KubernetesOidcProviderConfigFactory.class);
|
} else if (OpenShiftInfrastructure.NAME.equals(infrastructure)) {
|
||||||
} else {
|
bind(KubernetesClientConfigFactory.class).to(KeycloakProviderConfigFactory.class);
|
||||||
bind(KubernetesClientConfigFactory.class).to(KeycloakProviderConfigFactory.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (KubernetesInfrastructure.NAME.equals(infrastructure)
|
|
||||||
&& Boolean.parseBoolean(System.getenv("CHE_AUTH_NATIVEUSER"))) {
|
|
||||||
throw new ConfigurationException(
|
|
||||||
"Native user mode is not supported on Kubernetes. It is supported only on OpenShift.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
persistenceProperties.put(
|
persistenceProperties.put(
|
||||||
|
|
@ -395,11 +392,16 @@ public class WsMasterModule extends AbstractModule {
|
||||||
install(new OrganizationJpaModule());
|
install(new OrganizationJpaModule());
|
||||||
|
|
||||||
if (Boolean.parseBoolean(System.getenv("CHE_AUTH_NATIVEUSER"))) {
|
if (Boolean.parseBoolean(System.getenv("CHE_AUTH_NATIVEUSER"))) {
|
||||||
|
bind(RequestTokenExtractor.class).to(HeaderRequestTokenExtractor.class);
|
||||||
|
if (KubernetesInfrastructure.NAME.equals(infrastructure)) {
|
||||||
|
bind(OIDCInfo.class).toProvider(OIDCInfoProvider.class).asEagerSingleton();
|
||||||
|
bind(SigningKeyResolver.class).to(OIDCSigningKeyResolver.class);
|
||||||
|
bind(JwtParser.class).toProvider(OIDCJwtParserProvider.class);
|
||||||
|
bind(JwkProvider.class).toProvider(OIDCJwkProvider.class);
|
||||||
|
}
|
||||||
bind(TokenValidator.class).to(NotImplementedTokenValidator.class);
|
bind(TokenValidator.class).to(NotImplementedTokenValidator.class);
|
||||||
bind(JwtParser.class).to(DefaultJwtParser.class);
|
|
||||||
bind(ProfileDao.class).to(JpaProfileDao.class);
|
bind(ProfileDao.class).to(JpaProfileDao.class);
|
||||||
bind(OAuthAPI.class).to(EmbeddedOAuthAPI.class);
|
bind(OAuthAPI.class).to(EmbeddedOAuthAPI.class);
|
||||||
bind(RequestTokenExtractor.class).to(HeaderRequestTokenExtractor.class);
|
|
||||||
} else {
|
} else {
|
||||||
install(new KeycloakModule());
|
install(new KeycloakModule());
|
||||||
install(new KeycloakUserRemoverModule());
|
install(new KeycloakUserRemoverModule());
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import org.eclipse.che.inject.ConfigurationException;
|
||||||
import org.eclipse.che.inject.DynaModule;
|
import org.eclipse.che.inject.DynaModule;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.deploy.KeycloakServletModule;
|
import org.eclipse.che.multiuser.keycloak.server.deploy.KeycloakServletModule;
|
||||||
import org.eclipse.che.multiuser.machine.authentication.server.MachineLoginFilter;
|
import org.eclipse.che.multiuser.machine.authentication.server.MachineLoginFilter;
|
||||||
|
import org.eclipse.che.multiuser.oidc.filter.OidcTokenInitializationFilter;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructure;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftInfrastructure;
|
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftInfrastructure;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth.OpenshiftTokenInitializationFilter;
|
import org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth.OpenshiftTokenInitializationFilter;
|
||||||
import org.everrest.guice.servlet.GuiceEverrestServlet;
|
import org.everrest.guice.servlet.GuiceEverrestServlet;
|
||||||
|
|
@ -78,6 +80,8 @@ public class WsMasterServletModule extends ServletModule {
|
||||||
final String infrastructure = System.getenv("CHE_INFRASTRUCTURE_ACTIVE");
|
final String infrastructure = System.getenv("CHE_INFRASTRUCTURE_ACTIVE");
|
||||||
if (OpenShiftInfrastructure.NAME.equals(infrastructure)) {
|
if (OpenShiftInfrastructure.NAME.equals(infrastructure)) {
|
||||||
filter("/*").through(OpenshiftTokenInitializationFilter.class);
|
filter("/*").through(OpenshiftTokenInitializationFilter.class);
|
||||||
|
} else if (KubernetesInfrastructure.NAME.equals(infrastructure)) {
|
||||||
|
filter("/*").through(OidcTokenInitializationFilter.class);
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("Native user mode is currently supported on on OpenShift.");
|
throw new ConfigurationException("Native user mode is currently supported on on OpenShift.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -342,6 +342,9 @@ che.infra.kubernetes.service_account_name=NULL
|
||||||
# This property deprecates `che.infra.kubernetes.cluster_role_name`.
|
# This property deprecates `che.infra.kubernetes.cluster_role_name`.
|
||||||
che.infra.kubernetes.workspace_sa_cluster_roles=NULL
|
che.infra.kubernetes.workspace_sa_cluster_roles=NULL
|
||||||
|
|
||||||
|
# Cluster roles to assign to user in his namespace
|
||||||
|
che.infra.kubernetes.user_cluster_roles=NULL
|
||||||
|
|
||||||
# Defines wait time that limits the Kubernetes workspace start time.
|
# Defines wait time that limits the Kubernetes workspace start time.
|
||||||
che.infra.kubernetes.workspace_start_timeout_min=8
|
che.infra.kubernetes.workspace_start_timeout_min=8
|
||||||
|
|
||||||
|
|
@ -545,12 +548,6 @@ che.infra.kubernetes.trusted_ca.mount_path=/public-certs
|
||||||
# See the `che.infra.kubernetes.trusted_ca.dest_configmap` property.
|
# See the `che.infra.kubernetes.trusted_ca.dest_configmap` property.
|
||||||
che.infra.kubernetes.trusted_ca.dest_configmap_labels=
|
che.infra.kubernetes.trusted_ca.dest_configmap_labels=
|
||||||
|
|
||||||
# Enables the `/unsupported/k8s` endpoint to resolve calls on Kubernetes infrastructure.
|
|
||||||
# Provides direct access to the underlying infrastructure REST API.
|
|
||||||
# This results in huge privilege escalation.
|
|
||||||
# It impacts only Kubernetes infrastructure. Therefore it implies no security risk on OpenShift with OAuth.
|
|
||||||
# Do not enable this, unless you understand the risks.
|
|
||||||
che.infra.kubernetes.enable_unsupported_k8s=false
|
|
||||||
|
|
||||||
### OpenShift Infra parameters
|
### OpenShift Infra parameters
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,16 +93,31 @@ che.limits.organization.workspaces.run.count=-1
|
||||||
# See: link:https://www.keycloak.org/docs/latest/server_admin/#openshift-4[OpenShift identity provider]
|
# See: link:https://www.keycloak.org/docs/latest/server_admin/#openshift-4[OpenShift identity provider]
|
||||||
che.infra.openshift.oauth_identity_provider=NULL
|
che.infra.openshift.oauth_identity_provider=NULL
|
||||||
|
|
||||||
|
### OIDC configuration
|
||||||
|
|
||||||
|
# Url to OIDC identity provider server
|
||||||
|
# Can be set to NULL only if `che.oidc.oidcProvider` is used
|
||||||
|
che.oidc.auth_server_url=http://${CHE_HOST}:5050/auth
|
||||||
|
|
||||||
|
# Internal network service Url to OIDC identity provider server
|
||||||
|
che.oidc.auth_internal_server_url=NULL
|
||||||
|
|
||||||
|
# The number of seconds to tolerate for clock skew when verifying `exp` or `nbf` claims.
|
||||||
|
che.oidc.allowed_clock_skew_sec=3
|
||||||
|
|
||||||
|
# Username claim to be used as user display name when parsing JWT token
|
||||||
|
# if not defined the fallback value is 'preferred_username' in Keycloak installations and
|
||||||
|
# `name` in Dex installations.
|
||||||
|
che.oidc.username_claim=NULL
|
||||||
|
|
||||||
|
# Base URL of an alternate OIDC provider that provides
|
||||||
|
# a discovery endpoint as detailed in the following specification
|
||||||
|
# link:https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Obtaining OpenID Provider Configuration Information]
|
||||||
|
# Deprecated, use `che.oidc.auth_server_url` and `che.oidc.auth_internal_server_url` instead.
|
||||||
|
che.oidc.oidc_provider=NULL
|
||||||
|
|
||||||
### Keycloak configuration
|
### Keycloak configuration
|
||||||
|
|
||||||
# Url to keycloak identity provider server
|
|
||||||
# Can be set to NULL only if `che.keycloak.oidcProvider`
|
|
||||||
# is used
|
|
||||||
che.keycloak.auth_server_url=http://${CHE_HOST}:5050/auth
|
|
||||||
|
|
||||||
# Internal network service Url to keycloak identity provider server
|
|
||||||
che.keycloak.auth_internal_server_url=NULL
|
|
||||||
|
|
||||||
# Keycloak realm is used to authenticate users
|
# Keycloak realm is used to authenticate users
|
||||||
# Can be set to NULL only if `che.keycloak.oidcProvider`
|
# Can be set to NULL only if `che.keycloak.oidcProvider`
|
||||||
# is used
|
# is used
|
||||||
|
|
@ -117,9 +132,6 @@ che.keycloak.oso.endpoint=NULL
|
||||||
# URL to access Github OAuth tokens
|
# URL to access Github OAuth tokens
|
||||||
che.keycloak.github.endpoint=NULL
|
che.keycloak.github.endpoint=NULL
|
||||||
|
|
||||||
# The number of seconds to tolerate for clock skew when verifying `exp` or `nbf` claims.
|
|
||||||
che.keycloak.allowed_clock_skew_sec=3
|
|
||||||
|
|
||||||
# Use the OIDC optional `nonce` feature to increase security.
|
# Use the OIDC optional `nonce` feature to increase security.
|
||||||
che.keycloak.use_nonce=true
|
che.keycloak.use_nonce=true
|
||||||
|
|
||||||
|
|
@ -130,21 +142,11 @@ che.keycloak.use_nonce=true
|
||||||
# if an alternate `oidc_provider` is used
|
# if an alternate `oidc_provider` is used
|
||||||
che.keycloak.js_adapter_url=NULL
|
che.keycloak.js_adapter_url=NULL
|
||||||
|
|
||||||
# Base URL of an alternate OIDC provider that provides
|
|
||||||
# a discovery endpoint as detailed in the following specification
|
|
||||||
# link:https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Obtaining OpenID Provider Configuration Information]
|
|
||||||
che.keycloak.oidc_provider=NULL
|
|
||||||
|
|
||||||
# Set to true when using an alternate OIDC provider that
|
# Set to true when using an alternate OIDC provider that
|
||||||
# only supports fixed redirect Urls
|
# only supports fixed redirect Urls
|
||||||
# This property is ignored when `che.keycloak.oidc_provider` is NULL
|
# This property is ignored when `che.keycloak.oidc_provider` is NULL
|
||||||
che.keycloak.use_fixed_redirect_urls=false
|
che.keycloak.use_fixed_redirect_urls=false
|
||||||
|
|
||||||
# Username claim to be used as user display name
|
|
||||||
# when parsing JWT token
|
|
||||||
# if not defined the fallback value is 'preferred_username'
|
|
||||||
che.keycloak.username_claim=NULL
|
|
||||||
|
|
||||||
# Configuration of OAuth Authentication Service that can be used in "embedded" or "delegated" mode.
|
# Configuration of OAuth Authentication Service that can be used in "embedded" or "delegated" mode.
|
||||||
# If set to "embedded", then the service work as a wrapper to Che's OAuthAuthenticator ( as in Single User mode).
|
# If set to "embedded", then the service work as a wrapper to Che's OAuthAuthenticator ( as in Single User mode).
|
||||||
# If set to "delegated", then the service will use Keycloak IdentityProvider mechanism.
|
# If set to "delegated", then the service will use Keycloak IdentityProvider mechanism.
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,9 @@ che.infra.kubernetes.trusted_ca.dest_configmap=che.infra.openshift.trusted_ca_bu
|
||||||
che.infra.kubernetes.trusted_ca.mount_path=che.infra.openshift.trusted_ca_bundles_mount_path
|
che.infra.kubernetes.trusted_ca.mount_path=che.infra.openshift.trusted_ca_bundles_mount_path
|
||||||
che.infra.openshift.trusted_ca.dest_configmap_labels=che.infra.openshift.trusted_ca_bundles_config_map_labels
|
che.infra.openshift.trusted_ca.dest_configmap_labels=che.infra.openshift.trusted_ca_bundles_config_map_labels
|
||||||
che.integration.bitbucket.server_endpoints=bitbucket.server.endpoints
|
che.integration.bitbucket.server_endpoints=bitbucket.server.endpoints
|
||||||
|
|
||||||
|
che.oidc.auth_server_url=che.keycloak.auth_server_url
|
||||||
|
che.oidc.auth_internal_server_url=che.keycloak.auth_internal_server_url
|
||||||
|
che.oidc.allowed_clock_skew_sec=che.keycloak.allowed_clock_skew_sec
|
||||||
|
che.oidc.username_claim=che.keycloak.username_claim
|
||||||
|
che.oidc.oidc_provider=che.keycloak.oidc_provider
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,11 @@
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>mockwebserver</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.fabric8</groupId>
|
<groupId>io.fabric8</groupId>
|
||||||
<artifactId>kubernetes-server-mock</artifactId>
|
<artifactId>kubernetes-server-mock</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ public class CheServerKubernetesClientFactory extends KubernetesClientFactory {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CheServerKubernetesClientFactory(
|
public CheServerKubernetesClientFactory(
|
||||||
|
KubernetesClientConfigFactory configBuilder,
|
||||||
@Nullable @Named("che.infra.kubernetes.master_url") String masterUrl,
|
@Nullable @Named("che.infra.kubernetes.master_url") String masterUrl,
|
||||||
@Nullable @Named("che.infra.kubernetes.trust_certs") Boolean doTrustCerts,
|
@Nullable @Named("che.infra.kubernetes.trust_certs") Boolean doTrustCerts,
|
||||||
@Named("che.infra.kubernetes.client.http.async_requests.max") int maxConcurrentRequests,
|
@Named("che.infra.kubernetes.client.http.async_requests.max") int maxConcurrentRequests,
|
||||||
|
|
@ -41,6 +42,7 @@ public class CheServerKubernetesClientFactory extends KubernetesClientFactory {
|
||||||
int connectionPoolKeepAlive,
|
int connectionPoolKeepAlive,
|
||||||
EventListener eventListener) {
|
EventListener eventListener) {
|
||||||
super(
|
super(
|
||||||
|
configBuilder,
|
||||||
masterUrl,
|
masterUrl,
|
||||||
doTrustCerts,
|
doTrustCerts,
|
||||||
maxConcurrentRequests,
|
maxConcurrentRequests,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ package org.eclipse.che.workspace.infrastructure.kubernetes;
|
||||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
import static io.fabric8.kubernetes.client.utils.Utils.isNotNullOrEmpty;
|
import static io.fabric8.kubernetes.client.utils.Utils.isNotNullOrEmpty;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.client.BaseKubernetesClient;
|
||||||
import io.fabric8.kubernetes.client.Config;
|
import io.fabric8.kubernetes.client.Config;
|
||||||
import io.fabric8.kubernetes.client.ConfigBuilder;
|
import io.fabric8.kubernetes.client.ConfigBuilder;
|
||||||
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
|
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
|
||||||
|
|
@ -55,10 +56,13 @@ public class KubernetesClientFactory {
|
||||||
* Default Kubernetes {@link Config} that will be the base configuration to create per-workspace
|
* Default Kubernetes {@link Config} that will be the base configuration to create per-workspace
|
||||||
* configurations.
|
* configurations.
|
||||||
*/
|
*/
|
||||||
private Config defaultConfig;
|
private final Config defaultConfig;
|
||||||
|
|
||||||
|
protected final KubernetesClientConfigFactory configBuilder;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public KubernetesClientFactory(
|
public KubernetesClientFactory(
|
||||||
|
KubernetesClientConfigFactory configBuilder,
|
||||||
@Nullable @Named("che.infra.kubernetes.master_url") String masterUrl,
|
@Nullable @Named("che.infra.kubernetes.master_url") String masterUrl,
|
||||||
@Nullable @Named("che.infra.kubernetes.trust_certs") Boolean doTrustCerts,
|
@Nullable @Named("che.infra.kubernetes.trust_certs") Boolean doTrustCerts,
|
||||||
@Named("che.infra.kubernetes.client.http.async_requests.max") int maxConcurrentRequests,
|
@Named("che.infra.kubernetes.client.http.async_requests.max") int maxConcurrentRequests,
|
||||||
|
|
@ -68,6 +72,7 @@ public class KubernetesClientFactory {
|
||||||
@Named("che.infra.kubernetes.client.http.connection_pool.keep_alive_min")
|
@Named("che.infra.kubernetes.client.http.connection_pool.keep_alive_min")
|
||||||
int connectionPoolKeepAlive,
|
int connectionPoolKeepAlive,
|
||||||
EventListener eventListener) {
|
EventListener eventListener) {
|
||||||
|
this.configBuilder = configBuilder;
|
||||||
this.defaultConfig = buildDefaultConfig(masterUrl, doTrustCerts);
|
this.defaultConfig = buildDefaultConfig(masterUrl, doTrustCerts);
|
||||||
OkHttpClient temporary = HttpClientUtils.createHttpClient(defaultConfig);
|
OkHttpClient temporary = HttpClientUtils.createHttpClient(defaultConfig);
|
||||||
OkHttpClient.Builder builder = temporary.newBuilder();
|
OkHttpClient.Builder builder = temporary.newBuilder();
|
||||||
|
|
@ -166,7 +171,12 @@ public class KubernetesClientFactory {
|
||||||
* infromation
|
* infromation
|
||||||
*/
|
*/
|
||||||
public OkHttpClient getAuthenticatedHttpClient() throws InfrastructureException {
|
public OkHttpClient getAuthenticatedHttpClient() throws InfrastructureException {
|
||||||
return create(getDefaultConfig()).getHttpClient();
|
if (!configBuilder.isPersonalized()) {
|
||||||
|
throw new InfrastructureException(
|
||||||
|
"Not able to construct impersonating Kubernetes API client.");
|
||||||
|
}
|
||||||
|
// Ensure to get OkHttpClient with all necessary interceptors.
|
||||||
|
return create(buildConfig(getDefaultConfig(), null)).getHttpClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,7 +210,7 @@ public class KubernetesClientFactory {
|
||||||
*/
|
*/
|
||||||
protected Config buildConfig(Config config, @Nullable String workspaceId)
|
protected Config buildConfig(Config config, @Nullable String workspaceId)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
return config;
|
return configBuilder.buildConfig(config, workspaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Interceptor buildKubernetesInterceptor(Config config) {
|
protected Interceptor buildKubernetesInterceptor(Config config) {
|
||||||
|
|
@ -234,7 +244,7 @@ public class KubernetesClientFactory {
|
||||||
* authenticate with the credentials (user/password or Oauth token) contained in the {@code
|
* authenticate with the credentials (user/password or Oauth token) contained in the {@code
|
||||||
* config} parameter.
|
* config} parameter.
|
||||||
*/
|
*/
|
||||||
private DefaultKubernetesClient create(Config config) {
|
protected BaseKubernetesClient<?> create(Config config) {
|
||||||
OkHttpClient clientHttpClient =
|
OkHttpClient clientHttpClient =
|
||||||
httpClient.newBuilder().authenticator(Authenticator.NONE).build();
|
httpClient.newBuilder().authenticator(Authenticator.NONE).build();
|
||||||
OkHttpClient.Builder builder = clientHttpClient.newBuilder();
|
OkHttpClient.Builder builder = clientHttpClient.newBuilder();
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,13 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesDev
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.RemoveNamespaceOnWorkspaceRemove;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.RemoveNamespaceOnWorkspaceRemove;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.CredentialsSecretConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
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.UserPermissionConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserPreferencesConfigurator;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserPreferencesConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserProfileConfigurator;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserProfileConfigurator;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.WorkspaceServiceAccountConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.PerWorkspacePVCStrategy;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.PerWorkspacePVCStrategy;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.UniqueWorkspacePVCStrategy;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.UniqueWorkspacePVCStrategy;
|
||||||
|
|
@ -101,8 +105,15 @@ public class KubernetesInfraModule extends AbstractModule {
|
||||||
workspaceAttributeValidators.addBinding().to(K8sInfraNamespaceWsAttributeValidator.class);
|
workspaceAttributeValidators.addBinding().to(K8sInfraNamespaceWsAttributeValidator.class);
|
||||||
workspaceAttributeValidators.addBinding().to(AsyncStorageModeValidator.class);
|
workspaceAttributeValidators.addBinding().to(AsyncStorageModeValidator.class);
|
||||||
|
|
||||||
|
// order matters here!
|
||||||
|
// We first need to grant permissions to user, only then we can run other configurators with
|
||||||
|
// user's client.
|
||||||
Multibinder<NamespaceConfigurator> namespaceConfigurators =
|
Multibinder<NamespaceConfigurator> namespaceConfigurators =
|
||||||
Multibinder.newSetBinder(binder(), NamespaceConfigurator.class);
|
Multibinder.newSetBinder(binder(), NamespaceConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(UserPermissionConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(CredentialsSecretConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(PreferencesConfigMapConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(WorkspaceServiceAccountConfigurator.class);
|
||||||
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
|
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
|
||||||
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
|
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import java.net.URI;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import org.eclipse.che.api.core.ValidationException;
|
|
||||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
|
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
|
||||||
|
|
@ -47,8 +46,7 @@ public class KubernetesRuntimeContext<T extends KubernetesEnvironment> extends R
|
||||||
KubernetesRuntimeStateCache runtimeStatuses,
|
KubernetesRuntimeStateCache runtimeStatuses,
|
||||||
@Assisted T kubernetesEnvironment,
|
@Assisted T kubernetesEnvironment,
|
||||||
@Assisted RuntimeIdentity identity,
|
@Assisted RuntimeIdentity identity,
|
||||||
@Assisted RuntimeInfrastructure infrastructure)
|
@Assisted RuntimeInfrastructure infrastructure) {
|
||||||
throws ValidationException, InfrastructureException {
|
|
||||||
super(kubernetesEnvironment, identity, infrastructure);
|
super(kubernetesEnvironment, identity, infrastructure);
|
||||||
this.namespaceFactory = namespaceFactory;
|
this.namespaceFactory = namespaceFactory;
|
||||||
this.runtimeFactory = runtimeFactory;
|
this.runtimeFactory = runtimeFactory;
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ public class KubernetesNamespace {
|
||||||
*/
|
*/
|
||||||
void prepare(boolean canCreate, Map<String, String> labels, Map<String, String> annotations)
|
void prepare(boolean canCreate, Map<String, String> labels, Map<String, String> annotations)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
KubernetesClient client = clientFactory.create(workspaceId);
|
KubernetesClient client = cheSAClientFactory.create(workspaceId);
|
||||||
Namespace namespace = get(name, client);
|
Namespace namespace = get(name, client);
|
||||||
|
|
||||||
if (namespace == null) {
|
if (namespace == null) {
|
||||||
|
|
|
||||||
|
|
@ -20,24 +20,17 @@ import static java.util.Collections.singletonList;
|
||||||
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_INFRASTRUCTURE_NAMESPACE_ATTRIBUTE;
|
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_INFRASTRUCTURE_NAMESPACE_ATTRIBUTE;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.DEFAULT_ATTRIBUTE;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.DEFAULT_ATTRIBUTE;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
|
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.NamespaceNameValidator.METADATA_NAME_MAX_LENGTH;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.NamespaceNameValidator.METADATA_NAME_MAX_LENGTH;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
|
||||||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
|
||||||
import io.fabric8.kubernetes.api.model.HasMetadata;
|
import io.fabric8.kubernetes.api.model.HasMetadata;
|
||||||
import io.fabric8.kubernetes.api.model.Namespace;
|
import io.fabric8.kubernetes.api.model.Namespace;
|
||||||
import io.fabric8.kubernetes.api.model.Secret;
|
|
||||||
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
|
||||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -68,6 +61,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesCl
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
|
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.api.shared.KubernetesNamespaceMeta;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
@ -103,25 +97,23 @@ public class KubernetesNamespaceFactory {
|
||||||
protected final Map<String, String> namespaceLabels;
|
protected final Map<String, String> namespaceLabels;
|
||||||
protected final Map<String, String> namespaceAnnotations;
|
protected final Map<String, String> namespaceAnnotations;
|
||||||
|
|
||||||
private final String serviceAccountName;
|
|
||||||
private final Set<String> clusterRoleNames;
|
|
||||||
private final KubernetesClientFactory clientFactory;
|
private final KubernetesClientFactory clientFactory;
|
||||||
private final KubernetesClientFactory cheClientFactory;
|
private final KubernetesClientFactory cheClientFactory;
|
||||||
private final boolean namespaceCreationAllowed;
|
private final boolean namespaceCreationAllowed;
|
||||||
private final UserManager userManager;
|
private final UserManager userManager;
|
||||||
private final PreferenceManager preferenceManager;
|
private final PreferenceManager preferenceManager;
|
||||||
|
protected final Set<NamespaceConfigurator> namespaceConfigurators;
|
||||||
protected final KubernetesSharedPool sharedPool;
|
protected final KubernetesSharedPool sharedPool;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public KubernetesNamespaceFactory(
|
public KubernetesNamespaceFactory(
|
||||||
@Nullable @Named("che.infra.kubernetes.service_account_name") String serviceAccountName,
|
|
||||||
@Nullable @Named("che.infra.kubernetes.workspace_sa_cluster_roles") String clusterRoleNames,
|
|
||||||
@Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName,
|
@Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName,
|
||||||
@Named("che.infra.kubernetes.namespace.creation_allowed") boolean namespaceCreationAllowed,
|
@Named("che.infra.kubernetes.namespace.creation_allowed") boolean namespaceCreationAllowed,
|
||||||
@Named("che.infra.kubernetes.namespace.label") boolean labelNamespaces,
|
@Named("che.infra.kubernetes.namespace.label") boolean labelNamespaces,
|
||||||
@Named("che.infra.kubernetes.namespace.annotate") boolean annotateNamespaces,
|
@Named("che.infra.kubernetes.namespace.annotate") boolean annotateNamespaces,
|
||||||
@Named("che.infra.kubernetes.namespace.labels") String namespaceLabels,
|
@Named("che.infra.kubernetes.namespace.labels") String namespaceLabels,
|
||||||
@Named("che.infra.kubernetes.namespace.annotations") String namespaceAnnotations,
|
@Named("che.infra.kubernetes.namespace.annotations") String namespaceAnnotations,
|
||||||
|
Set<NamespaceConfigurator> namespaceConfigurators,
|
||||||
KubernetesClientFactory clientFactory,
|
KubernetesClientFactory clientFactory,
|
||||||
CheServerKubernetesClientFactory cheClientFactory,
|
CheServerKubernetesClientFactory cheClientFactory,
|
||||||
UserManager userManager,
|
UserManager userManager,
|
||||||
|
|
@ -130,7 +122,6 @@ public class KubernetesNamespaceFactory {
|
||||||
throws ConfigurationException {
|
throws ConfigurationException {
|
||||||
this.namespaceCreationAllowed = namespaceCreationAllowed;
|
this.namespaceCreationAllowed = namespaceCreationAllowed;
|
||||||
this.userManager = userManager;
|
this.userManager = userManager;
|
||||||
this.serviceAccountName = serviceAccountName;
|
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.cheClientFactory = cheClientFactory;
|
this.cheClientFactory = cheClientFactory;
|
||||||
this.defaultNamespaceName = defaultNamespaceName;
|
this.defaultNamespaceName = defaultNamespaceName;
|
||||||
|
|
@ -138,6 +129,7 @@ public class KubernetesNamespaceFactory {
|
||||||
this.sharedPool = sharedPool;
|
this.sharedPool = sharedPool;
|
||||||
this.labelNamespaces = labelNamespaces;
|
this.labelNamespaces = labelNamespaces;
|
||||||
this.annotateNamespaces = annotateNamespaces;
|
this.annotateNamespaces = annotateNamespaces;
|
||||||
|
this.namespaceConfigurators = ImmutableSet.copyOf(namespaceConfigurators);
|
||||||
|
|
||||||
//noinspection UnstableApiUsage
|
//noinspection UnstableApiUsage
|
||||||
Splitter.MapSplitter csvMapSplitter = Splitter.on(",").withKeyValueSeparator("=");
|
Splitter.MapSplitter csvMapSplitter = Splitter.on(",").withKeyValueSeparator("=");
|
||||||
|
|
@ -162,14 +154,6 @@ public class KubernetesNamespaceFactory {
|
||||||
+ " The current value is: `%s`.",
|
+ " The current value is: `%s`.",
|
||||||
Joiner.on(" or ").join(REQUIRED_NAMESPACE_NAME_PLACEHOLDERS), defaultNamespaceName));
|
Joiner.on(" or ").join(REQUIRED_NAMESPACE_NAME_PLACEHOLDERS), defaultNamespaceName));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isNullOrEmpty(clusterRoleNames)) {
|
|
||||||
this.clusterRoleNames =
|
|
||||||
Sets.newHashSet(
|
|
||||||
Splitter.on(",").trimResults().omitEmptyStrings().split(clusterRoleNames));
|
|
||||||
} else {
|
|
||||||
this.clusterRoleNames = Collections.emptySet();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -260,7 +244,7 @@ public class KubernetesNamespaceFactory {
|
||||||
public Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
public Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
try {
|
try {
|
||||||
Namespace namespace = clientFactory.create().namespaces().withName(name).get();
|
Namespace namespace = cheClientFactory.create().namespaces().withName(name).get();
|
||||||
if (namespace == null) {
|
if (namespace == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -336,8 +320,10 @@ public class KubernetesNamespaceFactory {
|
||||||
public KubernetesNamespace getOrCreate(RuntimeIdentity identity) throws InfrastructureException {
|
public KubernetesNamespace getOrCreate(RuntimeIdentity identity) throws InfrastructureException {
|
||||||
KubernetesNamespace namespace = get(identity);
|
KubernetesNamespace namespace = get(identity);
|
||||||
|
|
||||||
|
var subject = EnvironmentContext.getCurrent().getSubject();
|
||||||
NamespaceResolutionContext resolutionCtx =
|
NamespaceResolutionContext resolutionCtx =
|
||||||
new NamespaceResolutionContext(EnvironmentContext.getCurrent().getSubject());
|
new NamespaceResolutionContext(
|
||||||
|
identity.getWorkspaceId(), subject.getUserId(), subject.getUserName());
|
||||||
Map<String, String> namespaceAnnotationsEvaluated =
|
Map<String, String> namespaceAnnotationsEvaluated =
|
||||||
evaluateAnnotationPlaceholders(resolutionCtx);
|
evaluateAnnotationPlaceholders(resolutionCtx);
|
||||||
|
|
||||||
|
|
@ -346,44 +332,7 @@ public class KubernetesNamespaceFactory {
|
||||||
labelNamespaces ? namespaceLabels : emptyMap(),
|
labelNamespaces ? namespaceLabels : emptyMap(),
|
||||||
annotateNamespaces ? namespaceAnnotationsEvaluated : emptyMap());
|
annotateNamespaces ? namespaceAnnotationsEvaluated : emptyMap());
|
||||||
|
|
||||||
if (namespace
|
configureNamespace(resolutionCtx, namespace.getName());
|
||||||
.secrets()
|
|
||||||
.get()
|
|
||||||
.stream()
|
|
||||||
.noneMatch(s -> s.getMetadata().getName().equals(CREDENTIALS_SECRET_NAME))) {
|
|
||||||
Secret secret =
|
|
||||||
new SecretBuilder()
|
|
||||||
.withType("opaque")
|
|
||||||
.withNewMetadata()
|
|
||||||
.withName(CREDENTIALS_SECRET_NAME)
|
|
||||||
.endMetadata()
|
|
||||||
.build();
|
|
||||||
clientFactory
|
|
||||||
.create()
|
|
||||||
.secrets()
|
|
||||||
.inNamespace(identity.getInfrastructureNamespace())
|
|
||||||
.create(secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (namespace.configMaps().get(PREFERENCES_CONFIGMAP_NAME).isEmpty()) {
|
|
||||||
ConfigMap configMap =
|
|
||||||
new ConfigMapBuilder()
|
|
||||||
.withNewMetadata()
|
|
||||||
.withName(PREFERENCES_CONFIGMAP_NAME)
|
|
||||||
.endMetadata()
|
|
||||||
.build();
|
|
||||||
clientFactory
|
|
||||||
.create()
|
|
||||||
.configMaps()
|
|
||||||
.inNamespace(identity.getInfrastructureNamespace())
|
|
||||||
.create(configMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNullOrEmpty(serviceAccountName)) {
|
|
||||||
KubernetesWorkspaceServiceAccount workspaceServiceAccount =
|
|
||||||
doCreateServiceAccount(namespace.getWorkspaceId(), namespace.getName());
|
|
||||||
workspaceServiceAccount.prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
return namespace;
|
return namespace;
|
||||||
}
|
}
|
||||||
|
|
@ -590,7 +539,7 @@ public class KubernetesNamespaceFactory {
|
||||||
NamespaceResolutionContext namespaceCtx) throws InfrastructureException {
|
NamespaceResolutionContext namespaceCtx) throws InfrastructureException {
|
||||||
try {
|
try {
|
||||||
List<Namespace> workspaceNamespaces =
|
List<Namespace> workspaceNamespaces =
|
||||||
clientFactory.create().namespaces().withLabels(namespaceLabels).list().getItems();
|
cheClientFactory.create().namespaces().withLabels(namespaceLabels).list().getItems();
|
||||||
if (!workspaceNamespaces.isEmpty()) {
|
if (!workspaceNamespaces.isEmpty()) {
|
||||||
Map<String, String> evaluatedAnnotations = evaluateAnnotationPlaceholders(namespaceCtx);
|
Map<String, String> evaluatedAnnotations = evaluateAnnotationPlaceholders(namespaceCtx);
|
||||||
return workspaceNamespaces
|
return workspaceNamespaces
|
||||||
|
|
@ -617,6 +566,14 @@ public class KubernetesNamespaceFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void configureNamespace(
|
||||||
|
NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
|
throws InfrastructureException {
|
||||||
|
for (NamespaceConfigurator configurator : namespaceConfigurators) {
|
||||||
|
configurator.configure(namespaceResolutionContext, namespaceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate placeholder in `che.infra.kubernetes.namespace.annotations` property with given {@link
|
* Evaluate placeholder in `che.infra.kubernetes.namespace.annotations` property with given {@link
|
||||||
* NamespaceResolutionContext}.
|
* NamespaceResolutionContext}.
|
||||||
|
|
@ -657,31 +614,6 @@ public class KubernetesNamespaceFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean checkNamespaceExists(String namespaceName) throws InfrastructureException {
|
|
||||||
try {
|
|
||||||
return clientFactory.create().namespaces().withName(namespaceName).get() != null;
|
|
||||||
} catch (KubernetesClientException e) {
|
|
||||||
if (e.getCode() == 403) {
|
|
||||||
// 403 means that the project does not exist
|
|
||||||
// or a user really is not permitted to access it which is Che Server misconfiguration
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
throw new InfrastructureException(
|
|
||||||
format(
|
|
||||||
"Error occurred while trying to fetch the namespace '%s'. Cause: %s",
|
|
||||||
namespaceName, e.getMessage()),
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String evalPlaceholders(String namespace, Subject currentUser, String workspaceId) {
|
|
||||||
return evalPlaceholders(
|
|
||||||
namespace,
|
|
||||||
new NamespaceResolutionContext(
|
|
||||||
workspaceId, currentUser.getUserId(), currentUser.getUserName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String evalPlaceholders(String namespace, NamespaceResolutionContext ctx) {
|
protected String evalPlaceholders(String namespace, NamespaceResolutionContext ctx) {
|
||||||
checkArgument(!isNullOrEmpty(namespace));
|
checkArgument(!isNullOrEmpty(namespace));
|
||||||
String evaluated = namespace;
|
String evaluated = namespace;
|
||||||
|
|
@ -710,7 +642,7 @@ public class KubernetesNamespaceFactory {
|
||||||
preferences.put(NAMESPACE_TEMPLATE_ATTRIBUTE, defaultNamespaceName);
|
preferences.put(NAMESPACE_TEMPLATE_ATTRIBUTE, defaultNamespaceName);
|
||||||
preferenceManager.update(owner, preferences);
|
preferenceManager.update(owner, preferences);
|
||||||
} catch (ServerException e) {
|
} catch (ServerException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error("Failed storing namespace name in user properties.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -743,6 +675,7 @@ public class KubernetesNamespaceFactory {
|
||||||
String normalizeNamespaceName(String namespaceName) {
|
String normalizeNamespaceName(String namespaceName) {
|
||||||
namespaceName =
|
namespaceName =
|
||||||
namespaceName
|
namespaceName
|
||||||
|
.toLowerCase()
|
||||||
.replaceAll("[^-a-zA-Z0-9]", "-") // replace invalid chars with '-'
|
.replaceAll("[^-a-zA-Z0-9]", "-") // replace invalid chars with '-'
|
||||||
.replaceAll("-+", "-") // replace multiple '-' with single ones
|
.replaceAll("-+", "-") // replace multiple '-' with single ones
|
||||||
.replaceAll("^-|-$", ""); // trim dashes at beginning/end of the string
|
.replaceAll("^-|-$", ""); // trim dashes at beginning/end of the string
|
||||||
|
|
@ -755,19 +688,4 @@ public class KubernetesNamespaceFactory {
|
||||||
namespaceName.length(),
|
namespaceName.length(),
|
||||||
METADATA_NAME_MAX_LENGTH)); // limit length to METADATA_NAME_MAX_LENGTH
|
METADATA_NAME_MAX_LENGTH)); // limit length to METADATA_NAME_MAX_LENGTH
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
KubernetesWorkspaceServiceAccount doCreateServiceAccount(
|
|
||||||
String workspaceId, String namespaceName) {
|
|
||||||
return new KubernetesWorkspaceServiceAccount(
|
|
||||||
workspaceId, namespaceName, serviceAccountName, getClusterRoleNames(), clientFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getServiceAccountName() {
|
|
||||||
return serviceAccountName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Set<String> getClusterRoleNames() {
|
|
||||||
return clusterRoleNames;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.Secret;
|
||||||
|
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link NamespaceConfigurator} ensures that Secret {@link
|
||||||
|
* org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount#CREDENTIALS_SECRET_NAME}
|
||||||
|
* is present in the Workspace namespace.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class CredentialsSecretConfigurator implements NamespaceConfigurator {
|
||||||
|
|
||||||
|
private final KubernetesClientFactory clientFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CredentialsSecretConfigurator(KubernetesClientFactory clientFactory) {
|
||||||
|
this.clientFactory = clientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
|
throws InfrastructureException {
|
||||||
|
var client = clientFactory.create();
|
||||||
|
if (client.secrets().inNamespace(namespaceName).withName(CREDENTIALS_SECRET_NAME).get()
|
||||||
|
== null) {
|
||||||
|
Secret secret =
|
||||||
|
new SecretBuilder()
|
||||||
|
.withType("opaque")
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName(CREDENTIALS_SECRET_NAME)
|
||||||
|
.endMetadata()
|
||||||
|
.build();
|
||||||
|
client.secrets().inNamespace(namespaceName).create(secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,6 @@ public interface NamespaceConfigurator {
|
||||||
* @param namespaceResolutionContext users namespace context
|
* @param namespaceResolutionContext users namespace context
|
||||||
* @throws InfrastructureException when any error occurs
|
* @throws InfrastructureException when any error occurs
|
||||||
*/
|
*/
|
||||||
public void configure(NamespaceResolutionContext namespaceResolutionContext)
|
void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
throws InfrastructureException;
|
throws InfrastructureException;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||||
|
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link NamespaceConfigurator} ensures that ConfigMap {@link
|
||||||
|
* org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount#PREFERENCES_CONFIGMAP_NAME}
|
||||||
|
* is present in the Workspace namespace.
|
||||||
|
*/
|
||||||
|
public class PreferencesConfigMapConfigurator implements NamespaceConfigurator {
|
||||||
|
|
||||||
|
private final KubernetesClientFactory clientFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public PreferencesConfigMapConfigurator(KubernetesClientFactory clientFactory) {
|
||||||
|
this.clientFactory = clientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
|
throws InfrastructureException {
|
||||||
|
var client = clientFactory.create();
|
||||||
|
if (client.configMaps().inNamespace(namespaceName).withName(PREFERENCES_CONFIGMAP_NAME).get()
|
||||||
|
== null) {
|
||||||
|
ConfigMap configMap =
|
||||||
|
new ConfigMapBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName(PREFERENCES_CONFIGMAP_NAME)
|
||||||
|
.endMetadata()
|
||||||
|
.build();
|
||||||
|
client.configMaps().inNamespace(namespaceName).create(configMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
|
||||||
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
import java.util.Collections;
|
||||||
|
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.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link NamespaceConfigurator} ensures that User has assigned configured ClusterRoles from
|
||||||
|
* `che.infra.kubernetes.user_cluster_roles` property. These are assigned with RoleBindings in
|
||||||
|
* user's namespace.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class UserPermissionConfigurator implements NamespaceConfigurator {
|
||||||
|
|
||||||
|
private final Set<String> userClusterRoles;
|
||||||
|
private final KubernetesClientFactory clientFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public UserPermissionConfigurator(
|
||||||
|
@Nullable @Named("che.infra.kubernetes.user_cluster_roles") String userClusterRoles,
|
||||||
|
CheServerKubernetesClientFactory cheClientFactory) {
|
||||||
|
this.clientFactory = cheClientFactory;
|
||||||
|
if (!isNullOrEmpty(userClusterRoles)) {
|
||||||
|
this.userClusterRoles =
|
||||||
|
Sets.newHashSet(
|
||||||
|
Splitter.on(",").trimResults().omitEmptyStrings().split(userClusterRoles));
|
||||||
|
} else {
|
||||||
|
this.userClusterRoles = Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
|
throws InfrastructureException {
|
||||||
|
if (!userClusterRoles.isEmpty()) {
|
||||||
|
bindRoles(
|
||||||
|
clientFactory.create(),
|
||||||
|
namespaceName,
|
||||||
|
namespaceResolutionContext.getUserName(),
|
||||||
|
userClusterRoles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindRoles(
|
||||||
|
KubernetesClient client, String namespaceName, String username, Set<String> clusterRoles) {
|
||||||
|
for (String clusterRole : clusterRoles) {
|
||||||
|
client
|
||||||
|
.rbac()
|
||||||
|
.roleBindings()
|
||||||
|
.inNamespace(namespaceName)
|
||||||
|
.createOrReplace(
|
||||||
|
new RoleBindingBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName(clusterRole)
|
||||||
|
.endMetadata()
|
||||||
|
.addToSubjects(
|
||||||
|
new io.fabric8.kubernetes.api.model.rbac.Subject(
|
||||||
|
"rbac.authorization.k8s.io", "User", username, namespaceName))
|
||||||
|
.withNewRoleRef()
|
||||||
|
.withApiGroup("rbac.authorization.k8s.io")
|
||||||
|
.withKind("ClusterRole")
|
||||||
|
.withName(clusterRole)
|
||||||
|
.endRoleRef()
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import org.eclipse.che.api.core.NotFoundException;
|
import org.eclipse.che.api.core.NotFoundException;
|
||||||
import org.eclipse.che.api.core.ServerException;
|
import org.eclipse.che.api.core.ServerException;
|
||||||
import org.eclipse.che.api.core.model.user.User;
|
import org.eclipse.che.api.core.model.user.User;
|
||||||
|
|
@ -31,7 +32,6 @@ import org.eclipse.che.api.user.server.UserManager;
|
||||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates {@link Secret} with user preferences. This serves as a way for DevWorkspaces to acquire
|
* Creates {@link Secret} with user preferences. This serves as a way for DevWorkspaces to acquire
|
||||||
|
|
@ -39,39 +39,36 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesN
|
||||||
*
|
*
|
||||||
* @author Pavol Baran
|
* @author Pavol Baran
|
||||||
*/
|
*/
|
||||||
|
@Singleton
|
||||||
public class UserPreferencesConfigurator implements NamespaceConfigurator {
|
public class UserPreferencesConfigurator implements NamespaceConfigurator {
|
||||||
private static final String USER_PREFERENCES_SECRET_NAME = "user-preferences";
|
private static final String USER_PREFERENCES_SECRET_NAME = "user-preferences";
|
||||||
private static final String USER_PREFERENCES_SECRET_MOUNT_PATH = "/config/user/preferences";
|
private static final String USER_PREFERENCES_SECRET_MOUNT_PATH = "/config/user/preferences";
|
||||||
private static final int PREFERENCE_NAME_MAX_LENGTH = 253;
|
private static final int PREFERENCE_NAME_MAX_LENGTH = 253;
|
||||||
|
|
||||||
private final KubernetesNamespaceFactory namespaceFactory;
|
|
||||||
private final KubernetesClientFactory clientFactory;
|
private final KubernetesClientFactory clientFactory;
|
||||||
private final UserManager userManager;
|
private final UserManager userManager;
|
||||||
private final PreferenceManager preferenceManager;
|
private final PreferenceManager preferenceManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UserPreferencesConfigurator(
|
public UserPreferencesConfigurator(
|
||||||
KubernetesNamespaceFactory namespaceFactory,
|
|
||||||
KubernetesClientFactory clientFactory,
|
KubernetesClientFactory clientFactory,
|
||||||
UserManager userManager,
|
UserManager userManager,
|
||||||
PreferenceManager preferenceManager) {
|
PreferenceManager preferenceManager) {
|
||||||
this.namespaceFactory = namespaceFactory;
|
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.userManager = userManager;
|
this.userManager = userManager;
|
||||||
this.preferenceManager = preferenceManager;
|
this.preferenceManager = preferenceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(NamespaceResolutionContext namespaceResolutionContext)
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
Secret userPreferencesSecret = preparePreferencesSecret(namespaceResolutionContext);
|
Secret userPreferencesSecret = preparePreferencesSecret(namespaceResolutionContext);
|
||||||
String namespace = namespaceFactory.evaluateNamespaceName(namespaceResolutionContext);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clientFactory
|
clientFactory
|
||||||
.create()
|
.create()
|
||||||
.secrets()
|
.secrets()
|
||||||
.inNamespace(namespace)
|
.inNamespace(namespaceName)
|
||||||
.createOrReplace(userPreferencesSecret);
|
.createOrReplace(userPreferencesSecret);
|
||||||
} catch (KubernetesClientException e) {
|
} catch (KubernetesClientException e) {
|
||||||
throw new InfrastructureException(
|
throw new InfrastructureException(
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import org.eclipse.che.api.core.NotFoundException;
|
import org.eclipse.che.api.core.NotFoundException;
|
||||||
import org.eclipse.che.api.core.ServerException;
|
import org.eclipse.che.api.core.ServerException;
|
||||||
import org.eclipse.che.api.core.model.user.User;
|
import org.eclipse.che.api.core.model.user.User;
|
||||||
|
|
@ -29,7 +30,6 @@ import org.eclipse.che.api.user.server.UserManager;
|
||||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates {@link Secret} with user profile information such as his id, name and email. This serves
|
* Creates {@link Secret} with user profile information such as his id, name and email. This serves
|
||||||
|
|
@ -37,31 +37,30 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesN
|
||||||
*
|
*
|
||||||
* @author Pavol Baran
|
* @author Pavol Baran
|
||||||
*/
|
*/
|
||||||
|
@Singleton
|
||||||
public class UserProfileConfigurator implements NamespaceConfigurator {
|
public class UserProfileConfigurator implements NamespaceConfigurator {
|
||||||
private static final String USER_PROFILE_SECRET_NAME = "user-profile";
|
private static final String USER_PROFILE_SECRET_NAME = "user-profile";
|
||||||
private static final String USER_PROFILE_SECRET_MOUNT_PATH = "/config/user/profile";
|
private static final String USER_PROFILE_SECRET_MOUNT_PATH = "/config/user/profile";
|
||||||
|
|
||||||
private final KubernetesNamespaceFactory namespaceFactory;
|
|
||||||
private final KubernetesClientFactory clientFactory;
|
private final KubernetesClientFactory clientFactory;
|
||||||
private final UserManager userManager;
|
private final UserManager userManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UserProfileConfigurator(
|
public UserProfileConfigurator(KubernetesClientFactory clientFactory, UserManager userManager) {
|
||||||
KubernetesNamespaceFactory namespaceFactory,
|
|
||||||
KubernetesClientFactory clientFactory,
|
|
||||||
UserManager userManager) {
|
|
||||||
this.namespaceFactory = namespaceFactory;
|
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.userManager = userManager;
|
this.userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(NamespaceResolutionContext namespaceResolutionContext)
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
Secret userProfileSecret = prepareProfileSecret(namespaceResolutionContext);
|
Secret userProfileSecret = prepareProfileSecret(namespaceResolutionContext);
|
||||||
String namespace = namespaceFactory.evaluateNamespaceName(namespaceResolutionContext);
|
|
||||||
try {
|
try {
|
||||||
clientFactory.create().secrets().inNamespace(namespace).createOrReplace(userProfileSecret);
|
clientFactory
|
||||||
|
.create()
|
||||||
|
.secrets()
|
||||||
|
.inNamespace(namespaceName)
|
||||||
|
.createOrReplace(userProfileSecret);
|
||||||
} catch (KubernetesClientException e) {
|
} catch (KubernetesClientException e) {
|
||||||
throw new InfrastructureException(
|
throw new InfrastructureException(
|
||||||
"Error occurred while trying to create user profile secret.", e);
|
"Error occurred while trying to create user profile secret.", e);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import java.util.Collections;
|
||||||
|
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.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesWorkspaceServiceAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link NamespaceConfigurator} ensures that workspace ServiceAccount with proper ClusterRole
|
||||||
|
* is set in Workspace namespace.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class WorkspaceServiceAccountConfigurator implements NamespaceConfigurator {
|
||||||
|
|
||||||
|
private final KubernetesClientFactory clientFactory;
|
||||||
|
|
||||||
|
private final String serviceAccountName;
|
||||||
|
private final Set<String> clusterRoleNames;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public WorkspaceServiceAccountConfigurator(
|
||||||
|
@Nullable @Named("che.infra.kubernetes.service_account_name") String serviceAccountName,
|
||||||
|
@Nullable @Named("che.infra.kubernetes.workspace_sa_cluster_roles") String clusterRoleNames,
|
||||||
|
KubernetesClientFactory clientFactory) {
|
||||||
|
this.clientFactory = clientFactory;
|
||||||
|
this.serviceAccountName = serviceAccountName;
|
||||||
|
if (!isNullOrEmpty(clusterRoleNames)) {
|
||||||
|
this.clusterRoleNames =
|
||||||
|
Sets.newHashSet(
|
||||||
|
Splitter.on(",").trimResults().omitEmptyStrings().split(clusterRoleNames));
|
||||||
|
} else {
|
||||||
|
this.clusterRoleNames = Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
|
throws InfrastructureException {
|
||||||
|
if (!isNullOrEmpty(serviceAccountName)) {
|
||||||
|
KubernetesWorkspaceServiceAccount workspaceServiceAccount =
|
||||||
|
doCreateServiceAccount(namespaceResolutionContext.getWorkspaceId(), namespaceName);
|
||||||
|
workspaceServiceAccount.prepare();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public KubernetesWorkspaceServiceAccount doCreateServiceAccount(
|
||||||
|
String workspaceId, String namespaceName) {
|
||||||
|
return new KubernetesWorkspaceServiceAccount(
|
||||||
|
workspaceId, namespaceName, serviceAccountName, clusterRoleNames, clientFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
|
package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
|
||||||
|
|
||||||
import io.fabric8.kubernetes.api.model.Namespace;
|
import io.fabric8.kubernetes.api.model.Namespace;
|
||||||
import java.util.Set;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
|
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
|
||||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
|
@ -30,17 +29,13 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurato
|
||||||
*/
|
*/
|
||||||
public class NamespaceProvisioner {
|
public class NamespaceProvisioner {
|
||||||
private final KubernetesNamespaceFactory namespaceFactory;
|
private final KubernetesNamespaceFactory namespaceFactory;
|
||||||
private final Set<NamespaceConfigurator> namespaceConfigurators;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NamespaceProvisioner(
|
public NamespaceProvisioner(KubernetesNamespaceFactory namespaceFactory) {
|
||||||
KubernetesNamespaceFactory namespaceFactory,
|
|
||||||
Set<NamespaceConfigurator> namespaceConfigurators) {
|
|
||||||
this.namespaceFactory = namespaceFactory;
|
this.namespaceFactory = namespaceFactory;
|
||||||
this.namespaceConfigurators = namespaceConfigurators;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tests for this method are in KubernetesFactoryTest. */
|
/** Tests for this method are in KubernetesNamespaceFactoryTest. */
|
||||||
public KubernetesNamespaceMeta provision(NamespaceResolutionContext namespaceResolutionContext)
|
public KubernetesNamespaceMeta provision(NamespaceResolutionContext namespaceResolutionContext)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
KubernetesNamespace namespace =
|
KubernetesNamespace namespace =
|
||||||
|
|
@ -51,21 +46,9 @@ public class NamespaceProvisioner {
|
||||||
namespaceResolutionContext.getUserId(),
|
namespaceResolutionContext.getUserId(),
|
||||||
namespaceFactory.evaluateNamespaceName(namespaceResolutionContext)));
|
namespaceFactory.evaluateNamespaceName(namespaceResolutionContext)));
|
||||||
|
|
||||||
KubernetesNamespaceMeta namespaceMeta =
|
return namespaceFactory
|
||||||
namespaceFactory
|
.fetchNamespace(namespace.getName())
|
||||||
.fetchNamespace(namespace.getName())
|
.orElseThrow(
|
||||||
.orElseThrow(
|
() -> new InfrastructureException("Not able to find namespace " + namespace.getName()));
|
||||||
() ->
|
|
||||||
new InfrastructureException(
|
|
||||||
"Not able to find namespace " + namespace.getName()));
|
|
||||||
configureNamespace(namespaceResolutionContext);
|
|
||||||
return namespaceMeta;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureNamespace(NamespaceResolutionContext namespaceResolutionContext)
|
|
||||||
throws InfrastructureException {
|
|
||||||
for (NamespaceConfigurator configurator : namespaceConfigurators) {
|
|
||||||
configurator.configure(namespaceResolutionContext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.Kub
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
|
||||||
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.SECRETS_ROLE_NAME;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory.NAMESPACE_TEMPLATE_ATTRIBUTE;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory.NAMESPACE_TEMPLATE_ATTRIBUTE;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyMap;
|
import static org.mockito.ArgumentMatchers.anyMap;
|
||||||
|
|
@ -51,6 +52,7 @@ import io.fabric8.kubernetes.api.model.Secret;
|
||||||
import io.fabric8.kubernetes.api.model.ServiceAccountList;
|
import io.fabric8.kubernetes.api.model.ServiceAccountList;
|
||||||
import io.fabric8.kubernetes.api.model.Status;
|
import io.fabric8.kubernetes.api.model.Status;
|
||||||
import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBuilder;
|
import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBuilder;
|
||||||
|
import io.fabric8.kubernetes.api.model.rbac.PolicyRule;
|
||||||
import io.fabric8.kubernetes.api.model.rbac.Role;
|
import io.fabric8.kubernetes.api.model.rbac.Role;
|
||||||
import io.fabric8.kubernetes.api.model.rbac.RoleBindingList;
|
import io.fabric8.kubernetes.api.model.rbac.RoleBindingList;
|
||||||
import io.fabric8.kubernetes.api.model.rbac.RoleList;
|
import io.fabric8.kubernetes.api.model.rbac.RoleList;
|
||||||
|
|
@ -88,6 +90,10 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesCl
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
|
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.api.shared.KubernetesNamespaceMeta;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.CredentialsSecretConfigurator;
|
||||||
|
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;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.NamespaceProvisioner;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.NamespaceProvisioner;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
@ -145,6 +151,7 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
serverMock.before();
|
serverMock.before();
|
||||||
k8sClient = spy(serverMock.getClient());
|
k8sClient = spy(serverMock.getClient());
|
||||||
lenient().when(clientFactory.create()).thenReturn(k8sClient);
|
lenient().when(clientFactory.create()).thenReturn(k8sClient);
|
||||||
|
lenient().when(cheClientFactory.create()).thenReturn(k8sClient);
|
||||||
lenient().when(k8sClient.namespaces()).thenReturn(namespaceOperation);
|
lenient().when(k8sClient.namespaces()).thenReturn(namespaceOperation);
|
||||||
|
|
||||||
lenient().when(namespaceOperation.withName(any())).thenReturn(namespaceResource);
|
lenient().when(namespaceOperation.withName(any())).thenReturn(namespaceResource);
|
||||||
|
|
@ -171,14 +178,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -199,14 +205,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -220,14 +225,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void shouldNormaliseNamespaceWhenUserNameStartsWithKube() {
|
public void shouldNormaliseNamespaceWhenUserNameStartsWithKube() {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>",
|
"che-<userid>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -246,14 +250,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -269,14 +272,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void shouldThrowExceptionIfNoDefaultNamespaceIsConfigured() {
|
public void shouldThrowExceptionIfNoDefaultNamespaceIsConfigured() {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -320,14 +322,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -364,14 +365,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -395,14 +395,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -429,14 +428,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
.build());
|
.build());
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -458,14 +456,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -489,28 +486,27 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
Set.of(new CredentialsSecretConfigurator(clientFactory)),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool));
|
pool));
|
||||||
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
||||||
KubernetesSecrets secrets = mock(KubernetesSecrets.class);
|
when(toReturnNamespace.getName()).thenReturn("namespaceName");
|
||||||
when(toReturnNamespace.secrets()).thenReturn(secrets);
|
|
||||||
when(toReturnNamespace.configMaps()).thenReturn(mock(KubernetesConfigsMaps.class));
|
|
||||||
when(secrets.get()).thenReturn(Collections.emptyList());
|
|
||||||
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
||||||
MixedOperation mixedOperation = mock(MixedOperation.class);
|
MixedOperation mixedOperation = mock(MixedOperation.class);
|
||||||
lenient().when(k8sClient.secrets()).thenReturn(mixedOperation);
|
when(k8sClient.secrets()).thenReturn(mixedOperation);
|
||||||
lenient().when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
||||||
|
when(namespaceResource.get()).thenReturn(null);
|
||||||
|
when(cheClientFactory.create()).thenReturn(k8sClient);
|
||||||
|
when(clientFactory.create()).thenReturn(k8sClient);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -531,28 +527,25 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
Set.of(new PreferencesConfigMapConfigurator(clientFactory)),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool));
|
pool));
|
||||||
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
||||||
KubernetesConfigsMaps configsMaps = mock(KubernetesConfigsMaps.class);
|
when(toReturnNamespace.getName()).thenReturn("namespaceName");
|
||||||
when(toReturnNamespace.secrets()).thenReturn(mock(KubernetesSecrets.class));
|
|
||||||
when(toReturnNamespace.configMaps()).thenReturn(configsMaps);
|
|
||||||
when(configsMaps.get(eq(PREFERENCES_CONFIGMAP_NAME))).thenReturn(empty());
|
|
||||||
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
||||||
MixedOperation mixedOperation = mock(MixedOperation.class);
|
MixedOperation mixedOperation = mock(MixedOperation.class);
|
||||||
lenient().when(k8sClient.configMaps()).thenReturn(mixedOperation);
|
when(k8sClient.configMaps()).thenReturn(mixedOperation);
|
||||||
lenient().when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
||||||
|
when(namespaceResource.get()).thenReturn(null);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -567,19 +560,60 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldNotCreateCredentialsSecretIfExists() throws Exception {
|
public void testAllConfiguratorsAreCalledWhenCreatingNamespace() throws InfrastructureException {
|
||||||
// given
|
// given
|
||||||
|
String namespaceName = "testNamespaceName";
|
||||||
|
NamespaceConfigurator configurator1 = Mockito.mock(NamespaceConfigurator.class);
|
||||||
|
NamespaceConfigurator configurator2 = Mockito.mock(NamespaceConfigurator.class);
|
||||||
|
Set<NamespaceConfigurator> namespaceConfigurators = Set.of(configurator1, configurator2);
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
namespaceConfigurators,
|
||||||
|
clientFactory,
|
||||||
|
cheClientFactory,
|
||||||
|
userManager,
|
||||||
|
preferenceManager,
|
||||||
|
pool));
|
||||||
|
EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false));
|
||||||
|
|
||||||
|
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
||||||
|
when(toReturnNamespace.getName()).thenReturn(namespaceName);
|
||||||
|
|
||||||
|
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", null, USER_ID, "old-che");
|
||||||
|
doReturn(toReturnNamespace).when(namespaceFactory).get(identity);
|
||||||
|
|
||||||
|
// when
|
||||||
|
KubernetesNamespace namespace = namespaceFactory.getOrCreate(identity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
NamespaceResolutionContext resolutionCtx =
|
||||||
|
new NamespaceResolutionContext("workspace123", "123", "jondoe");
|
||||||
|
verify(configurator1).configure(resolutionCtx, namespaceName);
|
||||||
|
verify(configurator2).configure(resolutionCtx, namespaceName);
|
||||||
|
assertEquals(namespace, toReturnNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotCreateCredentialsSecretIfExists() throws Exception {
|
||||||
|
// given
|
||||||
|
namespaceFactory =
|
||||||
|
spy(
|
||||||
|
new KubernetesNamespaceFactory(
|
||||||
|
"<username>-che",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
NAMESPACE_LABELS,
|
||||||
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -607,14 +641,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -643,14 +676,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void shouldThrowExceptionWhenFailedToGetInfoAboutDefaultNamespace() throws Exception {
|
public void shouldThrowExceptionWhenFailedToGetInfoAboutDefaultNamespace() throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -669,14 +701,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void shouldThrowExceptionWhenFailedToGetNamespaces() throws Exception {
|
public void shouldThrowExceptionWhenFailedToGetNamespaces() throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -700,14 +731,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -723,7 +753,6 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(toReturnNamespace, namespace);
|
assertEquals(toReturnNamespace, namespace);
|
||||||
verify(namespaceFactory, never()).doCreateServiceAccount(any(), any());
|
|
||||||
verify(toReturnNamespace).prepare(eq(false), any(), any());
|
verify(toReturnNamespace).prepare(eq(false), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -733,14 +762,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -757,7 +785,6 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(toReturnNamespace, namespace);
|
assertEquals(toReturnNamespace, namespace);
|
||||||
verify(namespaceFactory, never()).doCreateServiceAccount(any(), any());
|
|
||||||
verify(toReturnNamespace).prepare(eq(false), any(), any());
|
verify(toReturnNamespace).prepare(eq(false), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,17 +792,18 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void shouldPrepareWorkspaceServiceAccountIfItIsConfiguredAndNamespaceIsNotPredefined()
|
public void shouldPrepareWorkspaceServiceAccountIfItIsConfiguredAndNamespaceIsNotPredefined()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// given
|
// given
|
||||||
|
var serviceAccountCfg =
|
||||||
|
spy(new WorkspaceServiceAccountConfigurator("serviceAccount", "", clientFactory));
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"serviceAccount",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
Set.of(serviceAccountCfg),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -783,13 +811,12 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
pool));
|
pool));
|
||||||
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
||||||
prepareNamespace(toReturnNamespace);
|
prepareNamespace(toReturnNamespace);
|
||||||
when(toReturnNamespace.getWorkspaceId()).thenReturn("workspace123");
|
|
||||||
when(toReturnNamespace.getName()).thenReturn("workspace123");
|
when(toReturnNamespace.getName()).thenReturn("workspace123");
|
||||||
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
||||||
|
|
||||||
KubernetesWorkspaceServiceAccount serviceAccount =
|
KubernetesWorkspaceServiceAccount serviceAccount =
|
||||||
mock(KubernetesWorkspaceServiceAccount.class);
|
mock(KubernetesWorkspaceServiceAccount.class);
|
||||||
doReturn(serviceAccount).when(namespaceFactory).doCreateServiceAccount(any(), any());
|
doReturn(serviceAccount).when(serviceAccountCfg).doCreateServiceAccount(any(), any());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -797,24 +824,25 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory.getOrCreate(identity);
|
namespaceFactory.getOrCreate(identity);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(namespaceFactory).doCreateServiceAccount("workspace123", "workspace123");
|
verify(serviceAccountCfg).doCreateServiceAccount("workspace123", "workspace123");
|
||||||
verify(serviceAccount).prepare();
|
verify(serviceAccount).prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldBindToAllConfiguredClusterRoles() throws Exception {
|
public void shouldBindToAllConfiguredClusterRoles() throws Exception {
|
||||||
// given
|
// given
|
||||||
|
var serviceAccountConfigurator =
|
||||||
|
new WorkspaceServiceAccountConfigurator("serviceAccount", "cr2, cr3", clientFactory);
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"serviceAccount",
|
|
||||||
"cr2, cr3",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
Set.of(serviceAccountConfigurator),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -822,10 +850,10 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
pool));
|
pool));
|
||||||
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
||||||
prepareNamespace(toReturnNamespace);
|
prepareNamespace(toReturnNamespace);
|
||||||
when(toReturnNamespace.getWorkspaceId()).thenReturn("workspace123");
|
|
||||||
when(toReturnNamespace.getName()).thenReturn("workspace123");
|
when(toReturnNamespace.getName()).thenReturn("workspace123");
|
||||||
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
||||||
when(k8sClient.supportsApiPath(eq("/apis/metrics.k8s.io"))).thenReturn(true);
|
when(k8sClient.supportsApiPath(eq("/apis/metrics.k8s.io"))).thenReturn(true);
|
||||||
|
when(cheClientFactory.create()).thenReturn(k8sClient);
|
||||||
when(clientFactory.create(any())).thenReturn(k8sClient);
|
when(clientFactory.create(any())).thenReturn(k8sClient);
|
||||||
|
|
||||||
// pre-create the cluster roles
|
// pre-create the cluster roles
|
||||||
|
|
@ -848,7 +876,6 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory.getOrCreate(identity);
|
namespaceFactory.getOrCreate(identity);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(namespaceFactory).doCreateServiceAccount("workspace123", "workspace123");
|
|
||||||
|
|
||||||
ServiceAccountList sas = k8sClient.serviceAccounts().inNamespace("workspace123").list();
|
ServiceAccountList sas = k8sClient.serviceAccounts().inNamespace("workspace123").list();
|
||||||
assertEquals(sas.getItems().size(), 1);
|
assertEquals(sas.getItems().size(), 1);
|
||||||
|
|
@ -881,19 +908,20 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateExecAndViewRolesAndBindings() throws Exception {
|
public void shouldCreateAndBindCredentialsSecretRole() throws Exception {
|
||||||
// given
|
// given
|
||||||
|
var serviceAccountConfigurator =
|
||||||
|
new WorkspaceServiceAccountConfigurator("serviceAccount", "cr2, cr3", clientFactory);
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"serviceAccount",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
Set.of(serviceAccountConfigurator),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -901,11 +929,70 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
pool));
|
pool));
|
||||||
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
||||||
prepareNamespace(toReturnNamespace);
|
prepareNamespace(toReturnNamespace);
|
||||||
when(toReturnNamespace.getWorkspaceId()).thenReturn("workspace123");
|
|
||||||
when(toReturnNamespace.getName()).thenReturn("workspace123");
|
when(toReturnNamespace.getName()).thenReturn("workspace123");
|
||||||
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
||||||
when(k8sClient.supportsApiPath(eq("/apis/metrics.k8s.io"))).thenReturn(true);
|
|
||||||
when(clientFactory.create(any())).thenReturn(k8sClient);
|
when(clientFactory.create(any())).thenReturn(k8sClient);
|
||||||
|
when(cheClientFactory.create()).thenReturn(k8sClient);
|
||||||
|
|
||||||
|
// when
|
||||||
|
RuntimeIdentity identity =
|
||||||
|
new RuntimeIdentityImpl("workspace123", null, USER_ID, "workspace123");
|
||||||
|
namespaceFactory.getOrCreate(identity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
Optional<Role> roleOptional =
|
||||||
|
k8sClient
|
||||||
|
.rbac()
|
||||||
|
.roles()
|
||||||
|
.inNamespace("workspace123")
|
||||||
|
.list()
|
||||||
|
.getItems()
|
||||||
|
.stream()
|
||||||
|
.filter(r -> r.getMetadata().getName().equals(SECRETS_ROLE_NAME))
|
||||||
|
.findAny();
|
||||||
|
assertTrue(roleOptional.isPresent());
|
||||||
|
PolicyRule rule = roleOptional.get().getRules().get(0);
|
||||||
|
assertEquals(rule.getResources(), singletonList("secrets"));
|
||||||
|
assertEquals(rule.getResourceNames(), singletonList(CREDENTIALS_SECRET_NAME));
|
||||||
|
assertEquals(rule.getApiGroups(), singletonList(""));
|
||||||
|
assertEquals(rule.getVerbs(), Arrays.asList("get", "patch"));
|
||||||
|
assertTrue(
|
||||||
|
k8sClient
|
||||||
|
.rbac()
|
||||||
|
.roleBindings()
|
||||||
|
.inNamespace("workspace123")
|
||||||
|
.list()
|
||||||
|
.getItems()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(rb -> rb.getMetadata().getName().equals("serviceAccount-secrets")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreateExecAndViewRolesAndBindings() throws Exception {
|
||||||
|
// given
|
||||||
|
namespaceFactory =
|
||||||
|
spy(
|
||||||
|
new KubernetesNamespaceFactory(
|
||||||
|
"<username>-che",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
NAMESPACE_LABELS,
|
||||||
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
Set.of(
|
||||||
|
new WorkspaceServiceAccountConfigurator("serviceAccount", "", clientFactory)),
|
||||||
|
clientFactory,
|
||||||
|
cheClientFactory,
|
||||||
|
userManager,
|
||||||
|
preferenceManager,
|
||||||
|
pool));
|
||||||
|
KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class);
|
||||||
|
prepareNamespace(toReturnNamespace);
|
||||||
|
when(toReturnNamespace.getName()).thenReturn("workspace123");
|
||||||
|
doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any());
|
||||||
|
when(k8sClient.supportsApiPath(eq("/apis/metrics.k8s.io"))).thenReturn(true);
|
||||||
|
when(clientFactory.create(any())).thenReturn(k8sClient);
|
||||||
|
when(cheClientFactory.create()).thenReturn(k8sClient);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -913,7 +1000,6 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory.getOrCreate(identity);
|
namespaceFactory.getOrCreate(identity);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(namespaceFactory).doCreateServiceAccount("workspace123", "workspace123");
|
|
||||||
|
|
||||||
ServiceAccountList sas = k8sClient.serviceAccounts().inNamespace("workspace123").list();
|
ServiceAccountList sas = k8sClient.serviceAccounts().inNamespace("workspace123").list();
|
||||||
assertEquals(sas.getItems().size(), 1);
|
assertEquals(sas.getItems().size(), 1);
|
||||||
|
|
@ -951,62 +1037,19 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
"serviceAccount-secrets"));
|
"serviceAccount-secrets"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNullClusterRolesResultsInEmptySet() {
|
|
||||||
namespaceFactory =
|
|
||||||
new KubernetesNamespaceFactory(
|
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"che-<userid>",
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
NAMESPACE_LABELS,
|
|
||||||
NAMESPACE_ANNOTATIONS,
|
|
||||||
clientFactory,
|
|
||||||
cheClientFactory,
|
|
||||||
userManager,
|
|
||||||
preferenceManager,
|
|
||||||
pool);
|
|
||||||
assertTrue(namespaceFactory.getClusterRoleNames().isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClusterRolesProperlyParsed() {
|
|
||||||
namespaceFactory =
|
|
||||||
new KubernetesNamespaceFactory(
|
|
||||||
"",
|
|
||||||
" one,two, three ,,five ",
|
|
||||||
"che-<userid>",
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
NAMESPACE_LABELS,
|
|
||||||
NAMESPACE_ANNOTATIONS,
|
|
||||||
clientFactory,
|
|
||||||
cheClientFactory,
|
|
||||||
userManager,
|
|
||||||
preferenceManager,
|
|
||||||
pool);
|
|
||||||
Set<String> expected = Sets.newHashSet("one", "two", "three", "five");
|
|
||||||
assertTrue(namespaceFactory.getClusterRoleNames().containsAll(expected));
|
|
||||||
assertTrue(expected.containsAll(namespaceFactory.getClusterRoleNames()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
testEvalNamespaceUsesNamespaceDefaultIfWorkspaceDoesntRecordNamespaceAndLegacyNamespaceDoesntExist()
|
testEvalNamespaceUsesNamespaceDefaultIfWorkspaceDoesntRecordNamespaceAndLegacyNamespaceDoesntExist()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>",
|
"che-<userid>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1025,14 +1068,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void testEvalNamespaceUsesNamespaceFromUserPreferencesIfExist() throws Exception {
|
public void testEvalNamespaceUsesNamespaceFromUserPreferencesIfExist() throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>",
|
"che-<userid>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1056,14 +1098,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>-<username>",
|
"che-<userid>-<username>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1088,14 +1129,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>-<username>",
|
"che-<userid>-<username>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1120,14 +1160,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<username>",
|
"che-<username>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1148,14 +1187,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>",
|
"che-<userid>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1178,14 +1216,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void testEvalNamespaceTreatsWorkspaceRecordedNamespaceLiterally() throws Exception {
|
public void testEvalNamespaceTreatsWorkspaceRecordedNamespaceLiterally() throws Exception {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>",
|
"che-<userid>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1229,14 +1266,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1256,14 +1292,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1296,14 +1331,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-cha-cha-cha",
|
"<username>-cha-cha-cha",
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1335,14 +1369,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-cha-cha-cha",
|
"<username>-cha-cha-cha",
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1386,14 +1419,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
"try_placeholder_here=<username>",
|
"try_placeholder_here=<username>",
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1412,14 +1444,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
spy(
|
spy(
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
"try_placeholder_here=<username>",
|
"try_placeholder_here=<username>",
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1444,14 +1475,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void normalizeTest(String raw, String expected) {
|
public void normalizeTest(String raw, String expected) {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1464,14 +1494,13 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
public void normalizeLengthTest() {
|
public void normalizeLengthTest() {
|
||||||
namespaceFactory =
|
namespaceFactory =
|
||||||
new KubernetesNamespaceFactory(
|
new KubernetesNamespaceFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"che-<userid>",
|
"che-<userid>",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -1551,14 +1580,15 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
private void prepareNamespace(KubernetesNamespace namespace) throws InfrastructureException {
|
private void prepareNamespace(KubernetesNamespace namespace) throws InfrastructureException {
|
||||||
KubernetesSecrets secrets = mock(KubernetesSecrets.class);
|
KubernetesSecrets secrets = mock(KubernetesSecrets.class);
|
||||||
|
lenient().when(namespace.secrets()).thenReturn(secrets);
|
||||||
KubernetesConfigsMaps configmaps = mock(KubernetesConfigsMaps.class);
|
KubernetesConfigsMaps configmaps = mock(KubernetesConfigsMaps.class);
|
||||||
when(namespace.secrets()).thenReturn(secrets);
|
lenient().when(namespace.secrets()).thenReturn(secrets);
|
||||||
when(namespace.configMaps()).thenReturn(configmaps);
|
lenient().when(namespace.configMaps()).thenReturn(configmaps);
|
||||||
Secret secretMock = mock(Secret.class);
|
Secret secretMock = mock(Secret.class);
|
||||||
ObjectMeta objectMeta = mock(ObjectMeta.class);
|
ObjectMeta objectMeta = mock(ObjectMeta.class);
|
||||||
when(objectMeta.getName()).thenReturn(CREDENTIALS_SECRET_NAME);
|
lenient().when(objectMeta.getName()).thenReturn(CREDENTIALS_SECRET_NAME);
|
||||||
when(secretMock.getMetadata()).thenReturn(objectMeta);
|
lenient().when(secretMock.getMetadata()).thenReturn(objectMeta);
|
||||||
when(secrets.get()).thenReturn(Collections.singletonList(secretMock));
|
lenient().when(secrets.get()).thenReturn(Collections.singletonList(secretMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Namespace createNamespace(String name, String phase) {
|
private Namespace createNamespace(String name, String phase) {
|
||||||
|
|
@ -1574,6 +1604,6 @@ public class KubernetesNamespaceFactoryTest {
|
||||||
|
|
||||||
private KubernetesNamespaceMeta testProvisioning(NamespaceResolutionContext context)
|
private KubernetesNamespaceMeta testProvisioning(NamespaceResolutionContext context)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
return new NamespaceProvisioner(namespaceFactory, emptySet()).provision(context);
|
return new NamespaceProvisioner(namespaceFactory).provision(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ public class KubernetesNamespaceTest {
|
||||||
@BeforeMethod
|
@BeforeMethod
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
lenient().when(clientFactory.create(anyString())).thenReturn(kubernetesClient);
|
lenient().when(clientFactory.create(anyString())).thenReturn(kubernetesClient);
|
||||||
|
lenient().when(cheClientFactory.create(anyString())).thenReturn(kubernetesClient);
|
||||||
|
|
||||||
lenient().doReturn(namespaceOperation).when(kubernetesClient).namespaces();
|
lenient().doReturn(namespaceOperation).when(kubernetesClient).namespaces();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
||||||
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
import org.testng.Assert;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
import org.testng.annotations.Listeners;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Listeners(MockitoTestNGListener.class)
|
||||||
|
public class CredentialsSecretConfiguratorTest {
|
||||||
|
|
||||||
|
private NamespaceConfigurator configurator;
|
||||||
|
|
||||||
|
@Mock private KubernetesClientFactory clientFactory;
|
||||||
|
private KubernetesServer serverMock;
|
||||||
|
|
||||||
|
private NamespaceResolutionContext namespaceResolutionContext;
|
||||||
|
private final String TEST_NAMESPACE_NAME = "namespace123";
|
||||||
|
private final String TEST_WORKSPACE_ID = "workspace123";
|
||||||
|
private final String TEST_USER_ID = "user123";
|
||||||
|
private final String TEST_USERNAME = "jondoe";
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() throws InfrastructureException {
|
||||||
|
configurator = new CredentialsSecretConfigurator(clientFactory);
|
||||||
|
|
||||||
|
serverMock = new KubernetesServer(true, true);
|
||||||
|
serverMock.before();
|
||||||
|
KubernetesClient client = spy(serverMock.getClient());
|
||||||
|
when(clientFactory.create()).thenReturn(client);
|
||||||
|
|
||||||
|
namespaceResolutionContext =
|
||||||
|
new NamespaceResolutionContext(TEST_WORKSPACE_ID, TEST_USER_ID, TEST_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createCredentialsSecretWhenDoesNotExist()
|
||||||
|
throws InfrastructureException, InterruptedException {
|
||||||
|
// given - clean env
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then create a secret
|
||||||
|
Assert.assertEquals(serverMock.getLastRequest().getMethod(), "POST");
|
||||||
|
Assert.assertNotNull(
|
||||||
|
serverMock
|
||||||
|
.getClient()
|
||||||
|
.secrets()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.withName(CREDENTIALS_SECRET_NAME)
|
||||||
|
.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doNothingWhenSecretAlreadyExists()
|
||||||
|
throws InfrastructureException, InterruptedException {
|
||||||
|
// given - secret already exists
|
||||||
|
serverMock
|
||||||
|
.getClient()
|
||||||
|
.secrets()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.create(
|
||||||
|
new SecretBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName(CREDENTIALS_SECRET_NAME)
|
||||||
|
.withAnnotations(Map.of("already", "created"))
|
||||||
|
.endMetadata()
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then - don't create the secret
|
||||||
|
Assert.assertEquals(serverMock.getLastRequest().getMethod(), "GET");
|
||||||
|
var secrets =
|
||||||
|
serverMock.getClient().secrets().inNamespace(TEST_NAMESPACE_NAME).list().getItems();
|
||||||
|
Assert.assertEquals(secrets.size(), 1);
|
||||||
|
Assert.assertEquals(secrets.get(0).getMetadata().getAnnotations().get("already"), "created");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
||||||
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
import org.testng.Assert;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
import org.testng.annotations.Listeners;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Listeners(MockitoTestNGListener.class)
|
||||||
|
public class PreferencesConfigMapConfiguratorTest {
|
||||||
|
private NamespaceConfigurator configurator;
|
||||||
|
|
||||||
|
@Mock private KubernetesClientFactory clientFactory;
|
||||||
|
private KubernetesServer serverMock;
|
||||||
|
|
||||||
|
private NamespaceResolutionContext namespaceResolutionContext;
|
||||||
|
private final String TEST_NAMESPACE_NAME = "namespace123";
|
||||||
|
private final String TEST_WORKSPACE_ID = "workspace123";
|
||||||
|
private final String TEST_USER_ID = "user123";
|
||||||
|
private final String TEST_USERNAME = "jondoe";
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() throws InfrastructureException {
|
||||||
|
configurator = new PreferencesConfigMapConfigurator(clientFactory);
|
||||||
|
|
||||||
|
serverMock = new KubernetesServer(true, true);
|
||||||
|
serverMock.before();
|
||||||
|
KubernetesClient client = spy(serverMock.getClient());
|
||||||
|
when(clientFactory.create()).thenReturn(client);
|
||||||
|
|
||||||
|
namespaceResolutionContext =
|
||||||
|
new NamespaceResolutionContext(TEST_WORKSPACE_ID, TEST_USER_ID, TEST_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createConfigmapWhenDoesntExist()
|
||||||
|
throws InfrastructureException, InterruptedException {
|
||||||
|
// given - clean env
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then configmap created
|
||||||
|
Assert.assertEquals(serverMock.getLastRequest().getMethod(), "POST");
|
||||||
|
Assert.assertNotNull(
|
||||||
|
serverMock
|
||||||
|
.getClient()
|
||||||
|
.configMaps()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.withName(PREFERENCES_CONFIGMAP_NAME)
|
||||||
|
.get());
|
||||||
|
verify(clientFactory, times(1)).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doNothingWhenConfigmapExists() throws InfrastructureException, InterruptedException {
|
||||||
|
// given - configmap already exists
|
||||||
|
serverMock
|
||||||
|
.getClient()
|
||||||
|
.configMaps()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.create(
|
||||||
|
new ConfigMapBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName(PREFERENCES_CONFIGMAP_NAME)
|
||||||
|
.withAnnotations(Map.of("already", "created"))
|
||||||
|
.endMetadata()
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then - don't create the configmap
|
||||||
|
Assert.assertEquals(serverMock.getLastRequest().getMethod(), "GET");
|
||||||
|
var configmaps =
|
||||||
|
serverMock.getClient().configMaps().inNamespace(TEST_NAMESPACE_NAME).list().getItems();
|
||||||
|
Assert.assertEquals(configmaps.size(), 1);
|
||||||
|
Assert.assertEquals(configmaps.get(0).getMetadata().getAnnotations().get("already"), "created");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.lenient;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
|
||||||
|
import io.fabric8.kubernetes.api.model.rbac.Subject;
|
||||||
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
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.Listeners;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Listeners(MockitoTestNGListener.class)
|
||||||
|
public class UserPermissionConfiguratorTest {
|
||||||
|
|
||||||
|
private NamespaceConfigurator configurator;
|
||||||
|
|
||||||
|
@Mock private CheServerKubernetesClientFactory clientFactory;
|
||||||
|
private KubernetesClient client;
|
||||||
|
private KubernetesServer serverMock;
|
||||||
|
|
||||||
|
private NamespaceResolutionContext namespaceResolutionContext;
|
||||||
|
private final String TEST_NAMESPACE_NAME = "namespace123";
|
||||||
|
private final String TEST_WORKSPACE_ID = "workspace123";
|
||||||
|
private final String TEST_USER_ID = "user123";
|
||||||
|
private final String TEST_USERNAME = "jondoe";
|
||||||
|
private final String TEST_CLUSTER_ROLES = "cr1,cr2";
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() throws InfrastructureException {
|
||||||
|
configurator = new UserPermissionConfigurator(TEST_CLUSTER_ROLES, clientFactory);
|
||||||
|
|
||||||
|
serverMock = new KubernetesServer(true, true);
|
||||||
|
serverMock.before();
|
||||||
|
client = spy(serverMock.getClient());
|
||||||
|
lenient().when(clientFactory.create()).thenReturn(client);
|
||||||
|
|
||||||
|
namespaceResolutionContext =
|
||||||
|
new NamespaceResolutionContext(TEST_WORKSPACE_ID, TEST_USER_ID, TEST_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doNothingWhenNoClusterRolesSet()
|
||||||
|
throws InfrastructureException, InterruptedException {
|
||||||
|
// given - no cluster roles set
|
||||||
|
configurator = new UserPermissionConfigurator("", clientFactory);
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then - do nothing
|
||||||
|
Assert.assertNull(serverMock.getLastRequest());
|
||||||
|
verify(clientFactory, never()).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bindAllClusterRolesWhenEmptyEnv()
|
||||||
|
throws InfrastructureException, InterruptedException {
|
||||||
|
// given - clean env
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then - create all role bindings
|
||||||
|
var roleBindings =
|
||||||
|
serverMock.getClient().rbac().roleBindings().inNamespace(TEST_NAMESPACE_NAME);
|
||||||
|
Assert.assertEquals(roleBindings.list().getItems().size(), 2);
|
||||||
|
|
||||||
|
var cr1 = roleBindings.withName("cr1").get();
|
||||||
|
Assert.assertNotNull(cr1);
|
||||||
|
Assert.assertEquals(cr1.getSubjects().get(0).getName(), TEST_USERNAME);
|
||||||
|
Assert.assertEquals(cr1.getSubjects().get(0).getNamespace(), TEST_NAMESPACE_NAME);
|
||||||
|
Assert.assertEquals(cr1.getRoleRef().getName(), "cr1");
|
||||||
|
|
||||||
|
var cr2 = roleBindings.withName("cr2").get();
|
||||||
|
Assert.assertNotNull(cr2);
|
||||||
|
Assert.assertEquals(cr2.getSubjects().get(0).getName(), TEST_USERNAME);
|
||||||
|
Assert.assertEquals(cr2.getSubjects().get(0).getNamespace(), TEST_NAMESPACE_NAME);
|
||||||
|
Assert.assertEquals(cr2.getRoleRef().getName(), "cr2");
|
||||||
|
|
||||||
|
verify(client, times(2)).rbac();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void replaceExistingBindingsWithSameName() throws InfrastructureException {
|
||||||
|
// given - cr1 binding already exists
|
||||||
|
client
|
||||||
|
.rbac()
|
||||||
|
.roleBindings()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.create(
|
||||||
|
new RoleBindingBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName("cr1")
|
||||||
|
.endMetadata()
|
||||||
|
.withSubjects(new Subject("blabol", "blabol", "blabol", "blabol"))
|
||||||
|
.withNewRoleRef()
|
||||||
|
.withName("blabol")
|
||||||
|
.endRoleRef()
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then
|
||||||
|
var roleBindings = client.rbac().roleBindings().inNamespace(TEST_NAMESPACE_NAME);
|
||||||
|
Assert.assertEquals(roleBindings.list().getItems().size(), 2);
|
||||||
|
|
||||||
|
var cr1 = roleBindings.withName("cr1").get();
|
||||||
|
Assert.assertEquals(cr1.getRoleRef().getName(), "cr1");
|
||||||
|
Assert.assertEquals(cr1.getSubjects().size(), 1);
|
||||||
|
Assert.assertEquals(cr1.getSubjects().get(0).getName(), TEST_USERNAME);
|
||||||
|
Assert.assertEquals(cr1.getSubjects().get(0).getNamespace(), TEST_NAMESPACE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keepOtherClusterRoles() throws InfrastructureException {
|
||||||
|
// given - some other binding in place
|
||||||
|
client
|
||||||
|
.rbac()
|
||||||
|
.roleBindings()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.create(
|
||||||
|
new RoleBindingBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName("othercr")
|
||||||
|
.endMetadata()
|
||||||
|
.withSubjects(new Subject("blabol", "blabol", "blabol", "blabol"))
|
||||||
|
.withNewRoleRef()
|
||||||
|
.withName("blabol")
|
||||||
|
.endRoleRef()
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then
|
||||||
|
var roleBindings = client.rbac().roleBindings().inNamespace(TEST_NAMESPACE_NAME);
|
||||||
|
Assert.assertEquals(roleBindings.list().getItems().size(), 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -86,7 +86,7 @@ public class UserPreferencesConfiguratorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreatePreferencesSecret() throws InfrastructureException {
|
public void shouldCreatePreferencesSecret() throws InfrastructureException {
|
||||||
userPreferencesConfigurator.configure(context);
|
userPreferencesConfigurator.configure(context, USER_NAMESPACE);
|
||||||
List<Secret> secrets =
|
List<Secret> secrets =
|
||||||
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
|
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
|
||||||
assertEquals(secrets.size(), 1);
|
assertEquals(secrets.size(), 1);
|
||||||
|
|
@ -99,7 +99,7 @@ public class UserPreferencesConfiguratorTest {
|
||||||
"Preferences of user with id:" + USER_ID + " cannot be retrieved.")
|
"Preferences of user with id:" + USER_ID + " cannot be retrieved.")
|
||||||
public void shouldNotCreateSecretOnException() throws ServerException, InfrastructureException {
|
public void shouldNotCreateSecretOnException() throws ServerException, InfrastructureException {
|
||||||
when(preferenceManager.find(USER_ID)).thenThrow(new ServerException("test exception"));
|
when(preferenceManager.find(USER_ID)).thenThrow(new ServerException("test exception"));
|
||||||
userPreferencesConfigurator.configure(context);
|
userPreferencesConfigurator.configure(context, USER_NAMESPACE);
|
||||||
fail("InfrastructureException should have been thrown.");
|
fail("InfrastructureException should have been thrown.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator;
|
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
import static org.testng.Assert.fail;
|
import static org.testng.Assert.fail;
|
||||||
|
|
@ -26,7 +25,6 @@ import org.eclipse.che.api.user.server.model.impl.UserImpl;
|
||||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.testng.MockitoTestNGListener;
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
|
@ -47,7 +45,6 @@ public class UserProfileConfiguratorTest {
|
||||||
private static final String USER_EMAIL = "user-email";
|
private static final String USER_EMAIL = "user-email";
|
||||||
private static final String USER_NAMESPACE = "user-namespace";
|
private static final String USER_NAMESPACE = "user-namespace";
|
||||||
|
|
||||||
@Mock private KubernetesNamespaceFactory namespaceFactory;
|
|
||||||
@Mock private KubernetesClientFactory clientFactory;
|
@Mock private KubernetesClientFactory clientFactory;
|
||||||
@Mock private UserManager userManager;
|
@Mock private UserManager userManager;
|
||||||
|
|
||||||
|
|
@ -63,7 +60,6 @@ public class UserProfileConfiguratorTest {
|
||||||
kubernetesServer.before();
|
kubernetesServer.before();
|
||||||
|
|
||||||
when(userManager.getById(USER_ID)).thenReturn(new UserImpl(USER_ID, USER_EMAIL, USER_NAME));
|
when(userManager.getById(USER_ID)).thenReturn(new UserImpl(USER_ID, USER_EMAIL, USER_NAME));
|
||||||
when(namespaceFactory.evaluateNamespaceName(any())).thenReturn(USER_NAMESPACE);
|
|
||||||
when(clientFactory.create()).thenReturn(kubernetesServer.getClient());
|
when(clientFactory.create()).thenReturn(kubernetesServer.getClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +70,7 @@ public class UserProfileConfiguratorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateProfileSecret() throws InfrastructureException {
|
public void shouldCreateProfileSecret() throws InfrastructureException {
|
||||||
userProfileConfigurator.configure(context);
|
userProfileConfigurator.configure(context, USER_NAMESPACE);
|
||||||
List<Secret> secrets =
|
List<Secret> secrets =
|
||||||
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
|
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
|
||||||
assertEquals(secrets.size(), 1);
|
assertEquals(secrets.size(), 1);
|
||||||
|
|
@ -87,7 +83,7 @@ public class UserProfileConfiguratorTest {
|
||||||
public void shouldNotCreateSecretOnException()
|
public void shouldNotCreateSecretOnException()
|
||||||
throws NotFoundException, ServerException, InfrastructureException {
|
throws NotFoundException, ServerException, InfrastructureException {
|
||||||
when(userManager.getById(USER_ID)).thenThrow(new ServerException("test exception"));
|
when(userManager.getById(USER_ID)).thenThrow(new ServerException("test exception"));
|
||||||
userProfileConfigurator.configure(context);
|
userProfileConfigurator.configure(context, USER_NAMESPACE);
|
||||||
fail("InfrastructureException should have been thrown.");
|
fail("InfrastructureException should have been thrown.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.namespace.configurator;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.joining;
|
||||||
|
import static org.mockito.Mockito.lenient;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBuilder;
|
||||||
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesWorkspaceServiceAccount;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
import org.testng.annotations.Listeners;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Listeners(MockitoTestNGListener.class)
|
||||||
|
public class WorkspaceServiceAccountConfiguratorTest {
|
||||||
|
|
||||||
|
private WorkspaceServiceAccountConfigurator configurator;
|
||||||
|
|
||||||
|
@Mock private CheServerKubernetesClientFactory clientFactory;
|
||||||
|
private KubernetesClient client;
|
||||||
|
private KubernetesServer serverMock;
|
||||||
|
|
||||||
|
private NamespaceResolutionContext namespaceResolutionContext;
|
||||||
|
@Mock private KubernetesWorkspaceServiceAccount kubeWSA;
|
||||||
|
private final String TEST_NAMESPACE_NAME = "namespace123";
|
||||||
|
private final String TEST_WORKSPACE_ID = "workspace123";
|
||||||
|
private final String TEST_USER_ID = "user123";
|
||||||
|
private final String TEST_USERNAME = "jondoe";
|
||||||
|
private final String TEST_SERVICE_ACCOUNT = "serviceAccount123";
|
||||||
|
private final String TEST_CLUSTER_ROLES = "cr1, cr2";
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() throws InfrastructureException {
|
||||||
|
configurator =
|
||||||
|
spy(
|
||||||
|
new WorkspaceServiceAccountConfigurator(
|
||||||
|
TEST_SERVICE_ACCOUNT, TEST_CLUSTER_ROLES, clientFactory));
|
||||||
|
// when(configurator.doCreateServiceAccount(TEST_WORKSPACE_ID,
|
||||||
|
// TEST_NAMESPACE_NAME)).thenReturn(kubeWSA);
|
||||||
|
|
||||||
|
serverMock = new KubernetesServer(true, true);
|
||||||
|
serverMock.before();
|
||||||
|
client = spy(serverMock.getClient());
|
||||||
|
lenient().when(clientFactory.create(TEST_WORKSPACE_ID)).thenReturn(client);
|
||||||
|
|
||||||
|
namespaceResolutionContext =
|
||||||
|
new NamespaceResolutionContext(TEST_WORKSPACE_ID, TEST_USER_ID, TEST_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createWorkspaceServiceAccountWithBindings()
|
||||||
|
throws InfrastructureException, InterruptedException {
|
||||||
|
// given - cluster roles exists in cluster
|
||||||
|
configurator =
|
||||||
|
new WorkspaceServiceAccountConfigurator(
|
||||||
|
TEST_SERVICE_ACCOUNT, TEST_CLUSTER_ROLES, clientFactory);
|
||||||
|
client
|
||||||
|
.rbac()
|
||||||
|
.clusterRoles()
|
||||||
|
.create(new ClusterRoleBuilder().withNewMetadata().withName("cr1").endMetadata().build());
|
||||||
|
client
|
||||||
|
.rbac()
|
||||||
|
.clusterRoles()
|
||||||
|
.create(new ClusterRoleBuilder().withNewMetadata().withName("cr2").endMetadata().build());
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then - create service account with all the bindings
|
||||||
|
var serviceAccount =
|
||||||
|
client
|
||||||
|
.serviceAccounts()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.withName(TEST_SERVICE_ACCOUNT)
|
||||||
|
.get();
|
||||||
|
assertNotNull(serviceAccount);
|
||||||
|
|
||||||
|
var roleBindings =
|
||||||
|
client.rbac().roleBindings().inNamespace(TEST_NAMESPACE_NAME).list().getItems();
|
||||||
|
assertEquals(
|
||||||
|
roleBindings.size(),
|
||||||
|
6,
|
||||||
|
roleBindings
|
||||||
|
.stream()
|
||||||
|
.map(r -> r.getMetadata().getName())
|
||||||
|
.collect(joining(", "))); // exec, secrets, configmaps, view bindings + cr1, cr2
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dontCreateBindingsWhenClusterRolesDontExists() throws InfrastructureException {
|
||||||
|
// given - cluster roles exists in cluster
|
||||||
|
configurator =
|
||||||
|
new WorkspaceServiceAccountConfigurator(
|
||||||
|
TEST_SERVICE_ACCOUNT, TEST_CLUSTER_ROLES, clientFactory);
|
||||||
|
|
||||||
|
// when
|
||||||
|
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||||
|
|
||||||
|
// then - create service account with default bindings
|
||||||
|
var serviceAccount =
|
||||||
|
client
|
||||||
|
.serviceAccounts()
|
||||||
|
.inNamespace(TEST_NAMESPACE_NAME)
|
||||||
|
.withName(TEST_SERVICE_ACCOUNT)
|
||||||
|
.get();
|
||||||
|
assertNotNull(serviceAccount);
|
||||||
|
|
||||||
|
var roleBindings =
|
||||||
|
client.rbac().roleBindings().inNamespace(TEST_NAMESPACE_NAME).list().getItems();
|
||||||
|
assertEquals(
|
||||||
|
roleBindings.size(),
|
||||||
|
4,
|
||||||
|
roleBindings
|
||||||
|
.stream()
|
||||||
|
.map(r -> r.getMetadata().getName())
|
||||||
|
.collect(joining(", "))); // exec, secrets, configmaps, view bindings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -134,6 +134,10 @@
|
||||||
<groupId>org.eclipse.che.multiuser</groupId>
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
<artifactId>che-multiuser-keycloak-shared</artifactId>
|
<artifactId>che-multiuser-keycloak-shared</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<artifactId>che-multiuser-oidc</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
|
@ -148,6 +152,22 @@
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>mockwebserver</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.fabric8</groupId>
|
||||||
|
<artifactId>openshift-server-mock</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.che.core</groupId>
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
<artifactId>che-core-api-dto</artifactId>
|
<artifactId>che-core-api-dto</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,6 @@ public class OpenShiftClientFactory extends KubernetesClientFactory {
|
||||||
private static final String BEFORE_TOKEN = "access_token=";
|
private static final String BEFORE_TOKEN = "access_token=";
|
||||||
private static final String AFTER_TOKEN = "&expires";
|
private static final String AFTER_TOKEN = "&expires";
|
||||||
|
|
||||||
private final KubernetesClientConfigFactory configBuilder;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public OpenShiftClientFactory(
|
public OpenShiftClientFactory(
|
||||||
KubernetesClientConfigFactory configBuilder,
|
KubernetesClientConfigFactory configBuilder,
|
||||||
|
|
@ -72,6 +70,7 @@ public class OpenShiftClientFactory extends KubernetesClientFactory {
|
||||||
int connectionPoolKeepAlive,
|
int connectionPoolKeepAlive,
|
||||||
EventListener eventListener) {
|
EventListener eventListener) {
|
||||||
super(
|
super(
|
||||||
|
configBuilder,
|
||||||
masterUrl,
|
masterUrl,
|
||||||
doTrustCerts,
|
doTrustCerts,
|
||||||
maxConcurrentRequests,
|
maxConcurrentRequests,
|
||||||
|
|
@ -79,7 +78,6 @@ public class OpenShiftClientFactory extends KubernetesClientFactory {
|
||||||
maxIdleConnections,
|
maxIdleConnections,
|
||||||
connectionPoolKeepAlive,
|
connectionPoolKeepAlive,
|
||||||
eventListener);
|
eventListener);
|
||||||
this.configBuilder = configBuilder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -96,7 +94,7 @@ public class OpenShiftClientFactory extends KubernetesClientFactory {
|
||||||
*/
|
*/
|
||||||
public OpenShiftClient createOC(String workspaceId) throws InfrastructureException {
|
public OpenShiftClient createOC(String workspaceId) throws InfrastructureException {
|
||||||
Config configForWorkspace = buildConfig(getDefaultConfig(), workspaceId);
|
Config configForWorkspace = buildConfig(getDefaultConfig(), workspaceId);
|
||||||
return createOC(configForWorkspace);
|
return create(configForWorkspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -114,23 +112,13 @@ public class OpenShiftClientFactory extends KubernetesClientFactory {
|
||||||
* @throws InfrastructureException if any error occurs on client instance creation.
|
* @throws InfrastructureException if any error occurs on client instance creation.
|
||||||
*/
|
*/
|
||||||
public OpenShiftClient createOC() throws InfrastructureException {
|
public OpenShiftClient createOC() throws InfrastructureException {
|
||||||
return createOC(buildConfig(getDefaultConfig(), null));
|
return create(buildConfig(getDefaultConfig(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenShiftClient createAuthenticatedClient(String token) {
|
public OpenShiftClient createAuthenticatedClient(String token) {
|
||||||
Config config = getDefaultConfig();
|
Config config = getDefaultConfig();
|
||||||
config.setOauthToken(token);
|
config.setOauthToken(token);
|
||||||
return createOC(config);
|
return create(config);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OkHttpClient getAuthenticatedHttpClient() throws InfrastructureException {
|
|
||||||
if (!configBuilder.isPersonalized()) {
|
|
||||||
throw new InfrastructureException(
|
|
||||||
"Not able to construct impersonating openshift API client.");
|
|
||||||
}
|
|
||||||
// Ensure to get OkHttpClient with all necessary interceptors.
|
|
||||||
return createOC(buildConfig(getDefaultConfig(), null)).getHttpClient();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -147,19 +135,6 @@ public class OpenShiftClientFactory extends KubernetesClientFactory {
|
||||||
return configBuilder.build();
|
return configBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the Openshift {@link Config} object based on a provided {@link Config} object and an
|
|
||||||
* optional workspace ID.
|
|
||||||
*
|
|
||||||
* <p>This method overrides the one in the Kubernetes infrastructure to introduce an additional
|
|
||||||
* extension level by delegating to an {@link KubernetesClientConfigFactory}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Config buildConfig(Config config, @Nullable String workspaceId)
|
|
||||||
throws InfrastructureException {
|
|
||||||
return configBuilder.buildConfig(config, workspaceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Interceptor buildKubernetesInterceptor(Config config) {
|
protected Interceptor buildKubernetesInterceptor(Config config) {
|
||||||
final String oauthToken;
|
final String oauthToken;
|
||||||
|
|
@ -223,7 +198,7 @@ public class OpenShiftClientFactory extends KubernetesClientFactory {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefaultOpenShiftClient createOC(Config config) {
|
protected DefaultOpenShiftClient create(Config config) {
|
||||||
return new UnclosableOpenShiftClient(
|
return new UnclosableOpenShiftClient(
|
||||||
clientForConfig(config), config, this::initializeRequestTracing);
|
clientForConfig(config), config, this::initializeRequestTracing);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,9 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesDev
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.CredentialsSecretConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
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.UserPreferencesConfigurator;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserPreferencesConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserProfileConfigurator;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserProfileConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy;
|
||||||
|
|
@ -94,6 +96,8 @@ import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftE
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory;
|
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.project.OpenShiftProjectFactory;
|
import org.eclipse.che.workspace.infrastructure.openshift.project.OpenShiftProjectFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.project.RemoveProjectOnWorkspaceRemove;
|
import org.eclipse.che.workspace.infrastructure.openshift.project.RemoveProjectOnWorkspaceRemove;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.openshift.project.configurator.OpenShiftStopWorkspaceRoleConfigurator;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.openshift.project.configurator.OpenShiftWorkspaceServiceAccountConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenShiftPreviewUrlCommandProvisioner;
|
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenShiftPreviewUrlCommandProvisioner;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenshiftTrustedCAProvisioner;
|
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenshiftTrustedCAProvisioner;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.provision.RouteTlsProvisioner;
|
import org.eclipse.che.workspace.infrastructure.openshift.provision.RouteTlsProvisioner;
|
||||||
|
|
@ -117,6 +121,10 @@ public class OpenShiftInfraModule extends AbstractModule {
|
||||||
Multibinder.newSetBinder(binder(), NamespaceConfigurator.class);
|
Multibinder.newSetBinder(binder(), NamespaceConfigurator.class);
|
||||||
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
|
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
|
||||||
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
|
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(CredentialsSecretConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(PreferencesConfigMapConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(OpenShiftWorkspaceServiceAccountConfigurator.class);
|
||||||
|
namespaceConfigurators.addBinding().to(OpenShiftStopWorkspaceRoleConfigurator.class);
|
||||||
|
|
||||||
bind(KubernetesNamespaceService.class);
|
bind(KubernetesNamespaceService.class);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
|
package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
|
||||||
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.AUTH_SERVER_URL_SETTING;
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import io.fabric8.kubernetes.client.Config;
|
import io.fabric8.kubernetes.client.Config;
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,6 @@ import static com.google.common.base.MoreObjects.firstNonNull;
|
||||||
import io.fabric8.kubernetes.api.model.ObjectMeta;
|
import io.fabric8.kubernetes.api.model.ObjectMeta;
|
||||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||||
import io.fabric8.openshift.client.OpenShiftClient;
|
import io.fabric8.openshift.client.OpenShiftClient;
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.FilterConfig;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.ServletRequest;
|
|
||||||
import jakarta.servlet.ServletResponse;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
@ -46,9 +37,6 @@ import org.slf4j.LoggerFactory;
|
||||||
/**
|
/**
|
||||||
* This filter uses given token directly. It's used for native OpenShift user authentication.
|
* This filter uses given token directly. It's used for native OpenShift user authentication.
|
||||||
* Requests without token or with invalid token are rejected.
|
* Requests without token or with invalid token are rejected.
|
||||||
*
|
|
||||||
* <p>{@link OpenshiftTokenInitializationFilter#UNAUTHORIZED_ENDPOINT_PATHS} is list of
|
|
||||||
* unauthenticated paths, that are allowed without token.
|
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class OpenshiftTokenInitializationFilter
|
public class OpenshiftTokenInitializationFilter
|
||||||
|
|
@ -57,9 +45,6 @@ public class OpenshiftTokenInitializationFilter
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
LoggerFactory.getLogger(OpenshiftTokenInitializationFilter.class);
|
LoggerFactory.getLogger(OpenshiftTokenInitializationFilter.class);
|
||||||
|
|
||||||
private static final List<String> UNAUTHORIZED_ENDPOINT_PATHS =
|
|
||||||
Collections.singletonList("/system/state");
|
|
||||||
|
|
||||||
private final PermissionChecker permissionChecker;
|
private final PermissionChecker permissionChecker;
|
||||||
private final OpenShiftClientFactory clientFactory;
|
private final OpenShiftClientFactory clientFactory;
|
||||||
|
|
||||||
|
|
@ -121,38 +106,4 @@ public class OpenshiftTokenInitializationFilter
|
||||||
// we can use fake email, but probably we will need to find better solution.
|
// we can use fake email, but probably we will need to find better solution.
|
||||||
return userMeta.getName() + "@che";
|
return userMeta.getName() + "@che";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If request path is in {@link OpenshiftTokenInitializationFilter#UNAUTHORIZED_ENDPOINT_PATHS},
|
|
||||||
* the request is allowed. All other requests are rejected with error code 401.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void handleMissingToken(
|
|
||||||
ServletRequest request, ServletResponse response, FilterChain chain)
|
|
||||||
throws IOException, ServletException {
|
|
||||||
|
|
||||||
// if request path is in unauthorized endpoints, continue
|
|
||||||
if (request instanceof HttpServletRequest) {
|
|
||||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
|
||||||
String path = httpRequest.getServletPath();
|
|
||||||
if (UNAUTHORIZED_ENDPOINT_PATHS.contains(path)) {
|
|
||||||
LOG.debug("Allowing request to '{}' without authorization header.", path);
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.error("Rejecting the request due to missing/expired token in Authorization header.");
|
|
||||||
sendError(response, 401, "Authorization token is missing or expired");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(FilterConfig filterConfig) {
|
|
||||||
LOG.trace("OpenshiftTokenInitializationFilter#init({})", filterConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
LOG.trace("OpenshiftTokenInitializationFilter#destroy()");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ public class OpenShiftProject extends KubernetesNamespace {
|
||||||
|
|
||||||
private final OpenShiftRoutes routes;
|
private final OpenShiftRoutes routes;
|
||||||
private final OpenShiftClientFactory clientFactory;
|
private final OpenShiftClientFactory clientFactory;
|
||||||
private final KubernetesClientFactory cheClientFactory;
|
|
||||||
private final CheServerOpenshiftClientFactory cheServerOpenshiftClientFactory;
|
private final CheServerOpenshiftClientFactory cheServerOpenshiftClientFactory;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
@ -78,7 +77,6 @@ public class OpenShiftProject extends KubernetesNamespace {
|
||||||
ingresses,
|
ingresses,
|
||||||
secrets,
|
secrets,
|
||||||
configMaps);
|
configMaps);
|
||||||
this.cheClientFactory = cheClientFactory;
|
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.routes = routes;
|
this.routes = routes;
|
||||||
this.cheServerOpenshiftClientFactory = cheServerOpenshiftClientFactory;
|
this.cheServerOpenshiftClientFactory = cheServerOpenshiftClientFactory;
|
||||||
|
|
@ -93,7 +91,6 @@ public class OpenShiftProject extends KubernetesNamespace {
|
||||||
String workspaceId) {
|
String workspaceId) {
|
||||||
super(clientFactory, cheClientFactory, executor, name, workspaceId);
|
super(clientFactory, cheClientFactory, executor, name, workspaceId);
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.cheClientFactory = cheClientFactory;
|
|
||||||
this.routes = new OpenShiftRoutes(name, workspaceId, clientFactory);
|
this.routes = new OpenShiftRoutes(name, workspaceId, clientFactory);
|
||||||
this.cheServerOpenshiftClientFactory = cheServerOpenshiftClientFactory;
|
this.cheServerOpenshiftClientFactory = cheServerOpenshiftClientFactory;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,23 +16,18 @@ import static java.lang.String.format;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
|
||||||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
|
||||||
import io.fabric8.kubernetes.api.model.ObjectMeta;
|
import io.fabric8.kubernetes.api.model.ObjectMeta;
|
||||||
import io.fabric8.kubernetes.api.model.Secret;
|
|
||||||
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
|
||||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||||
import io.fabric8.openshift.api.model.Project;
|
import io.fabric8.openshift.api.model.Project;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import org.eclipse.che.api.core.model.workspace.Workspace;
|
import org.eclipse.che.api.core.model.workspace.Workspace;
|
||||||
|
|
@ -47,11 +42,11 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesCl
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
|
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.api.shared.KubernetesNamespaceMeta;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
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;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.CheServerOpenshiftClientFactory;
|
import org.eclipse.che.workspace.infrastructure.openshift.CheServerOpenshiftClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.Constants;
|
import org.eclipse.che.workspace.infrastructure.openshift.Constants;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenShiftStopWorkspaceRoleProvisioner;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
@ -67,14 +62,11 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory {
|
||||||
private final boolean initWithCheServerSa;
|
private final boolean initWithCheServerSa;
|
||||||
private final OpenShiftClientFactory clientFactory;
|
private final OpenShiftClientFactory clientFactory;
|
||||||
private final CheServerOpenshiftClientFactory cheOpenShiftClientFactory;
|
private final CheServerOpenshiftClientFactory cheOpenShiftClientFactory;
|
||||||
private final OpenShiftStopWorkspaceRoleProvisioner stopWorkspaceRoleProvisioner;
|
|
||||||
|
|
||||||
private final String oAuthIdentityProvider;
|
private final String oAuthIdentityProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public OpenShiftProjectFactory(
|
public OpenShiftProjectFactory(
|
||||||
@Nullable @Named("che.infra.kubernetes.service_account_name") String serviceAccountName,
|
|
||||||
@Nullable @Named("che.infra.kubernetes.workspace_sa_cluster_roles") String clusterRoleNames,
|
|
||||||
@Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName,
|
@Nullable @Named("che.infra.kubernetes.namespace.default") String defaultNamespaceName,
|
||||||
@Named("che.infra.kubernetes.namespace.creation_allowed") boolean namespaceCreationAllowed,
|
@Named("che.infra.kubernetes.namespace.creation_allowed") boolean namespaceCreationAllowed,
|
||||||
@Named("che.infra.kubernetes.namespace.label") boolean labelProjects,
|
@Named("che.infra.kubernetes.namespace.label") boolean labelProjects,
|
||||||
|
|
@ -82,24 +74,23 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory {
|
||||||
@Named("che.infra.kubernetes.namespace.labels") String projectLabels,
|
@Named("che.infra.kubernetes.namespace.labels") String projectLabels,
|
||||||
@Named("che.infra.kubernetes.namespace.annotations") String projectAnnotations,
|
@Named("che.infra.kubernetes.namespace.annotations") String projectAnnotations,
|
||||||
@Named("che.infra.openshift.project.init_with_server_sa") boolean initWithCheServerSa,
|
@Named("che.infra.openshift.project.init_with_server_sa") boolean initWithCheServerSa,
|
||||||
|
Set<NamespaceConfigurator> namespaceConfigurators,
|
||||||
OpenShiftClientFactory clientFactory,
|
OpenShiftClientFactory clientFactory,
|
||||||
CheServerKubernetesClientFactory cheClientFactory,
|
CheServerKubernetesClientFactory cheClientFactory,
|
||||||
CheServerOpenshiftClientFactory cheOpenShiftClientFactory,
|
CheServerOpenshiftClientFactory cheOpenShiftClientFactory,
|
||||||
OpenShiftStopWorkspaceRoleProvisioner stopWorkspaceRoleProvisioner,
|
|
||||||
UserManager userManager,
|
UserManager userManager,
|
||||||
PreferenceManager preferenceManager,
|
PreferenceManager preferenceManager,
|
||||||
KubernetesSharedPool sharedPool,
|
KubernetesSharedPool sharedPool,
|
||||||
@Nullable @Named("che.infra.openshift.oauth_identity_provider")
|
@Nullable @Named("che.infra.openshift.oauth_identity_provider")
|
||||||
String oAuthIdentityProvider) {
|
String oAuthIdentityProvider) {
|
||||||
super(
|
super(
|
||||||
serviceAccountName,
|
|
||||||
clusterRoleNames,
|
|
||||||
defaultNamespaceName,
|
defaultNamespaceName,
|
||||||
namespaceCreationAllowed,
|
namespaceCreationAllowed,
|
||||||
labelProjects,
|
labelProjects,
|
||||||
annotateProjects,
|
annotateProjects,
|
||||||
projectLabels,
|
projectLabels,
|
||||||
projectAnnotations,
|
projectAnnotations,
|
||||||
|
namespaceConfigurators,
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
userManager,
|
userManager,
|
||||||
|
|
@ -108,15 +99,16 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory {
|
||||||
this.initWithCheServerSa = initWithCheServerSa;
|
this.initWithCheServerSa = initWithCheServerSa;
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.cheOpenShiftClientFactory = cheOpenShiftClientFactory;
|
this.cheOpenShiftClientFactory = cheOpenShiftClientFactory;
|
||||||
this.stopWorkspaceRoleProvisioner = stopWorkspaceRoleProvisioner;
|
|
||||||
this.oAuthIdentityProvider = oAuthIdentityProvider;
|
this.oAuthIdentityProvider = oAuthIdentityProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenShiftProject getOrCreate(RuntimeIdentity identity) throws InfrastructureException {
|
public OpenShiftProject getOrCreate(RuntimeIdentity identity) throws InfrastructureException {
|
||||||
OpenShiftProject osProject = get(identity);
|
OpenShiftProject osProject = get(identity);
|
||||||
|
|
||||||
|
var subject = EnvironmentContext.getCurrent().getSubject();
|
||||||
NamespaceResolutionContext resolutionCtx =
|
NamespaceResolutionContext resolutionCtx =
|
||||||
new NamespaceResolutionContext(EnvironmentContext.getCurrent().getSubject());
|
new NamespaceResolutionContext(
|
||||||
|
identity.getWorkspaceId(), subject.getUserId(), subject.getUserName());
|
||||||
Map<String, String> namespaceAnnotationsEvaluated =
|
Map<String, String> namespaceAnnotationsEvaluated =
|
||||||
evaluateAnnotationPlaceholders(resolutionCtx);
|
evaluateAnnotationPlaceholders(resolutionCtx);
|
||||||
|
|
||||||
|
|
@ -126,50 +118,8 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory {
|
||||||
labelNamespaces ? namespaceLabels : emptyMap(),
|
labelNamespaces ? namespaceLabels : emptyMap(),
|
||||||
annotateNamespaces ? namespaceAnnotationsEvaluated : emptyMap());
|
annotateNamespaces ? namespaceAnnotationsEvaluated : emptyMap());
|
||||||
|
|
||||||
// create credentials secret
|
configureNamespace(resolutionCtx, osProject.getName());
|
||||||
if (osProject
|
|
||||||
.secrets()
|
|
||||||
.get()
|
|
||||||
.stream()
|
|
||||||
.noneMatch(s -> s.getMetadata().getName().equals(CREDENTIALS_SECRET_NAME))) {
|
|
||||||
Secret secret =
|
|
||||||
new SecretBuilder()
|
|
||||||
.withType("opaque")
|
|
||||||
.withNewMetadata()
|
|
||||||
.withName(CREDENTIALS_SECRET_NAME)
|
|
||||||
.endMetadata()
|
|
||||||
.build();
|
|
||||||
clientFactory
|
|
||||||
.createOC()
|
|
||||||
.secrets()
|
|
||||||
.inNamespace(identity.getInfrastructureNamespace())
|
|
||||||
.create(secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create preferences configmap
|
|
||||||
if (osProject.configMaps().get(PREFERENCES_CONFIGMAP_NAME).isEmpty()) {
|
|
||||||
ConfigMap configMap =
|
|
||||||
new ConfigMapBuilder()
|
|
||||||
.withNewMetadata()
|
|
||||||
.withName(PREFERENCES_CONFIGMAP_NAME)
|
|
||||||
.endMetadata()
|
|
||||||
.build();
|
|
||||||
clientFactory
|
|
||||||
.createOC()
|
|
||||||
.configMaps()
|
|
||||||
.inNamespace(identity.getInfrastructureNamespace())
|
|
||||||
.create(configMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNullOrEmpty(getServiceAccountName())) {
|
|
||||||
OpenShiftWorkspaceServiceAccount osWorkspaceServiceAccount =
|
|
||||||
doCreateServiceAccount(osProject.getWorkspaceId(), osProject.getName());
|
|
||||||
osWorkspaceServiceAccount.prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNullOrEmpty(oAuthIdentityProvider)) {
|
|
||||||
stopWorkspaceRoleProvisioner.provision(osProject.getName());
|
|
||||||
}
|
|
||||||
return osProject;
|
return osProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,11 +140,6 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean checkNamespaceExists(String namespaceName) throws InfrastructureException {
|
|
||||||
return fetchNamespaceObject(namespaceName).isPresent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a kubernetes namespace for the specified workspace.
|
* Creates a kubernetes namespace for the specified workspace.
|
||||||
*
|
*
|
||||||
|
|
@ -218,12 +163,6 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory {
|
||||||
workspaceId);
|
workspaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
OpenShiftWorkspaceServiceAccount doCreateServiceAccount(String workspaceId, String projectName) {
|
|
||||||
return new OpenShiftWorkspaceServiceAccount(
|
|
||||||
workspaceId, projectName, getServiceAccountName(), getClusterRoleNames(), clientFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
public Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory
|
||||||
* @see
|
* @see
|
||||||
* org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesWorkspaceServiceAccount
|
* org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesWorkspaceServiceAccount
|
||||||
*/
|
*/
|
||||||
class OpenShiftWorkspaceServiceAccount
|
public class OpenShiftWorkspaceServiceAccount
|
||||||
extends AbstractWorkspaceServiceAccount<OpenShiftClient, Role, RoleBinding> {
|
extends AbstractWorkspaceServiceAccount<OpenShiftClient, Role, RoleBinding> {
|
||||||
|
|
||||||
OpenShiftWorkspaceServiceAccount(
|
public OpenShiftWorkspaceServiceAccount(
|
||||||
String workspaceId,
|
String workspaceId,
|
||||||
String projectName,
|
String projectName,
|
||||||
String serviceAccountName,
|
String serviceAccountName,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Red Hat, Inc. - initial API and implementation
|
* Red Hat, Inc. - initial API and implementation
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.workspace.infrastructure.openshift.provision;
|
package org.eclipse.che.workspace.infrastructure.openshift.project.configurator;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
|
||||||
import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder;
|
import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder;
|
||||||
import io.fabric8.openshift.api.model.PolicyRuleBuilder;
|
import io.fabric8.openshift.api.model.PolicyRuleBuilder;
|
||||||
|
|
@ -20,8 +22,12 @@ import io.fabric8.openshift.api.model.RoleBuilder;
|
||||||
import io.fabric8.openshift.client.OpenShiftClient;
|
import io.fabric8.openshift.client.OpenShiftClient;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.CheInstallationLocation;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.CheInstallationLocation;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
@ -32,27 +38,37 @@ import org.slf4j.LoggerFactory;
|
||||||
*
|
*
|
||||||
* @author Tom George
|
* @author Tom George
|
||||||
*/
|
*/
|
||||||
public class OpenShiftStopWorkspaceRoleProvisioner {
|
@Singleton
|
||||||
|
public class OpenShiftStopWorkspaceRoleConfigurator implements NamespaceConfigurator {
|
||||||
|
|
||||||
private final OpenShiftClientFactory clientFactory;
|
private final OpenShiftClientFactory clientFactory;
|
||||||
private final String installationLocation;
|
private final String installationLocation;
|
||||||
private final boolean stopWorkspaceRoleEnabled;
|
private final boolean stopWorkspaceRoleEnabled;
|
||||||
|
private final String oAuthIdentityProvider;
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
LoggerFactory.getLogger(OpenShiftStopWorkspaceRoleProvisioner.class);
|
LoggerFactory.getLogger(OpenShiftStopWorkspaceRoleConfigurator.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public OpenShiftStopWorkspaceRoleProvisioner(
|
public OpenShiftStopWorkspaceRoleConfigurator(
|
||||||
OpenShiftClientFactory clientFactory,
|
OpenShiftClientFactory clientFactory,
|
||||||
CheInstallationLocation installationLocation,
|
CheInstallationLocation installationLocation,
|
||||||
@Named("che.workspace.stop.role.enabled") boolean stopWorkspaceRoleEnabled)
|
@Named("che.workspace.stop.role.enabled") boolean stopWorkspaceRoleEnabled,
|
||||||
|
@Nullable @Named("che.infra.openshift.oauth_identity_provider") String oAuthIdentityProvider)
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.installationLocation = installationLocation.getInstallationLocationNamespace();
|
this.installationLocation = installationLocation.getInstallationLocationNamespace();
|
||||||
this.stopWorkspaceRoleEnabled = stopWorkspaceRoleEnabled;
|
this.stopWorkspaceRoleEnabled = stopWorkspaceRoleEnabled;
|
||||||
|
this.oAuthIdentityProvider = oAuthIdentityProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void provision(String projectName) throws InfrastructureException {
|
@Override
|
||||||
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String projectName)
|
||||||
|
throws InfrastructureException {
|
||||||
|
if (isNullOrEmpty(oAuthIdentityProvider)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (stopWorkspaceRoleEnabled && installationLocation != null) {
|
if (stopWorkspaceRoleEnabled && installationLocation != null) {
|
||||||
OpenShiftClient osClient = clientFactory.createOC();
|
OpenShiftClient osClient = clientFactory.createOC();
|
||||||
String stopWorkspacesRoleName = "workspace-stop";
|
String stopWorkspacesRoleName = "workspace-stop";
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.project.configurator;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import java.util.Collections;
|
||||||
|
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.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.openshift.project.OpenShiftWorkspaceServiceAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link NamespaceConfigurator} ensures that workspace ServiceAccount with proper ClusterRole
|
||||||
|
* is set in Workspace project.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class OpenShiftWorkspaceServiceAccountConfigurator implements NamespaceConfigurator {
|
||||||
|
|
||||||
|
private final OpenShiftClientFactory clientFactory;
|
||||||
|
|
||||||
|
private final String serviceAccountName;
|
||||||
|
private final Set<String> clusterRoleNames;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public OpenShiftWorkspaceServiceAccountConfigurator(
|
||||||
|
@Nullable @Named("che.infra.kubernetes.service_account_name") String serviceAccountName,
|
||||||
|
@Nullable @Named("che.infra.kubernetes.workspace_sa_cluster_roles") String clusterRoleNames,
|
||||||
|
OpenShiftClientFactory clientFactory) {
|
||||||
|
this.clientFactory = clientFactory;
|
||||||
|
this.serviceAccountName = serviceAccountName;
|
||||||
|
if (!isNullOrEmpty(clusterRoleNames)) {
|
||||||
|
this.clusterRoleNames =
|
||||||
|
Sets.newHashSet(
|
||||||
|
Splitter.on(",").trimResults().omitEmptyStrings().split(clusterRoleNames));
|
||||||
|
} else {
|
||||||
|
this.clusterRoleNames = Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
|
||||||
|
throws InfrastructureException {
|
||||||
|
if (!isNullOrEmpty(serviceAccountName)) {
|
||||||
|
OpenShiftWorkspaceServiceAccount osWorkspaceServiceAccount =
|
||||||
|
createServiceAccount(namespaceResolutionContext.getWorkspaceId(), namespaceName);
|
||||||
|
osWorkspaceServiceAccount.prepare();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public OpenShiftWorkspaceServiceAccount createServiceAccount(String wsId, String namespaceName) {
|
||||||
|
return new OpenShiftWorkspaceServiceAccount(
|
||||||
|
wsId, namespaceName, serviceAccountName, clusterRoleNames, clientFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,9 +11,9 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
|
package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
|
||||||
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.AUTH_SERVER_URL_SETTING;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
|
package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.testng.Assert.*;
|
import static org.testng.Assert.*;
|
||||||
|
|
@ -21,11 +19,6 @@ import io.fabric8.kubernetes.api.model.ObjectMeta;
|
||||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||||
import io.fabric8.openshift.api.model.User;
|
import io.fabric8.openshift.api.model.User;
|
||||||
import io.fabric8.openshift.client.OpenShiftClient;
|
import io.fabric8.openshift.client.OpenShiftClient;
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.eclipse.che.api.core.ConflictException;
|
import org.eclipse.che.api.core.ConflictException;
|
||||||
import org.eclipse.che.api.core.ServerException;
|
import org.eclipse.che.api.core.ServerException;
|
||||||
|
|
@ -55,10 +48,6 @@ public class OpenshiftTokenInitializationFilterTest {
|
||||||
@Mock private User openshiftUser;
|
@Mock private User openshiftUser;
|
||||||
@Mock private ObjectMeta openshiftUserMeta;
|
@Mock private ObjectMeta openshiftUserMeta;
|
||||||
|
|
||||||
@Mock private HttpServletRequest servletRequest;
|
|
||||||
@Mock private HttpServletResponse servletResponse;
|
|
||||||
@Mock private FilterChain filterChain;
|
|
||||||
|
|
||||||
private static final String TOKEN = "touken";
|
private static final String TOKEN = "touken";
|
||||||
private static final String USER_UID = "almost-certainly-unique-id";
|
private static final String USER_UID = "almost-certainly-unique-id";
|
||||||
private static final String USERNAME = "test_username";
|
private static final String USERNAME = "test_username";
|
||||||
|
|
@ -111,7 +100,7 @@ public class OpenshiftTokenInitializationFilterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void extractSubjectCreatesSubjectWithCurrentlyAuthenticatedUser()
|
public void extractSubjectCreatesSubjectWithCurrentlyAuthenticatedUser()
|
||||||
throws InfrastructureException, ServerException, ConflictException {
|
throws ServerException, ConflictException {
|
||||||
when(openShiftClientFactory.createAuthenticatedClient(TOKEN)).thenReturn(openShiftClient);
|
when(openShiftClientFactory.createAuthenticatedClient(TOKEN)).thenReturn(openShiftClient);
|
||||||
when(openShiftClient.currentUser()).thenReturn(openshiftUser);
|
when(openShiftClient.currentUser()).thenReturn(openshiftUser);
|
||||||
when(openshiftUser.getMetadata()).thenReturn(openshiftUserMeta);
|
when(openshiftUser.getMetadata()).thenReturn(openshiftUserMeta);
|
||||||
|
|
@ -128,27 +117,6 @@ public class OpenshiftTokenInitializationFilterTest {
|
||||||
assertEquals(subject.getUserName(), USERNAME);
|
assertEquals(subject.getUserName(), USERNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handleMissingTokenShouldAllowUnauthorizedEndpoint()
|
|
||||||
throws ServletException, IOException {
|
|
||||||
when(servletRequest.getServletPath()).thenReturn("/system/state");
|
|
||||||
|
|
||||||
openshiftTokenInitializationFilter.handleMissingToken(
|
|
||||||
servletRequest, servletResponse, filterChain);
|
|
||||||
|
|
||||||
verify(filterChain).doFilter(servletRequest, servletResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handleMissingTokenShouldRejectRequest() throws ServletException, IOException {
|
|
||||||
when(servletRequest.getServletPath()).thenReturn("blabol");
|
|
||||||
|
|
||||||
openshiftTokenInitializationFilter.handleMissingToken(
|
|
||||||
servletRequest, servletResponse, filterChain);
|
|
||||||
|
|
||||||
verify(servletResponse).sendError(eq(401), anyString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invalidTokenShouldBeHandledAsMissing() throws Exception {
|
public void invalidTokenShouldBeHandledAsMissing() throws Exception {
|
||||||
when(openShiftClientFactory.createAuthenticatedClient(TOKEN)).thenReturn(openShiftClient);
|
when(openShiftClientFactory.createAuthenticatedClient(TOKEN)).thenReturn(openShiftClient);
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ package org.eclipse.che.workspace.infrastructure.openshift.project;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Optional.empty;
|
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.DEFAULT_ATTRIBUTE;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.DEFAULT_ATTRIBUTE;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.PHASE_ATTRIBUTE;
|
||||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.CREDENTIALS_SECRET_NAME;
|
||||||
|
|
@ -32,7 +32,6 @@ import static org.mockito.Mockito.lenient;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
@ -58,7 +57,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Set;
|
||||||
import org.eclipse.che.api.core.ValidationException;
|
import org.eclipse.che.api.core.ValidationException;
|
||||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||||
import org.eclipse.che.api.user.server.PreferenceManager;
|
import org.eclipse.che.api.user.server.PreferenceManager;
|
||||||
|
|
@ -76,12 +75,16 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesCl
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
|
||||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps;
|
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.KubernetesSecrets;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.CredentialsSecretConfigurator;
|
||||||
|
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.util.KubernetesSharedPool;
|
import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.CheServerOpenshiftClientFactory;
|
import org.eclipse.che.workspace.infrastructure.openshift.CheServerOpenshiftClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
||||||
import org.eclipse.che.workspace.infrastructure.openshift.provision.OpenShiftStopWorkspaceRoleProvisioner;
|
import org.eclipse.che.workspace.infrastructure.openshift.project.configurator.OpenShiftWorkspaceServiceAccountConfigurator;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.testng.MockitoTestNGListener;
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
import org.testng.Assert;
|
import org.testng.Assert;
|
||||||
import org.testng.annotations.AfterMethod;
|
import org.testng.annotations.AfterMethod;
|
||||||
|
|
@ -110,7 +113,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
@Mock private OpenShiftClientFactory clientFactory;
|
@Mock private OpenShiftClientFactory clientFactory;
|
||||||
@Mock private CheServerKubernetesClientFactory cheClientFactory;
|
@Mock private CheServerKubernetesClientFactory cheClientFactory;
|
||||||
@Mock private CheServerOpenshiftClientFactory cheServerOpenshiftClientFactory;
|
@Mock private CheServerOpenshiftClientFactory cheServerOpenshiftClientFactory;
|
||||||
@Mock private OpenShiftStopWorkspaceRoleProvisioner stopWorkspaceRoleProvisioner;
|
|
||||||
@Mock private WorkspaceManager workspaceManager;
|
@Mock private WorkspaceManager workspaceManager;
|
||||||
@Mock private UserManager userManager;
|
@Mock private UserManager userManager;
|
||||||
@Mock private PreferenceManager preferenceManager;
|
@Mock private PreferenceManager preferenceManager;
|
||||||
|
|
@ -131,6 +133,7 @@ public class OpenShiftProjectFactoryTest {
|
||||||
@BeforeMethod
|
@BeforeMethod
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
lenient().when(clientFactory.createOC()).thenReturn(osClient);
|
lenient().when(clientFactory.createOC()).thenReturn(osClient);
|
||||||
|
lenient().when(clientFactory.create()).thenReturn(osClient);
|
||||||
lenient().when(osClient.projects()).thenReturn(projectOperation);
|
lenient().when(osClient.projects()).thenReturn(projectOperation);
|
||||||
|
|
||||||
lenient()
|
lenient()
|
||||||
|
|
@ -162,8 +165,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -171,10 +172,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -195,8 +196,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
System.out.println("2--------");
|
System.out.println("2--------");
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -204,10 +203,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -228,8 +227,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -237,10 +234,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -266,8 +263,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -275,10 +270,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -305,8 +300,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -314,10 +307,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -340,8 +333,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -349,10 +340,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -385,8 +376,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -394,10 +383,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -424,8 +413,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -433,10 +420,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -463,8 +450,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -472,10 +457,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -492,8 +477,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
throwOnTryToGetProjectsList(new KubernetesClientException("connection refused"));
|
throwOnTryToGetProjectsList(new KubernetesClientException("connection refused"));
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<username>-che",
|
"<username>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -501,10 +484,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -526,8 +509,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
projectFactory =
|
projectFactory =
|
||||||
spy(
|
spy(
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -535,10 +516,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -554,7 +535,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(toReturnProject, project);
|
assertEquals(toReturnProject, project);
|
||||||
verify(projectFactory, never()).doCreateServiceAccount(any(), any());
|
|
||||||
verify(toReturnProject).prepare(eq(false), eq(false), any(), any());
|
verify(toReturnProject).prepare(eq(false), eq(false), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -564,8 +544,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
projectFactory =
|
projectFactory =
|
||||||
spy(
|
spy(
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -573,30 +551,28 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
Set.of(new CredentialsSecretConfigurator(clientFactory)),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
NO_OAUTH_IDENTITY_PROVIDER));
|
NO_OAUTH_IDENTITY_PROVIDER));
|
||||||
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
||||||
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
||||||
|
when(toReturnProject.getName()).thenReturn("namespace123");
|
||||||
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
||||||
MixedOperation mixedOperation = mock(MixedOperation.class);
|
MixedOperation mixedOperation = mock(MixedOperation.class);
|
||||||
KubernetesSecrets secrets = mock(KubernetesSecrets.class);
|
when(osClient.secrets()).thenReturn(mixedOperation);
|
||||||
KubernetesConfigsMaps configsMaps = mock(KubernetesConfigsMaps.class);
|
when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
||||||
when(toReturnProject.secrets()).thenReturn(secrets);
|
Resource<Secret> nullSecret = mock(Resource.class);
|
||||||
when(toReturnProject.configMaps()).thenReturn(configsMaps);
|
when(namespaceOperation.withName(CREDENTIALS_SECRET_NAME)).thenReturn(nullSecret);
|
||||||
when(secrets.get()).thenReturn(Collections.emptyList());
|
when(nullSecret.get()).thenReturn(null);
|
||||||
when(configsMaps.get(anyString())).thenReturn(Optional.of(mock(ConfigMap.class)));
|
|
||||||
lenient().when(osClient.secrets()).thenReturn(mixedOperation);
|
|
||||||
lenient().when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
new RuntimeIdentityImpl("workspace123", null, USER_ID, "workspace123");
|
new RuntimeIdentityImpl("workspace123", null, USER_ID, "namespace123");
|
||||||
projectFactory.getOrCreate(identity);
|
projectFactory.getOrCreate(identity);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
|
@ -613,8 +589,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
projectFactory =
|
projectFactory =
|
||||||
spy(
|
spy(
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -622,31 +596,23 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
Set.of(new PreferencesConfigMapConfigurator(clientFactory)),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
NO_OAUTH_IDENTITY_PROVIDER));
|
NO_OAUTH_IDENTITY_PROVIDER));
|
||||||
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
||||||
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
||||||
|
when(toReturnProject.getName()).thenReturn("namespace123");
|
||||||
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
||||||
MixedOperation mixedOperation = mock(MixedOperation.class);
|
MixedOperation mixedOperation = mock(MixedOperation.class);
|
||||||
KubernetesSecrets secrets = mock(KubernetesSecrets.class);
|
when(osClient.configMaps()).thenReturn(mixedOperation);
|
||||||
Secret secret = mock(Secret.class);
|
when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
||||||
ObjectMeta objectMeta = mock(ObjectMeta.class);
|
Resource<ConfigMap> nullCm = mock(Resource.class);
|
||||||
when(secret.getMetadata()).thenReturn(objectMeta);
|
when(namespaceOperation.withName(PREFERENCES_CONFIGMAP_NAME)).thenReturn(nullCm);
|
||||||
when(objectMeta.getName()).thenReturn(CREDENTIALS_SECRET_NAME);
|
|
||||||
when(toReturnProject.secrets()).thenReturn(secrets);
|
|
||||||
when(secrets.get()).thenReturn(singletonList(secret));
|
|
||||||
lenient().when(osClient.secrets()).thenReturn(mixedOperation);
|
|
||||||
KubernetesConfigsMaps configsMaps = mock(KubernetesConfigsMaps.class);
|
|
||||||
when(toReturnProject.configMaps()).thenReturn(configsMaps);
|
|
||||||
when(configsMaps.get(eq(PREFERENCES_CONFIGMAP_NAME))).thenReturn(empty());
|
|
||||||
lenient().when(osClient.configMaps()).thenReturn(mixedOperation);
|
|
||||||
lenient().when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -666,8 +632,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
projectFactory =
|
projectFactory =
|
||||||
spy(
|
spy(
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -675,10 +639,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
Set.of(new CredentialsSecretConfigurator(clientFactory)),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -686,10 +650,14 @@ public class OpenShiftProjectFactoryTest {
|
||||||
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
||||||
prepareProject(toReturnProject);
|
prepareProject(toReturnProject);
|
||||||
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
||||||
|
when(toReturnProject.getName()).thenReturn("namespace123");
|
||||||
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
||||||
MixedOperation mixedOperation = mock(MixedOperation.class);
|
MixedOperation mixedOperation = mock(MixedOperation.class);
|
||||||
lenient().when(osClient.secrets()).thenReturn(mixedOperation);
|
when(osClient.secrets()).thenReturn(mixedOperation);
|
||||||
lenient().when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
||||||
|
Resource<Secret> secretResource = mock(Resource.class);
|
||||||
|
when(namespaceOperation.withName(CREDENTIALS_SECRET_NAME)).thenReturn(secretResource);
|
||||||
|
when(secretResource.get()).thenReturn(mock(Secret.class));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -706,8 +674,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
projectFactory =
|
projectFactory =
|
||||||
spy(
|
spy(
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -715,10 +681,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
Set.of(new PreferencesConfigMapConfigurator(clientFactory)),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -726,10 +692,14 @@ public class OpenShiftProjectFactoryTest {
|
||||||
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
||||||
prepareProject(toReturnProject);
|
prepareProject(toReturnProject);
|
||||||
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
||||||
|
when(toReturnProject.getName()).thenReturn("namespace123");
|
||||||
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
NonNamespaceOperation namespaceOperation = mock(NonNamespaceOperation.class);
|
||||||
MixedOperation mixedOperation = mock(MixedOperation.class);
|
MixedOperation mixedOperation = mock(MixedOperation.class);
|
||||||
lenient().when(osClient.configMaps()).thenReturn(mixedOperation);
|
when(osClient.configMaps()).thenReturn(mixedOperation);
|
||||||
lenient().when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
when(mixedOperation.inNamespace(anyString())).thenReturn(namespaceOperation);
|
||||||
|
Resource<ConfigMap> cmResource = mock(Resource.class);
|
||||||
|
when(namespaceOperation.withName(PREFERENCES_CONFIGMAP_NAME)).thenReturn(cmResource);
|
||||||
|
when(cmResource.get()).thenReturn(mock(ConfigMap.class));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -740,56 +710,13 @@ public class OpenShiftProjectFactoryTest {
|
||||||
verify(namespaceOperation, never()).create(any());
|
verify(namespaceOperation, never()).create(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldPrepareWorkspaceServiceAccountIfItIsConfiguredAndProjectIsNotPredefined()
|
|
||||||
throws Exception {
|
|
||||||
// given
|
|
||||||
projectFactory =
|
|
||||||
spy(
|
|
||||||
new OpenShiftProjectFactory(
|
|
||||||
"serviceAccount",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
NAMESPACE_LABELS,
|
|
||||||
NAMESPACE_ANNOTATIONS,
|
|
||||||
true,
|
|
||||||
clientFactory,
|
|
||||||
cheClientFactory,
|
|
||||||
cheServerOpenshiftClientFactory,
|
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
|
||||||
preferenceManager,
|
|
||||||
pool,
|
|
||||||
NO_OAUTH_IDENTITY_PROVIDER));
|
|
||||||
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
|
||||||
prepareProject(toReturnProject);
|
|
||||||
when(toReturnProject.getWorkspaceId()).thenReturn("workspace123");
|
|
||||||
when(toReturnProject.getName()).thenReturn("workspace123");
|
|
||||||
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
|
||||||
|
|
||||||
OpenShiftWorkspaceServiceAccount serviceAccount = mock(OpenShiftWorkspaceServiceAccount.class);
|
|
||||||
doReturn(serviceAccount).when(projectFactory).doCreateServiceAccount(any(), any());
|
|
||||||
|
|
||||||
// when
|
|
||||||
RuntimeIdentity identity =
|
|
||||||
new RuntimeIdentityImpl("workspace123", null, USER_ID, "workspace123");
|
|
||||||
projectFactory.getOrCreate(identity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
verify(projectFactory).doCreateServiceAccount("workspace123", "workspace123");
|
|
||||||
verify(serviceAccount).prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCallStopWorkspaceRoleProvisionWhenIdentityProviderIsDefined() throws Exception {
|
public void shouldCallStopWorkspaceRoleProvisionWhenIdentityProviderIsDefined() throws Exception {
|
||||||
|
var saConf =
|
||||||
|
spy(new OpenShiftWorkspaceServiceAccountConfigurator("serviceAccount", "", clientFactory));
|
||||||
projectFactory =
|
projectFactory =
|
||||||
spy(
|
spy(
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"serviceAccount",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -797,22 +724,21 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
Set.of(saConf),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
OAUTH_IDENTITY_PROVIDER));
|
OAUTH_IDENTITY_PROVIDER));
|
||||||
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
||||||
when(toReturnProject.getWorkspaceId()).thenReturn("workspace123");
|
|
||||||
when(toReturnProject.getName()).thenReturn("workspace123");
|
when(toReturnProject.getName()).thenReturn("workspace123");
|
||||||
prepareProject(toReturnProject);
|
prepareProject(toReturnProject);
|
||||||
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
||||||
|
|
||||||
OpenShiftWorkspaceServiceAccount serviceAccount = mock(OpenShiftWorkspaceServiceAccount.class);
|
OpenShiftWorkspaceServiceAccount serviceAccount = mock(OpenShiftWorkspaceServiceAccount.class);
|
||||||
doReturn(serviceAccount).when(projectFactory).doCreateServiceAccount(any(), any());
|
doReturn(serviceAccount).when(saConf).createServiceAccount("workspace123", "workspace123");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RuntimeIdentity identity =
|
RuntimeIdentity identity =
|
||||||
|
|
@ -820,52 +746,7 @@ public class OpenShiftProjectFactoryTest {
|
||||||
projectFactory.getOrCreate(identity);
|
projectFactory.getOrCreate(identity);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(projectFactory).doCreateServiceAccount("workspace123", "workspace123");
|
|
||||||
verify(serviceAccount).prepare();
|
verify(serviceAccount).prepare();
|
||||||
verify(stopWorkspaceRoleProvisioner, times(1)).provision("workspace123");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldNotCallStopWorkspaceRoleProvisionWhenIdentityProviderIsDefined()
|
|
||||||
throws Exception {
|
|
||||||
projectFactory =
|
|
||||||
spy(
|
|
||||||
new OpenShiftProjectFactory(
|
|
||||||
"serviceAccount",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
NAMESPACE_LABELS,
|
|
||||||
NAMESPACE_ANNOTATIONS,
|
|
||||||
true,
|
|
||||||
clientFactory,
|
|
||||||
cheClientFactory,
|
|
||||||
cheServerOpenshiftClientFactory,
|
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
|
||||||
preferenceManager,
|
|
||||||
pool,
|
|
||||||
NO_OAUTH_IDENTITY_PROVIDER));
|
|
||||||
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
|
||||||
prepareProject(toReturnProject);
|
|
||||||
when(toReturnProject.getWorkspaceId()).thenReturn("workspace123");
|
|
||||||
when(toReturnProject.getName()).thenReturn("workspace123");
|
|
||||||
doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any());
|
|
||||||
|
|
||||||
OpenShiftWorkspaceServiceAccount serviceAccount = mock(OpenShiftWorkspaceServiceAccount.class);
|
|
||||||
doReturn(serviceAccount).when(projectFactory).doCreateServiceAccount(any(), any());
|
|
||||||
|
|
||||||
// when
|
|
||||||
RuntimeIdentity identity =
|
|
||||||
new RuntimeIdentityImpl("workspace123", null, USER_ID, "workspace123");
|
|
||||||
projectFactory.getOrCreate(identity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
verify(projectFactory).doCreateServiceAccount("workspace123", "workspace123");
|
|
||||||
verify(serviceAccount).prepare();
|
|
||||||
verify(stopWorkspaceRoleProvisioner, times(0)).provision("workspace123");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -886,8 +767,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -895,10 +774,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -921,8 +800,6 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
projectFactory =
|
projectFactory =
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -930,10 +807,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
"try_placeholder_here=<username>",
|
"try_placeholder_here=<username>",
|
||||||
NAMESPACE_ANNOTATIONS,
|
NAMESPACE_ANNOTATIONS,
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -946,13 +823,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUsernamePlaceholderInAnnotationsIsEvaluated() throws InfrastructureException {
|
public void testUsernamePlaceholderInAnnotationsIsEvaluated() throws InfrastructureException {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
projectFactory =
|
projectFactory =
|
||||||
spy(
|
spy(
|
||||||
new OpenShiftProjectFactory(
|
new OpenShiftProjectFactory(
|
||||||
"",
|
|
||||||
null,
|
|
||||||
"<userid>-che",
|
"<userid>-che",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
@ -960,10 +834,10 @@ public class OpenShiftProjectFactoryTest {
|
||||||
NAMESPACE_LABELS,
|
NAMESPACE_LABELS,
|
||||||
"try_placeholder_here=<username>",
|
"try_placeholder_here=<username>",
|
||||||
true,
|
true,
|
||||||
|
emptySet(),
|
||||||
clientFactory,
|
clientFactory,
|
||||||
cheClientFactory,
|
cheClientFactory,
|
||||||
cheServerOpenshiftClientFactory,
|
cheServerOpenshiftClientFactory,
|
||||||
stopWorkspaceRoleProvisioner,
|
|
||||||
userManager,
|
userManager,
|
||||||
preferenceManager,
|
preferenceManager,
|
||||||
pool,
|
pool,
|
||||||
|
|
@ -983,6 +857,51 @@ public class OpenShiftProjectFactoryTest {
|
||||||
.prepare(eq(false), eq(false), any(), eq(Map.of("try_placeholder_here", "jondoe")));
|
.prepare(eq(false), eq(false), any(), eq(Map.of("try_placeholder_here", "jondoe")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllConfiguratorsAreCalledWhenCreatingProject() throws InfrastructureException {
|
||||||
|
// given
|
||||||
|
String projectName = "testprojectname";
|
||||||
|
NamespaceConfigurator configurator1 = Mockito.mock(NamespaceConfigurator.class);
|
||||||
|
NamespaceConfigurator configurator2 = Mockito.mock(NamespaceConfigurator.class);
|
||||||
|
Set<NamespaceConfigurator> namespaceConfigurators = Set.of(configurator1, configurator2);
|
||||||
|
|
||||||
|
projectFactory =
|
||||||
|
spy(
|
||||||
|
new OpenShiftProjectFactory(
|
||||||
|
"<username>-che",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
NAMESPACE_LABELS,
|
||||||
|
"try_placeholder_here=<username>",
|
||||||
|
true,
|
||||||
|
namespaceConfigurators,
|
||||||
|
clientFactory,
|
||||||
|
cheClientFactory,
|
||||||
|
cheServerOpenshiftClientFactory,
|
||||||
|
userManager,
|
||||||
|
preferenceManager,
|
||||||
|
pool,
|
||||||
|
NO_OAUTH_IDENTITY_PROVIDER));
|
||||||
|
EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false));
|
||||||
|
|
||||||
|
OpenShiftProject toReturnProject = mock(OpenShiftProject.class);
|
||||||
|
when(toReturnProject.getName()).thenReturn(projectName);
|
||||||
|
|
||||||
|
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", null, USER_ID, "old-che");
|
||||||
|
doReturn(toReturnProject).when(projectFactory).get(identity);
|
||||||
|
|
||||||
|
// when
|
||||||
|
OpenShiftProject project = projectFactory.getOrCreate(identity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
NamespaceResolutionContext resolutionCtx =
|
||||||
|
new NamespaceResolutionContext("workspace123", "123", "jondoe");
|
||||||
|
verify(configurator1).configure(resolutionCtx, projectName);
|
||||||
|
verify(configurator2).configure(resolutionCtx, projectName);
|
||||||
|
assertEquals(project, toReturnProject);
|
||||||
|
}
|
||||||
|
|
||||||
private void prepareNamespaceToBeFoundByName(String name, Project project) throws Exception {
|
private void prepareNamespaceToBeFoundByName(String name, Project project) throws Exception {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Resource<Project> getProjectByNameOperation = mock(Resource.class);
|
Resource<Project> getProjectByNameOperation = mock(Resource.class);
|
||||||
|
|
@ -1010,15 +929,13 @@ public class OpenShiftProjectFactoryTest {
|
||||||
|
|
||||||
private void prepareProject(OpenShiftProject project) throws InfrastructureException {
|
private void prepareProject(OpenShiftProject project) throws InfrastructureException {
|
||||||
KubernetesSecrets secrets = mock(KubernetesSecrets.class);
|
KubernetesSecrets secrets = mock(KubernetesSecrets.class);
|
||||||
|
lenient().when(project.secrets()).thenReturn(secrets);
|
||||||
KubernetesConfigsMaps configsMaps = mock(KubernetesConfigsMaps.class);
|
KubernetesConfigsMaps configsMaps = mock(KubernetesConfigsMaps.class);
|
||||||
when(project.secrets()).thenReturn(secrets);
|
|
||||||
when(project.configMaps()).thenReturn(configsMaps);
|
|
||||||
when(configsMaps.get(anyString())).thenReturn(Optional.of(mock(ConfigMap.class)));
|
|
||||||
Secret secretMock = mock(Secret.class);
|
Secret secretMock = mock(Secret.class);
|
||||||
ObjectMeta objectMeta = mock(ObjectMeta.class);
|
ObjectMeta objectMeta = mock(ObjectMeta.class);
|
||||||
when(objectMeta.getName()).thenReturn(CREDENTIALS_SECRET_NAME);
|
lenient().when(objectMeta.getName()).thenReturn(CREDENTIALS_SECRET_NAME);
|
||||||
when(secretMock.getMetadata()).thenReturn(objectMeta);
|
lenient().when(secretMock.getMetadata()).thenReturn(objectMeta);
|
||||||
when(secrets.get()).thenReturn(singletonList(secretMock));
|
lenient().when(secrets.get()).thenReturn(Collections.singletonList(secretMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwOnTryToGetProjectsList(Throwable e) throws Exception {
|
private void throwOnTryToGetProjectsList(Throwable e) throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Red Hat, Inc. - initial API and implementation
|
* Red Hat, Inc. - initial API and implementation
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.workspace.infrastructure.openshift.provision;
|
package org.eclipse.che.workspace.infrastructure.openshift.project.configurator;
|
||||||
|
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.anyString;
|
import static org.mockito.Mockito.anyString;
|
||||||
|
|
@ -17,6 +17,7 @@ import static org.mockito.Mockito.lenient;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder;
|
import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder;
|
||||||
|
|
@ -42,15 +43,16 @@ import org.testng.annotations.Listeners;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for {@link OpenShiftStopWorkspaceRoleProvisioner}
|
* Test for {@link
|
||||||
|
* org.eclipse.che.workspace.infrastructure.openshift.project.configurator.OpenShiftStopWorkspaceRoleConfigurator}
|
||||||
*
|
*
|
||||||
* <p>#author Tom George
|
* <p>#author Tom George
|
||||||
*/
|
*/
|
||||||
@Listeners(MockitoTestNGListener.class)
|
@Listeners(MockitoTestNGListener.class)
|
||||||
public class OpenShiftStopWorkspaceRoleProvisionerTest {
|
public class OpenShiftStopWorkspaceRoleConfiguratorTest {
|
||||||
|
|
||||||
@Mock private CheInstallationLocation cheInstallationLocation;
|
@Mock private CheInstallationLocation cheInstallationLocation;
|
||||||
private OpenShiftStopWorkspaceRoleProvisioner stopWorkspaceRoleProvisioner;
|
private OpenShiftStopWorkspaceRoleConfigurator stopWorkspaceRoleProvisioner;
|
||||||
|
|
||||||
@Mock private OpenShiftClientFactory clientFactory;
|
@Mock private OpenShiftClientFactory clientFactory;
|
||||||
@Mock private OpenShiftClient osClient;
|
@Mock private OpenShiftClient osClient;
|
||||||
|
|
@ -123,7 +125,8 @@ public class OpenShiftStopWorkspaceRoleProvisionerTest {
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
lenient().when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn("che");
|
lenient().when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn("che");
|
||||||
stopWorkspaceRoleProvisioner =
|
stopWorkspaceRoleProvisioner =
|
||||||
new OpenShiftStopWorkspaceRoleProvisioner(clientFactory, cheInstallationLocation, true);
|
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||||
|
clientFactory, cheInstallationLocation, true, "yes");
|
||||||
lenient().when(clientFactory.createOC()).thenReturn(osClient);
|
lenient().when(clientFactory.createOC()).thenReturn(osClient);
|
||||||
lenient().when(osClient.roles()).thenReturn(mixedRoleOperation);
|
lenient().when(osClient.roles()).thenReturn(mixedRoleOperation);
|
||||||
lenient().when(osClient.roleBindings()).thenReturn(mixedRoleBindingOperation);
|
lenient().when(osClient.roleBindings()).thenReturn(mixedRoleBindingOperation);
|
||||||
|
|
@ -160,7 +163,7 @@ public class OpenShiftStopWorkspaceRoleProvisionerTest {
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateRoleAndRoleBindingWhenRoleDoesNotYetExist()
|
public void shouldCreateRoleAndRoleBindingWhenRoleDoesNotYetExist()
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
stopWorkspaceRoleProvisioner.provision("developer-che");
|
stopWorkspaceRoleProvisioner.configure(null, "developer-che");
|
||||||
verify(osClient, times(2)).roles();
|
verify(osClient, times(2)).roles();
|
||||||
verify(osClient.roles(), times(2)).inNamespace("developer-che");
|
verify(osClient.roles(), times(2)).inNamespace("developer-che");
|
||||||
verify(osClient.roles().inNamespace("developer-che")).withName("workspace-stop");
|
verify(osClient.roles().inNamespace("developer-che")).withName("workspace-stop");
|
||||||
|
|
@ -174,7 +177,7 @@ public class OpenShiftStopWorkspaceRoleProvisionerTest {
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateRoleBindingWhenRoleAlreadyExists() throws InfrastructureException {
|
public void shouldCreateRoleBindingWhenRoleAlreadyExists() throws InfrastructureException {
|
||||||
lenient().when(roleResource.get()).thenReturn(expectedRole);
|
lenient().when(roleResource.get()).thenReturn(expectedRole);
|
||||||
stopWorkspaceRoleProvisioner.provision("developer-che");
|
stopWorkspaceRoleProvisioner.configure(null, "developer-che");
|
||||||
verify(osClient, times(1)).roles();
|
verify(osClient, times(1)).roles();
|
||||||
verify(osClient).roleBindings();
|
verify(osClient).roleBindings();
|
||||||
verify(osClient.roleBindings()).inNamespace("developer-che");
|
verify(osClient.roleBindings()).inNamespace("developer-che");
|
||||||
|
|
@ -185,9 +188,10 @@ public class OpenShiftStopWorkspaceRoleProvisionerTest {
|
||||||
@Test
|
@Test
|
||||||
public void shouldNotCreateRoleBindingWhenStopWorkspaceRolePropertyIsDisabled()
|
public void shouldNotCreateRoleBindingWhenStopWorkspaceRolePropertyIsDisabled()
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
OpenShiftStopWorkspaceRoleProvisioner disabledStopWorkspaceRoleProvisioner =
|
OpenShiftStopWorkspaceRoleConfigurator disabledStopWorkspaceRoleProvisioner =
|
||||||
new OpenShiftStopWorkspaceRoleProvisioner(clientFactory, cheInstallationLocation, false);
|
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||||
disabledStopWorkspaceRoleProvisioner.provision("developer-che");
|
clientFactory, cheInstallationLocation, false, "yes");
|
||||||
|
disabledStopWorkspaceRoleProvisioner.configure(null, "developer-che");
|
||||||
verify(osClient, never()).roles();
|
verify(osClient, never()).roles();
|
||||||
verify(osClient, never()).roleBindings();
|
verify(osClient, never()).roleBindings();
|
||||||
verify(osClient.roleBindings(), never()).inNamespace("developer-che");
|
verify(osClient.roleBindings(), never()).inNamespace("developer-che");
|
||||||
|
|
@ -197,12 +201,26 @@ public class OpenShiftStopWorkspaceRoleProvisionerTest {
|
||||||
public void shouldNotCreateRoleBindingWhenInstallationLocationIsNull()
|
public void shouldNotCreateRoleBindingWhenInstallationLocationIsNull()
|
||||||
throws InfrastructureException {
|
throws InfrastructureException {
|
||||||
lenient().when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn(null);
|
lenient().when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn(null);
|
||||||
OpenShiftStopWorkspaceRoleProvisioner
|
OpenShiftStopWorkspaceRoleConfigurator
|
||||||
stopWorkspaceRoleProvisionerWithoutValidInstallationLocation =
|
stopWorkspaceRoleProvisionerWithoutValidInstallationLocation =
|
||||||
new OpenShiftStopWorkspaceRoleProvisioner(clientFactory, cheInstallationLocation, true);
|
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||||
stopWorkspaceRoleProvisionerWithoutValidInstallationLocation.provision("developer-che");
|
clientFactory, cheInstallationLocation, true, "yes");
|
||||||
|
stopWorkspaceRoleProvisionerWithoutValidInstallationLocation.configure(null, "developer-che");
|
||||||
verify(osClient, never()).roles();
|
verify(osClient, never()).roles();
|
||||||
verify(osClient, never()).roleBindings();
|
verify(osClient, never()).roleBindings();
|
||||||
verify(osClient.roleBindings(), never()).inNamespace("developer-che");
|
verify(osClient.roleBindings(), never()).inNamespace("developer-che");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotCallStopWorkspaceRoleProvisionWhenIdentityProviderIsDefined()
|
||||||
|
throws Exception {
|
||||||
|
when(cheInstallationLocation.getInstallationLocationNamespace()).thenReturn("something");
|
||||||
|
OpenShiftStopWorkspaceRoleConfigurator configurator =
|
||||||
|
new OpenShiftStopWorkspaceRoleConfigurator(
|
||||||
|
clientFactory, cheInstallationLocation, true, null);
|
||||||
|
|
||||||
|
configurator.configure(null, "something");
|
||||||
|
|
||||||
|
verify(clientFactory, times(0)).createOC();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.project.configurator;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||||
|
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
||||||
|
import org.eclipse.che.workspace.infrastructure.openshift.project.OpenShiftWorkspaceServiceAccount;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
import org.testng.annotations.Listeners;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Listeners(MockitoTestNGListener.class)
|
||||||
|
public class OpenShiftWorkspaceServiceAccountConfiguratorTest {
|
||||||
|
private final String SA_NAME = "test-serviceaccout";
|
||||||
|
private final String CLUSTER_ROLES = "role1, role2";
|
||||||
|
|
||||||
|
private final String WS_ID = "ws123";
|
||||||
|
private final String USER_ID = "user123";
|
||||||
|
private final String USERNAME = "user-che";
|
||||||
|
|
||||||
|
private final String NS_NAME = "namespace-che";
|
||||||
|
|
||||||
|
private NamespaceResolutionContext nsContext;
|
||||||
|
|
||||||
|
@Mock private OpenShiftClientFactory clientFactory;
|
||||||
|
|
||||||
|
private OpenShiftWorkspaceServiceAccountConfigurator saConfigurator;
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() {
|
||||||
|
nsContext = new NamespaceResolutionContext(WS_ID, USER_ID, USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPreparesServiceAccount() throws InfrastructureException {
|
||||||
|
saConfigurator =
|
||||||
|
spy(
|
||||||
|
new OpenShiftWorkspaceServiceAccountConfigurator(
|
||||||
|
SA_NAME, CLUSTER_ROLES, clientFactory));
|
||||||
|
OpenShiftWorkspaceServiceAccount serviceAccount = mock(OpenShiftWorkspaceServiceAccount.class);
|
||||||
|
doReturn(serviceAccount).when(saConfigurator).createServiceAccount(WS_ID, NS_NAME);
|
||||||
|
|
||||||
|
saConfigurator.configure(nsContext, NS_NAME);
|
||||||
|
|
||||||
|
verify(serviceAccount).prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoNothingWhenServiceAccountNotSet() throws InfrastructureException {
|
||||||
|
saConfigurator =
|
||||||
|
spy(new OpenShiftWorkspaceServiceAccountConfigurator(null, CLUSTER_ROLES, clientFactory));
|
||||||
|
|
||||||
|
saConfigurator.configure(nsContext, NS_NAME);
|
||||||
|
|
||||||
|
verify(saConfigurator, times(0)).createServiceAccount(any(), any());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,8 @@ import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||||
import org.eclipse.che.commons.subject.Subject;
|
import org.eclipse.che.commons.subject.Subject;
|
||||||
|
|
@ -43,6 +45,9 @@ import org.slf4j.LoggerFactory;
|
||||||
* <li>Set subject for current request into {@link EnvironmentContext}
|
* <li>Set subject for current request into {@link EnvironmentContext}
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
* <p>{@link MultiUserEnvironmentInitializationFilter#UNAUTHORIZED_ENDPOINT_PATHS} is list of
|
||||||
|
* unauthenticated paths, that are allowed without token.
|
||||||
|
*
|
||||||
* @param <T> the type of intermediary type used for conversion from a string token to a Subject
|
* @param <T> the type of intermediary type used for conversion from a string token to a Subject
|
||||||
* @author Max Shaposhnyk (mshaposh@redhat.com)
|
* @author Max Shaposhnyk (mshaposh@redhat.com)
|
||||||
*/
|
*/
|
||||||
|
|
@ -51,6 +56,9 @@ public abstract class MultiUserEnvironmentInitializationFilter<T> implements Fil
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
LoggerFactory.getLogger(MultiUserEnvironmentInitializationFilter.class);
|
LoggerFactory.getLogger(MultiUserEnvironmentInitializationFilter.class);
|
||||||
|
|
||||||
|
private static final List<String> UNAUTHORIZED_ENDPOINT_PATHS =
|
||||||
|
Collections.singletonList("/system/state");
|
||||||
|
|
||||||
private final SessionStore sessionStore;
|
private final SessionStore sessionStore;
|
||||||
private final RequestTokenExtractor tokenExtractor;
|
private final RequestTokenExtractor tokenExtractor;
|
||||||
|
|
||||||
|
|
@ -197,9 +205,23 @@ public abstract class MultiUserEnvironmentInitializationFilter<T> implements Fil
|
||||||
* @throws IOException inherited from {@link FilterChain#doFilter}
|
* @throws IOException inherited from {@link FilterChain#doFilter}
|
||||||
* @throws ServletException inherited from {@link FilterChain#doFilter}
|
* @throws ServletException inherited from {@link FilterChain#doFilter}
|
||||||
*/
|
*/
|
||||||
protected abstract void handleMissingToken(
|
protected void handleMissingToken(
|
||||||
ServletRequest request, ServletResponse response, FilterChain chain)
|
ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
throws IOException, ServletException;
|
throws IOException, ServletException {
|
||||||
|
// if request path is in unauthorized endpoints, continue
|
||||||
|
if (request instanceof HttpServletRequest) {
|
||||||
|
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||||
|
String path = httpRequest.getServletPath();
|
||||||
|
if (UNAUTHORIZED_ENDPOINT_PATHS.contains(path)) {
|
||||||
|
LOG.debug("Allowing request to '{}' without authorization header.", path);
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.error("Rejecting the request due to missing/expired token in Authorization header.");
|
||||||
|
sendError(response, 401, "Authorization token is missing or expired");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends appropriate error status code and message into response.
|
* Sends appropriate error status code and message into response.
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,11 @@ import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.Mockito.withSettings;
|
import static org.mockito.Mockito.withSettings;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||||
import org.eclipse.che.commons.subject.Subject;
|
import org.eclipse.che.commons.subject.Subject;
|
||||||
|
|
@ -83,6 +85,7 @@ public class MultiUserEnvironmentInitializationFilterTest {
|
||||||
// then
|
// then
|
||||||
verify(tokenExtractor).getToken(eq(request));
|
verify(tokenExtractor).getToken(eq(request));
|
||||||
verify(filter).handleMissingToken(eq(request), eq(response), eq(chain));
|
verify(filter).handleMissingToken(eq(request), eq(response), eq(chain));
|
||||||
|
verify(request).getServletPath();
|
||||||
verifyNoMoreInteractions(request);
|
verifyNoMoreInteractions(request);
|
||||||
verify(filter, never()).getUserId(any());
|
verify(filter, never()).getUserId(any());
|
||||||
verify(filter, never()).extractSubject(anyString(), any());
|
verify(filter, never()).extractSubject(anyString(), any());
|
||||||
|
|
@ -100,6 +103,7 @@ public class MultiUserEnvironmentInitializationFilterTest {
|
||||||
// then
|
// then
|
||||||
verify(tokenExtractor).getToken(eq(request));
|
verify(tokenExtractor).getToken(eq(request));
|
||||||
verify(filter).handleMissingToken(eq(request), eq(response), eq(chain));
|
verify(filter).handleMissingToken(eq(request), eq(response), eq(chain));
|
||||||
|
verify(request).getServletPath();
|
||||||
verifyNoMoreInteractions(request);
|
verifyNoMoreInteractions(request);
|
||||||
verify(filter, never()).getUserId(any());
|
verify(filter, never()).getUserId(any());
|
||||||
verify(filter, never()).extractSubject(anyString(), any());
|
verify(filter, never()).extractSubject(anyString(), any());
|
||||||
|
|
@ -168,4 +172,23 @@ public class MultiUserEnvironmentInitializationFilterTest {
|
||||||
// then
|
// then
|
||||||
verify(context).setSubject(eq(subject));
|
verify(context).setSubject(eq(subject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleMissingTokenShouldAllowUnauthorizedEndpoint()
|
||||||
|
throws ServletException, IOException {
|
||||||
|
when(request.getServletPath()).thenReturn("/system/state");
|
||||||
|
|
||||||
|
filter.handleMissingToken(request, response, chain);
|
||||||
|
|
||||||
|
verify(chain).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleMissingTokenShouldRejectRequest() throws ServletException, IOException {
|
||||||
|
when(request.getServletPath()).thenReturn("blabol");
|
||||||
|
|
||||||
|
filter.handleMissingToken(request, response, chain);
|
||||||
|
|
||||||
|
verify(response).sendError(eq(401), anyString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,6 @@
|
||||||
<groupId>com.auth0</groupId>
|
<groupId>com.auth0</groupId>
|
||||||
<artifactId>jwks-rsa</artifactId>
|
<artifactId>jwks-rsa</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-databind</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
|
|
@ -102,10 +94,6 @@
|
||||||
<groupId>org.eclipse.che.core</groupId>
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
<artifactId>che-core-commons-annotations</artifactId>
|
<artifactId>che-core-commons-annotations</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.eclipse.che.core</groupId>
|
|
||||||
<artifactId>che-core-commons-inject</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.che.core</groupId>
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
<artifactId>che-core-commons-lang</artifactId>
|
<artifactId>che-core-commons-lang</artifactId>
|
||||||
|
|
@ -130,6 +118,10 @@
|
||||||
<groupId>org.eclipse.che.multiuser</groupId>
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
<artifactId>che-multiuser-machine-authentication-shared</artifactId>
|
<artifactId>che-multiuser-machine-authentication-shared</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<artifactId>che-multiuser-oidc</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.che.multiuser</groupId>
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
<artifactId>che-multiuser-personal-account</artifactId>
|
<artifactId>che-multiuser-personal-account</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ package org.eclipse.che.multiuser.keycloak.server;
|
||||||
|
|
||||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_USERNAME_CLAIM_SETTING;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
|
|
@ -43,7 +44,6 @@ import org.eclipse.che.multiuser.api.authentication.commons.filter.MultiUserEnvi
|
||||||
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
|
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
|
||||||
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
|
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
|
||||||
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
|
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
|
||||||
import org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
@ -123,8 +123,7 @@ public class KeycloakEnvironmentInitializationFilter
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String username =
|
String username =
|
||||||
claims.get(
|
claims.get(keycloakSettings.get().get(OIDC_USERNAME_CLAIM_SETTING), String.class);
|
||||||
keycloakSettings.get().get(KeycloakConstants.USERNAME_CLAIM_SETTING), String.class);
|
|
||||||
if (username == null) { // fallback to unique id promised by spec
|
if (username == null) { // fallback to unique id promised by spec
|
||||||
// https://openid.net/specs/openid-connect-basic-1_0.html#ClaimStability
|
// https://openid.net/specs/openid-connect-basic-1_0.html#ClaimStability
|
||||||
username = claims.getIssuer() + ":" + claims.getSubject();
|
username = claims.getIssuer() + ":" + claims.getSubject();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.multiuser.keycloak.server;
|
||||||
|
|
||||||
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfoProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KeycloakOIDCInfoProvider retrieves OpenID Connect (OIDC) configuration for well-known endpoint.
|
||||||
|
* These information is useful to provide access to the Keycloak api.
|
||||||
|
*/
|
||||||
|
public class KeycloakOIDCInfoProvider extends OIDCInfoProvider {
|
||||||
|
public final String realm;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public KeycloakOIDCInfoProvider(
|
||||||
|
@Nullable @Named(AUTH_SERVER_URL_SETTING) String serverURL,
|
||||||
|
@Nullable @Named(AUTH_SERVER_URL_INTERNAL_SETTING) String serverInternalURL,
|
||||||
|
@Nullable @Named(OIDC_PROVIDER_SETTING) String oidcProviderUrl,
|
||||||
|
@Nullable @Named(REALM_SETTING) String realm) {
|
||||||
|
super(serverURL, serverInternalURL, oidcProviderUrl);
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String constructServerAuthUrl(String serverAuthUrl) {
|
||||||
|
return serverAuthUrl + "/realms/" + realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validate() {
|
||||||
|
if (oidcProviderUrl == null && realm == null) {
|
||||||
|
throw new RuntimeException("The '" + REALM_SETTING + "' property must be set");
|
||||||
|
}
|
||||||
|
super.validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ import javax.inject.Singleton;
|
||||||
import org.eclipse.che.api.core.ApiException;
|
import org.eclipse.che.api.core.ApiException;
|
||||||
import org.eclipse.che.api.core.ServerException;
|
import org.eclipse.che.api.core.ServerException;
|
||||||
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ import org.eclipse.che.commons.lang.Pair;
|
||||||
import org.eclipse.che.dto.server.DtoFactory;
|
import org.eclipse.che.dto.server.DtoFactory;
|
||||||
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakErrorResponse;
|
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakErrorResponse;
|
||||||
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse;
|
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
@ -75,7 +76,7 @@ public class KeycloakServiceClient {
|
||||||
Pattern.compile("<div id=\"kc-error-message\">(\\s*)<p class=\"instruction\">(.+?)</p>");
|
Pattern.compile("<div id=\"kc-error-message\">(\\s*)<p class=\"instruction\">(.+?)</p>");
|
||||||
|
|
||||||
private static final Gson gson = new Gson();
|
private static final Gson gson = new Gson();
|
||||||
private JwtParser jwtParser;
|
private final JwtParser jwtParser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public KeycloakServiceClient(
|
public KeycloakServiceClient(
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.keycloak.server;
|
||||||
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_DASHBOARD;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_DASHBOARD;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_IDE;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_IDE;
|
||||||
|
|
@ -19,16 +18,17 @@ import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.GITHUB
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JS_ADAPTER_URL_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JS_ADAPTER_URL_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JWKS_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JWKS_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.LOGOUT_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.LOGOUT_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.OIDC_PROVIDER_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.OSO_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.OSO_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PASSWORD_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PASSWORD_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PROFILE_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PROFILE_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.TOKEN_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.TOKEN_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USERINFO_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USERINFO_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USERNAME_CLAIM_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USE_FIXED_REDIRECT_URLS_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USE_FIXED_REDIRECT_URLS_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USE_NONCE_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USE_NONCE_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.AUTH_SERVER_URL_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_PROVIDER_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_USERNAME_CLAIM_SETTING;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -37,6 +37,7 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import org.eclipse.che.commons.annotation.Nullable;
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
|
|
||||||
/** @author Max Shaposhnik (mshaposh@redhat.com) */
|
/** @author Max Shaposhnik (mshaposh@redhat.com) */
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|
@ -54,7 +55,7 @@ public class KeycloakSettings {
|
||||||
@Nullable @Named(REALM_SETTING) String realm,
|
@Nullable @Named(REALM_SETTING) String realm,
|
||||||
@Named(CLIENT_ID_SETTING) String clientId,
|
@Named(CLIENT_ID_SETTING) String clientId,
|
||||||
@Nullable @Named(OIDC_PROVIDER_SETTING) String oidcProviderUrl,
|
@Nullable @Named(OIDC_PROVIDER_SETTING) String oidcProviderUrl,
|
||||||
@Nullable @Named(USERNAME_CLAIM_SETTING) String usernameClaim,
|
@Nullable @Named(OIDC_USERNAME_CLAIM_SETTING) String usernameClaim,
|
||||||
@Named(USE_NONCE_SETTING) boolean useNonce,
|
@Named(USE_NONCE_SETTING) boolean useNonce,
|
||||||
@Nullable @Named(OSO_ENDPOINT_SETTING) String osoEndpoint,
|
@Nullable @Named(OSO_ENDPOINT_SETTING) String osoEndpoint,
|
||||||
@Nullable @Named(GITHUB_ENDPOINT_SETTING) String gitHubEndpoint,
|
@Nullable @Named(GITHUB_ENDPOINT_SETTING) String gitHubEndpoint,
|
||||||
|
|
@ -64,7 +65,8 @@ public class KeycloakSettings {
|
||||||
|
|
||||||
Map<String, String> settings = Maps.newHashMap();
|
Map<String, String> settings = Maps.newHashMap();
|
||||||
settings.put(
|
settings.put(
|
||||||
USERNAME_CLAIM_SETTING, usernameClaim == null ? DEFAULT_USERNAME_CLAIM : usernameClaim);
|
OIDC_USERNAME_CLAIM_SETTING,
|
||||||
|
usernameClaim == null ? DEFAULT_USERNAME_CLAIM : usernameClaim);
|
||||||
settings.put(CLIENT_ID_SETTING, clientId);
|
settings.put(CLIENT_ID_SETTING, clientId);
|
||||||
settings.put(REALM_SETTING, realm);
|
settings.put(REALM_SETTING, realm);
|
||||||
|
|
||||||
|
|
@ -80,9 +82,8 @@ public class KeycloakSettings {
|
||||||
serverURL + "/realms/" + realm + "/protocol/openid-connect/token");
|
serverURL + "/realms/" + realm + "/protocol/openid-connect/token");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oidcInfo.getEndSessionPublicEndpoint() != null) {
|
oidcInfo.getEndSessionPublicEndpoint().ifPresent(e -> settings.put(LOGOUT_ENDPOINT_SETTING, e));
|
||||||
settings.put(LOGOUT_ENDPOINT_SETTING, oidcInfo.getEndSessionPublicEndpoint());
|
|
||||||
}
|
|
||||||
if (oidcInfo.getTokenPublicEndpoint() != null) {
|
if (oidcInfo.getTokenPublicEndpoint() != null) {
|
||||||
settings.put(TOKEN_ENDPOINT_SETTING, oidcInfo.getTokenPublicEndpoint());
|
settings.put(TOKEN_ENDPOINT_SETTING, oidcInfo.getTokenPublicEndpoint());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||||
* This program and the accompanying materials are made
|
* This program and the accompanying materials are made
|
||||||
* available under the terms of the Eclipse Public License 2.0
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||||
|
|
@ -13,30 +13,20 @@ package org.eclipse.che.multiuser.keycloak.server;
|
||||||
|
|
||||||
import static org.eclipse.che.multiuser.machine.authentication.shared.Constants.MACHINE_TOKEN_KIND;
|
import static org.eclipse.che.multiuser.machine.authentication.shared.Constants.MACHINE_TOKEN_KIND;
|
||||||
|
|
||||||
import com.auth0.jwk.JwkException;
|
|
||||||
import com.auth0.jwk.JwkProvider;
|
import com.auth0.jwk.JwkProvider;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.JwsHeader;
|
import io.jsonwebtoken.JwsHeader;
|
||||||
import io.jsonwebtoken.JwtException;
|
|
||||||
import io.jsonwebtoken.SigningKeyResolverAdapter;
|
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.PublicKey;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import org.slf4j.Logger;
|
import org.eclipse.che.multiuser.oidc.OIDCSigningKeyResolver;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/** Resolves signing key based on id from JWT header */
|
/** Resolves signing key based on id from JWT header */
|
||||||
@Singleton
|
@Singleton
|
||||||
public class KeycloakSigningKeyResolver extends SigningKeyResolverAdapter {
|
public class KeycloakSigningKeyResolver extends OIDCSigningKeyResolver {
|
||||||
|
|
||||||
private final JwkProvider jwkProvider;
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(KeycloakSigningKeyResolver.class);
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
KeycloakSigningKeyResolver(JwkProvider jwkProvider) {
|
KeycloakSigningKeyResolver(JwkProvider jwkProvider) {
|
||||||
this.jwkProvider = jwkProvider;
|
super(jwkProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -54,19 +44,4 @@ public class KeycloakSigningKeyResolver extends SigningKeyResolverAdapter {
|
||||||
}
|
}
|
||||||
return getJwtPublicKey(header);
|
return getJwtPublicKey(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized PublicKey getJwtPublicKey(JwsHeader<?> header) {
|
|
||||||
String kid = header.getKeyId();
|
|
||||||
if (header.getKeyId() == null) {
|
|
||||||
LOG.warn(
|
|
||||||
"'kid' is missing in the JWT token header. This is not possible to validate the token with OIDC provider keys");
|
|
||||||
throw new JwtException("'kid' is missing in the JWT token header.");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return jwkProvider.get(kid).getPublicKey();
|
|
||||||
} catch (JwkException e) {
|
|
||||||
throw new JwtException(
|
|
||||||
"Error during the retrieval of the public key during JWT token validation", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,20 @@ package org.eclipse.che.multiuser.keycloak.server.deploy;
|
||||||
import com.auth0.jwk.JwkProvider;
|
import com.auth0.jwk.JwkProvider;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import io.jsonwebtoken.JwtParser;
|
import io.jsonwebtoken.JwtParser;
|
||||||
|
import io.jsonwebtoken.SigningKeyResolver;
|
||||||
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
||||||
import org.eclipse.che.api.user.server.TokenValidator;
|
import org.eclipse.che.api.user.server.TokenValidator;
|
||||||
import org.eclipse.che.api.user.server.spi.ProfileDao;
|
import org.eclipse.che.api.user.server.spi.ProfileDao;
|
||||||
import org.eclipse.che.multiuser.api.account.personal.PersonalAccountUserManager;
|
import org.eclipse.che.multiuser.api.account.personal.PersonalAccountUserManager;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.KeycloakConfigurationService;
|
import org.eclipse.che.multiuser.keycloak.server.KeycloakConfigurationService;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.KeycloakJwkProvider;
|
import org.eclipse.che.multiuser.keycloak.server.KeycloakOIDCInfoProvider;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.KeycloakJwtParserProvider;
|
import org.eclipse.che.multiuser.keycloak.server.KeycloakSigningKeyResolver;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.KeycloakTokenValidator;
|
import org.eclipse.che.multiuser.keycloak.server.KeycloakTokenValidator;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.KeycloakUserManager;
|
import org.eclipse.che.multiuser.keycloak.server.KeycloakUserManager;
|
||||||
import org.eclipse.che.multiuser.keycloak.server.OIDCInfo;
|
|
||||||
import org.eclipse.che.multiuser.keycloak.server.OIDCInfoProvider;
|
|
||||||
import org.eclipse.che.multiuser.keycloak.server.dao.KeycloakProfileDao;
|
import org.eclipse.che.multiuser.keycloak.server.dao.KeycloakProfileDao;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCJwkProvider;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCJwtParserProvider;
|
||||||
import org.eclipse.che.security.oauth.OAuthAPI;
|
import org.eclipse.che.security.oauth.OAuthAPI;
|
||||||
|
|
||||||
public class KeycloakModule extends AbstractModule {
|
public class KeycloakModule extends AbstractModule {
|
||||||
|
|
@ -38,9 +40,10 @@ public class KeycloakModule extends AbstractModule {
|
||||||
bind(KeycloakConfigurationService.class);
|
bind(KeycloakConfigurationService.class);
|
||||||
|
|
||||||
bind(ProfileDao.class).to(KeycloakProfileDao.class);
|
bind(ProfileDao.class).to(KeycloakProfileDao.class);
|
||||||
bind(JwkProvider.class).toProvider(KeycloakJwkProvider.class);
|
bind(JwkProvider.class).toProvider(OIDCJwkProvider.class);
|
||||||
bind(JwtParser.class).toProvider(KeycloakJwtParserProvider.class);
|
bind(SigningKeyResolver.class).to(KeycloakSigningKeyResolver.class);
|
||||||
bind(OIDCInfo.class).toProvider(OIDCInfoProvider.class).asEagerSingleton();
|
bind(JwtParser.class).toProvider(OIDCJwtParserProvider.class);
|
||||||
|
bind(OIDCInfo.class).toProvider(KeycloakOIDCInfoProvider.class).asEagerSingleton();
|
||||||
bind(PersonalAccountUserManager.class).to(KeycloakUserManager.class);
|
bind(PersonalAccountUserManager.class).to(KeycloakUserManager.class);
|
||||||
|
|
||||||
bind(OAuthAPI.class).toProvider(OAuthAPIProvider.class);
|
bind(OAuthAPI.class).toProvider(OAuthAPIProvider.class);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.keycloak.server;
|
||||||
|
|
||||||
import static org.eclipse.che.multiuser.api.authentication.commons.Constants.CHE_SUBJECT_ATTRIBUTE;
|
import static org.eclipse.che.multiuser.api.authentication.commons.Constants.CHE_SUBJECT_ATTRIBUTE;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USERNAME_CLAIM_SETTING;
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_USERNAME_CLAIM_SETTING;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
|
@ -50,7 +50,6 @@ import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
|
||||||
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
|
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
|
||||||
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
|
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
|
||||||
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
|
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
|
||||||
import org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants;
|
|
||||||
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
|
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
|
@ -119,7 +118,7 @@ public class KeycloakEnvironmentInitializationFilterTest {
|
||||||
DefaultJws<Claims> jws = new DefaultJws<>(new DefaultJwsHeader(), claims, "");
|
DefaultJws<Claims> jws = new DefaultJws<>(new DefaultJwsHeader(), claims, "");
|
||||||
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn("token");
|
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn("token");
|
||||||
when(jwtParser.parseClaimsJws(anyString())).thenReturn(jws);
|
when(jwtParser.parseClaimsJws(anyString())).thenReturn(jws);
|
||||||
keycloakSettingsMap.put(USERNAME_CLAIM_SETTING, "preferred_username");
|
keycloakSettingsMap.put(OIDC_USERNAME_CLAIM_SETTING, "preferred_username");
|
||||||
when(userManager.getOrCreateUser(anyString(), anyString(), anyString()))
|
when(userManager.getOrCreateUser(anyString(), anyString(), anyString()))
|
||||||
.thenReturn(mock(UserImpl.class, RETURNS_DEEP_STUBS));
|
.thenReturn(mock(UserImpl.class, RETURNS_DEEP_STUBS));
|
||||||
filter =
|
filter =
|
||||||
|
|
@ -149,7 +148,7 @@ public class KeycloakEnvironmentInitializationFilterTest {
|
||||||
DefaultJws<Claims> jws = new DefaultJws<>(new DefaultJwsHeader(), claims, "");
|
DefaultJws<Claims> jws = new DefaultJws<>(new DefaultJwsHeader(), claims, "");
|
||||||
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn("token");
|
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn("token");
|
||||||
when(jwtParser.parseClaimsJws(anyString())).thenReturn(jws);
|
when(jwtParser.parseClaimsJws(anyString())).thenReturn(jws);
|
||||||
keycloakSettingsMap.put(USERNAME_CLAIM_SETTING, "preferred_username");
|
keycloakSettingsMap.put(OIDC_USERNAME_CLAIM_SETTING, "preferred_username");
|
||||||
when(userManager.getOrCreateUser(anyString(), anyString(), anyString()))
|
when(userManager.getOrCreateUser(anyString(), anyString(), anyString()))
|
||||||
.thenReturn(mock(UserImpl.class, RETURNS_DEEP_STUBS));
|
.thenReturn(mock(UserImpl.class, RETURNS_DEEP_STUBS));
|
||||||
filter =
|
filter =
|
||||||
|
|
@ -210,7 +209,7 @@ public class KeycloakEnvironmentInitializationFilterTest {
|
||||||
Claims claims = new DefaultClaims(claimParams).setSubject("id");
|
Claims claims = new DefaultClaims(claimParams).setSubject("id");
|
||||||
DefaultJws<Claims> jws = new DefaultJws<>(new DefaultJwsHeader(), claims, "");
|
DefaultJws<Claims> jws = new DefaultJws<>(new DefaultJwsHeader(), claims, "");
|
||||||
UserImpl user = new UserImpl("id", "test@test.com", "username");
|
UserImpl user = new UserImpl("id", "test@test.com", "username");
|
||||||
keycloakSettingsMap.put(KeycloakConstants.USERNAME_CLAIM_SETTING, "preferred_username");
|
keycloakSettingsMap.put(OIDC_USERNAME_CLAIM_SETTING, "preferred_username");
|
||||||
// given
|
// given
|
||||||
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn("token");
|
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn("token");
|
||||||
when(jwtParser.parseClaimsJws(anyString())).thenReturn(jws);
|
when(jwtParser.parseClaimsJws(anyString())).thenReturn(jws);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ import org.eclipse.che.api.core.rest.Service;
|
||||||
import org.eclipse.che.dto.server.DtoFactory;
|
import org.eclipse.che.dto.server.DtoFactory;
|
||||||
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakErrorResponse;
|
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakErrorResponse;
|
||||||
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse;
|
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
import org.everrest.assured.EverrestJetty;
|
import org.everrest.assured.EverrestJetty;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.testng.MockitoTestNGListener;
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.keycloak.server;
|
||||||
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.server.KeycloakSettings.DEFAULT_USERNAME_CLAIM;
|
import static org.eclipse.che.multiuser.keycloak.server.KeycloakSettings.DEFAULT_USERNAME_CLAIM;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.CLIENT_ID_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_DASHBOARD;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_DASHBOARD;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_IDE;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.FIXED_REDIRECT_URL_FOR_IDE;
|
||||||
|
|
@ -20,20 +19,23 @@ import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.GITHUB
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JS_ADAPTER_URL_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JS_ADAPTER_URL_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JWKS_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.JWKS_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.LOGOUT_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.LOGOUT_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.OIDC_PROVIDER_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.OSO_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.OSO_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PASSWORD_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PASSWORD_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PROFILE_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.PROFILE_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.TOKEN_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.TOKEN_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USERINFO_ENDPOINT_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USERINFO_ENDPOINT_SETTING;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USERNAME_CLAIM_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USE_NONCE_SETTING;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.USE_NONCE_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.AUTH_SERVER_URL_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_PROVIDER_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_USERNAME_CLAIM_SETTING;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
import static org.testng.Assert.assertNull;
|
import static org.testng.Assert.assertNull;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.testng.MockitoTestNGListener;
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
import org.testng.annotations.Listeners;
|
import org.testng.annotations.Listeners;
|
||||||
|
|
@ -185,7 +187,8 @@ public class KeycloakSettingsTest {
|
||||||
public void shouldBeUsedConfigurationFromExternalOIDCProviderWithoutFixedRedirectLinks() {
|
public void shouldBeUsedConfigurationFromExternalOIDCProviderWithoutFixedRedirectLinks() {
|
||||||
final String SERVER_AUTH_URL = "https://external-keycloak-che.apps-crc.testing/auth";
|
final String SERVER_AUTH_URL = "https://external-keycloak-che.apps-crc.testing/auth";
|
||||||
|
|
||||||
when(oidcInfo.getEndSessionPublicEndpoint()).thenReturn(SERVER_AUTH_URL + LOGOUT_URL_PATH);
|
when(oidcInfo.getEndSessionPublicEndpoint())
|
||||||
|
.thenReturn(Optional.of(SERVER_AUTH_URL + LOGOUT_URL_PATH));
|
||||||
when(oidcInfo.getJwksPublicUri()).thenReturn(SERVER_AUTH_URL + JWKS_ENDPOINT_PATH);
|
when(oidcInfo.getJwksPublicUri()).thenReturn(SERVER_AUTH_URL + JWKS_ENDPOINT_PATH);
|
||||||
when(oidcInfo.getUserInfoPublicEndpoint()).thenReturn(SERVER_AUTH_URL + USER_INFO_PATH);
|
when(oidcInfo.getUserInfoPublicEndpoint()).thenReturn(SERVER_AUTH_URL + USER_INFO_PATH);
|
||||||
when(oidcInfo.getTokenPublicEndpoint()).thenReturn(SERVER_AUTH_URL + TOKEN_URL_PATH);
|
when(oidcInfo.getTokenPublicEndpoint()).thenReturn(SERVER_AUTH_URL + TOKEN_URL_PATH);
|
||||||
|
|
@ -206,7 +209,7 @@ public class KeycloakSettingsTest {
|
||||||
oidcInfo);
|
oidcInfo);
|
||||||
|
|
||||||
Map<String, String> publicSettings = settings.get();
|
Map<String, String> publicSettings = settings.get();
|
||||||
assertEquals(publicSettings.get(USERNAME_CLAIM_SETTING), DEFAULT_USERNAME_CLAIM);
|
assertEquals(publicSettings.get(OIDC_USERNAME_CLAIM_SETTING), DEFAULT_USERNAME_CLAIM);
|
||||||
assertEquals(publicSettings.get(CLIENT_ID_SETTING), CLIENT_ID);
|
assertEquals(publicSettings.get(CLIENT_ID_SETTING), CLIENT_ID);
|
||||||
assertEquals(publicSettings.get(REALM_SETTING), CHE_REALM);
|
assertEquals(publicSettings.get(REALM_SETTING), CHE_REALM);
|
||||||
assertNull(publicSettings.get(AUTH_SERVER_URL_SETTING));
|
assertNull(publicSettings.get(AUTH_SERVER_URL_SETTING));
|
||||||
|
|
@ -229,7 +232,8 @@ public class KeycloakSettingsTest {
|
||||||
public void shouldBeUsedConfigurationFromExternalAuthServer() {
|
public void shouldBeUsedConfigurationFromExternalAuthServer() {
|
||||||
final String SERVER_AUTH_URL = "https://keycloak-che.apps-crc.testing/auth";
|
final String SERVER_AUTH_URL = "https://keycloak-che.apps-crc.testing/auth";
|
||||||
|
|
||||||
when(oidcInfo.getEndSessionPublicEndpoint()).thenReturn(SERVER_AUTH_URL + LOGOUT_URL_PATH);
|
when(oidcInfo.getEndSessionPublicEndpoint())
|
||||||
|
.thenReturn(Optional.of(SERVER_AUTH_URL + LOGOUT_URL_PATH));
|
||||||
when(oidcInfo.getJwksPublicUri()).thenReturn(SERVER_AUTH_URL + JWKS_ENDPOINT_PATH);
|
when(oidcInfo.getJwksPublicUri()).thenReturn(SERVER_AUTH_URL + JWKS_ENDPOINT_PATH);
|
||||||
when(oidcInfo.getUserInfoPublicEndpoint()).thenReturn(SERVER_AUTH_URL + USER_INFO_PATH);
|
when(oidcInfo.getUserInfoPublicEndpoint()).thenReturn(SERVER_AUTH_URL + USER_INFO_PATH);
|
||||||
when(oidcInfo.getTokenPublicEndpoint()).thenReturn(SERVER_AUTH_URL + TOKEN_URL_PATH);
|
when(oidcInfo.getTokenPublicEndpoint()).thenReturn(SERVER_AUTH_URL + TOKEN_URL_PATH);
|
||||||
|
|
@ -250,7 +254,7 @@ public class KeycloakSettingsTest {
|
||||||
oidcInfo);
|
oidcInfo);
|
||||||
|
|
||||||
Map<String, String> publicSettings = settings.get();
|
Map<String, String> publicSettings = settings.get();
|
||||||
assertEquals(publicSettings.get(USERNAME_CLAIM_SETTING), DEFAULT_USERNAME_CLAIM);
|
assertEquals(publicSettings.get(OIDC_USERNAME_CLAIM_SETTING), DEFAULT_USERNAME_CLAIM);
|
||||||
assertEquals(publicSettings.get(CLIENT_ID_SETTING), CLIENT_ID);
|
assertEquals(publicSettings.get(CLIENT_ID_SETTING), CLIENT_ID);
|
||||||
assertEquals(publicSettings.get(REALM_SETTING), CHE_REALM);
|
assertEquals(publicSettings.get(REALM_SETTING), CHE_REALM);
|
||||||
assertEquals(publicSettings.get(AUTH_SERVER_URL_SETTING), SERVER_AUTH_URL);
|
assertEquals(publicSettings.get(AUTH_SERVER_URL_SETTING), SERVER_AUTH_URL);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import static org.testng.Assert.assertNull;
|
||||||
|
|
||||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
import org.testng.annotations.AfterClass;
|
import org.testng.annotations.AfterClass;
|
||||||
import org.testng.annotations.BeforeClass;
|
import org.testng.annotations.BeforeClass;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
@ -86,9 +87,8 @@ public class OIDCInfoProviderTest {
|
||||||
.willReturn(
|
.willReturn(
|
||||||
aResponse().withHeader("Content-Type", "text/html").withBody("broken json")));
|
aResponse().withHeader("Content-Type", "text/html").withBody("broken json")));
|
||||||
|
|
||||||
OIDCInfoProvider oidcInfoProvider = new OIDCInfoProvider();
|
KeycloakOIDCInfoProvider oidcInfoProvider =
|
||||||
oidcInfoProvider.oidcProviderUrl = serverUrl;
|
new KeycloakOIDCInfoProvider(null, null, serverUrl, CHE_REALM);
|
||||||
oidcInfoProvider.realm = CHE_REALM;
|
|
||||||
|
|
||||||
oidcInfoProvider.get();
|
oidcInfoProvider.get();
|
||||||
}
|
}
|
||||||
|
|
@ -100,9 +100,8 @@ public class OIDCInfoProviderTest {
|
||||||
.willReturn(
|
.willReturn(
|
||||||
aResponse().withHeader("Content-Type", "text/html").withBody(openIdConfig)));
|
aResponse().withHeader("Content-Type", "text/html").withBody(openIdConfig)));
|
||||||
|
|
||||||
OIDCInfoProvider oidcInfoProvider = new OIDCInfoProvider();
|
KeycloakOIDCInfoProvider oidcInfoProvider =
|
||||||
oidcInfoProvider.serverURL = serverUrl;
|
new KeycloakOIDCInfoProvider(serverUrl, null, null, CHE_REALM);
|
||||||
oidcInfoProvider.realm = CHE_REALM;
|
|
||||||
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|
@ -110,7 +109,7 @@ public class OIDCInfoProviderTest {
|
||||||
oidcInfo.getTokenPublicEndpoint());
|
oidcInfo.getTokenPublicEndpoint());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
||||||
oidcInfo.getEndSessionPublicEndpoint());
|
oidcInfo.getEndSessionPublicEndpoint().get());
|
||||||
assertNull(oidcInfo.getUserInfoInternalEndpoint());
|
assertNull(oidcInfo.getUserInfoInternalEndpoint());
|
||||||
assertNull(oidcInfo.getJwksInternalUri());
|
assertNull(oidcInfo.getJwksInternalUri());
|
||||||
}
|
}
|
||||||
|
|
@ -150,10 +149,8 @@ public class OIDCInfoProviderTest {
|
||||||
.withHeader("Content-Type", "text/html")
|
.withHeader("Content-Type", "text/html")
|
||||||
.withBody(OPEN_ID_CONF_TEMPLATE)));
|
.withBody(OPEN_ID_CONF_TEMPLATE)));
|
||||||
|
|
||||||
OIDCInfoProvider oidcInfoProvider = new OIDCInfoProvider();
|
KeycloakOIDCInfoProvider oidcInfoProvider =
|
||||||
oidcInfoProvider.serverURL = serverPublicUrl;
|
new KeycloakOIDCInfoProvider(serverPublicUrl, serverUrl, null, CHE_REALM);
|
||||||
oidcInfoProvider.serverInternalURL = serverUrl;
|
|
||||||
oidcInfoProvider.realm = CHE_REALM;
|
|
||||||
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|
@ -161,7 +158,7 @@ public class OIDCInfoProviderTest {
|
||||||
oidcInfo.getTokenPublicEndpoint());
|
oidcInfo.getTokenPublicEndpoint());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
||||||
oidcInfo.getEndSessionPublicEndpoint());
|
oidcInfo.getEndSessionPublicEndpoint().get());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo",
|
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo",
|
||||||
oidcInfo.getUserInfoPublicEndpoint());
|
oidcInfo.getUserInfoPublicEndpoint());
|
||||||
|
|
@ -215,10 +212,8 @@ public class OIDCInfoProviderTest {
|
||||||
.withHeader("Content-Type", "text/html")
|
.withHeader("Content-Type", "text/html")
|
||||||
.withBody(OPEN_ID_CONF_TEMPLATE)));
|
.withBody(OPEN_ID_CONF_TEMPLATE)));
|
||||||
|
|
||||||
OIDCInfoProvider oidcInfoProvider = new OIDCInfoProvider();
|
KeycloakOIDCInfoProvider oidcInfoProvider =
|
||||||
oidcInfoProvider.serverURL = serverPublicUrl;
|
new KeycloakOIDCInfoProvider(serverPublicUrl, serverInternalUrl, null, CHE_REALM);
|
||||||
oidcInfoProvider.serverInternalURL = serverInternalUrl;
|
|
||||||
oidcInfoProvider.realm = CHE_REALM;
|
|
||||||
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|
@ -226,7 +221,7 @@ public class OIDCInfoProviderTest {
|
||||||
oidcInfo.getTokenPublicEndpoint());
|
oidcInfo.getTokenPublicEndpoint());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
||||||
oidcInfo.getEndSessionPublicEndpoint());
|
oidcInfo.getEndSessionPublicEndpoint().get());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo",
|
serverPublicUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo",
|
||||||
oidcInfo.getUserInfoPublicEndpoint());
|
oidcInfo.getUserInfoPublicEndpoint());
|
||||||
|
|
@ -253,11 +248,8 @@ public class OIDCInfoProviderTest {
|
||||||
.willReturn(
|
.willReturn(
|
||||||
aResponse().withHeader("Content-Type", "text/html").withBody(openIdConfig)));
|
aResponse().withHeader("Content-Type", "text/html").withBody(openIdConfig)));
|
||||||
|
|
||||||
OIDCInfoProvider oidcInfoProvider = new OIDCInfoProvider();
|
KeycloakOIDCInfoProvider oidcInfoProvider =
|
||||||
oidcInfoProvider.serverURL = TEST_URL;
|
new KeycloakOIDCInfoProvider(TEST_URL, TEST_URL, OIDCProviderUrl, CHE_REALM);
|
||||||
oidcInfoProvider.serverInternalURL = TEST_URL;
|
|
||||||
oidcInfoProvider.oidcProviderUrl = OIDCProviderUrl;
|
|
||||||
oidcInfoProvider.realm = CHE_REALM;
|
|
||||||
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
OIDCInfo oidcInfo = oidcInfoProvider.get();
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|
@ -265,7 +257,7 @@ public class OIDCInfoProviderTest {
|
||||||
oidcInfo.getTokenPublicEndpoint());
|
oidcInfo.getTokenPublicEndpoint());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/logout",
|
||||||
oidcInfo.getEndSessionPublicEndpoint());
|
oidcInfo.getEndSessionPublicEndpoint().get());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo",
|
serverUrl + "/realms/" + CHE_REALM + "/protocol/openid-connect/userinfo",
|
||||||
oidcInfo.getUserInfoInternalEndpoint());
|
oidcInfo.getUserInfoInternalEndpoint());
|
||||||
|
|
@ -278,17 +270,17 @@ public class OIDCInfoProviderTest {
|
||||||
expectedExceptions = RuntimeException.class,
|
expectedExceptions = RuntimeException.class,
|
||||||
expectedExceptionsMessageRegExp = "Either the '.*' or '.*' or '.*' property should be set")
|
expectedExceptionsMessageRegExp = "Either the '.*' or '.*' or '.*' property should be set")
|
||||||
public void shouldThrowErrorWhenAuthServerWasNotSet() {
|
public void shouldThrowErrorWhenAuthServerWasNotSet() {
|
||||||
OIDCInfoProvider oidcInfoProvider = new OIDCInfoProvider();
|
KeycloakOIDCInfoProvider oidcInfoProvider =
|
||||||
oidcInfoProvider.realm = CHE_REALM;
|
new KeycloakOIDCInfoProvider(null, null, null, CHE_REALM);
|
||||||
oidcInfoProvider.get();
|
oidcInfoProvider.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(
|
@Test(
|
||||||
expectedExceptions = RuntimeException.class,
|
expectedExceptions = RuntimeException.class,
|
||||||
expectedExceptionsMessageRegExp = "The '.*' property should be set")
|
expectedExceptionsMessageRegExp = "The '.*' property must be set")
|
||||||
public void shouldThrowErrorWhenRealmPropertyWasNotSet() {
|
public void shouldThrowErrorWhenRealmPropertyWasNotSet() {
|
||||||
OIDCInfoProvider oidcInfoProvider = new OIDCInfoProvider();
|
KeycloakOIDCInfoProvider oidcInfoProvider =
|
||||||
oidcInfoProvider.serverURL = TEST_URL;
|
new KeycloakOIDCInfoProvider(null, null, null, null);
|
||||||
oidcInfoProvider.get();
|
oidcInfoProvider.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,13 @@ package org.eclipse.che.multiuser.keycloak.shared;
|
||||||
public class KeycloakConstants {
|
public class KeycloakConstants {
|
||||||
|
|
||||||
private static final String KEYCLOAK_SETTING_PREFIX = "che.keycloak.";
|
private static final String KEYCLOAK_SETTING_PREFIX = "che.keycloak.";
|
||||||
private static final String KEYCLOAK_SETTINGS_ENDPOINT_PATH = "/keycloak/settings";
|
|
||||||
|
|
||||||
public static final String AUTH_SERVER_URL_SETTING = KEYCLOAK_SETTING_PREFIX + "auth_server_url";
|
|
||||||
public static final String AUTH_SERVER_URL_INTERNAL_SETTING =
|
|
||||||
KEYCLOAK_SETTING_PREFIX + "auth_internal_server_url";
|
|
||||||
|
|
||||||
public static final String REALM_SETTING = KEYCLOAK_SETTING_PREFIX + "realm";
|
public static final String REALM_SETTING = KEYCLOAK_SETTING_PREFIX + "realm";
|
||||||
public static final String CLIENT_ID_SETTING = KEYCLOAK_SETTING_PREFIX + "client_id";
|
public static final String CLIENT_ID_SETTING = KEYCLOAK_SETTING_PREFIX + "client_id";
|
||||||
public static final String OIDC_PROVIDER_SETTING = KEYCLOAK_SETTING_PREFIX + "oidc_provider";
|
|
||||||
public static final String USERNAME_CLAIM_SETTING = KEYCLOAK_SETTING_PREFIX + "username_claim";
|
|
||||||
public static final String USE_NONCE_SETTING = KEYCLOAK_SETTING_PREFIX + "use_nonce";
|
public static final String USE_NONCE_SETTING = KEYCLOAK_SETTING_PREFIX + "use_nonce";
|
||||||
public static final String USE_FIXED_REDIRECT_URLS_SETTING =
|
public static final String USE_FIXED_REDIRECT_URLS_SETTING =
|
||||||
KEYCLOAK_SETTING_PREFIX + "use_fixed_redirect_urls";
|
KEYCLOAK_SETTING_PREFIX + "use_fixed_redirect_urls";
|
||||||
public static final String JS_ADAPTER_URL_SETTING = KEYCLOAK_SETTING_PREFIX + "js_adapter_url";
|
public static final String JS_ADAPTER_URL_SETTING = KEYCLOAK_SETTING_PREFIX + "js_adapter_url";
|
||||||
public static final String ALLOWED_CLOCK_SKEW_SEC =
|
|
||||||
KEYCLOAK_SETTING_PREFIX + "allowed_clock_skew_sec";
|
|
||||||
|
|
||||||
public static final String OSO_ENDPOINT_SETTING = KEYCLOAK_SETTING_PREFIX + "oso.endpoint";
|
public static final String OSO_ENDPOINT_SETTING = KEYCLOAK_SETTING_PREFIX + "oso.endpoint";
|
||||||
public static final String PROFILE_ENDPOINT_SETTING =
|
public static final String PROFILE_ENDPOINT_SETTING =
|
||||||
|
|
@ -48,8 +39,4 @@ public class KeycloakConstants {
|
||||||
KEYCLOAK_SETTING_PREFIX + "redirect_url.dashboard";
|
KEYCLOAK_SETTING_PREFIX + "redirect_url.dashboard";
|
||||||
public static final String FIXED_REDIRECT_URL_FOR_IDE =
|
public static final String FIXED_REDIRECT_URL_FOR_IDE =
|
||||||
KEYCLOAK_SETTING_PREFIX + "redirect_url.ide";
|
KEYCLOAK_SETTING_PREFIX + "redirect_url.ide";
|
||||||
|
|
||||||
public static String getEndpoint(String apiEndpoint) {
|
|
||||||
return apiEndpoint + KEYCLOAK_SETTINGS_ENDPOINT_PATH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@
|
||||||
<groupId>org.eclipse.che.multiuser</groupId>
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
<artifactId>che-multiuser-keycloak-shared</artifactId>
|
<artifactId>che-multiuser-keycloak-shared</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<artifactId>che-multiuser-oidc</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.keycloak.server;
|
||||||
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.*;
|
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.AUTH_SERVER_URL_INTERNAL_SETTING;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.AUTH_SERVER_URL_SETTING;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
@ -30,6 +32,7 @@ import org.eclipse.che.api.user.server.event.BeforeUserRemovedEvent;
|
||||||
import org.eclipse.che.commons.annotation.Nullable;
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
import org.eclipse.che.core.db.cascade.CascadeEventSubscriber;
|
import org.eclipse.che.core.db.cascade.CascadeEventSubscriber;
|
||||||
import org.eclipse.che.inject.ConfigurationException;
|
import org.eclipse.che.inject.ConfigurationException;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import org.eclipse.che.api.core.NotFoundException;
|
||||||
import org.eclipse.che.api.core.ServerException;
|
import org.eclipse.che.api.core.ServerException;
|
||||||
import org.eclipse.che.api.core.rest.DefaultHttpJsonRequest;
|
import org.eclipse.che.api.core.rest.DefaultHttpJsonRequest;
|
||||||
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
||||||
|
import org.eclipse.che.multiuser.oidc.OIDCInfo;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.testng.annotations.BeforeMethod;
|
import org.testng.annotations.BeforeMethod;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright (c) 2012-2021 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
|
||||||
|
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<artifactId>che-multiuser-parent</artifactId>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<version>7.40.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>che-multiuser-oidc</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>Che Multiuser :: OIDC</name>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>jwks-rsa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
|
<artifactId>che-core-api-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
|
<artifactId>che-core-api-model</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
|
<artifactId>che-core-api-user</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
|
<artifactId>che-core-commons-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.core</groupId>
|
||||||
|
<artifactId>che-core-commons-inject</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<artifactId>che-multiuser-api-authentication-commons</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<artifactId>che-multiuser-api-authorization</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.inject</groupId>
|
||||||
|
<artifactId>jakarta.inject-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-testng</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.testng</groupId>
|
||||||
|
<artifactId>testng</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -9,9 +9,12 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Red Hat, Inc. - initial API and implementation
|
* Red Hat, Inc. - initial API and implementation
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.oidc;
|
||||||
|
|
||||||
/** OIDCInfo - POJO object to store information about Keycloak api. */
|
import java.util.Optional;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
/** OIDCInfo - POJO object to store information about OIDC api. */
|
||||||
public class OIDCInfo {
|
public class OIDCInfo {
|
||||||
|
|
||||||
private final String tokenPublicEndpoint;
|
private final String tokenPublicEndpoint;
|
||||||
|
|
@ -38,7 +41,6 @@ public class OIDCInfo {
|
||||||
this.userInfoInternalEndpoint = userInfoInternalEndpoint;
|
this.userInfoInternalEndpoint = userInfoInternalEndpoint;
|
||||||
this.jwksPublicUri = jwksPublicUri;
|
this.jwksPublicUri = jwksPublicUri;
|
||||||
this.jwksInternalUri = jwksInternalUri;
|
this.jwksInternalUri = jwksInternalUri;
|
||||||
|
|
||||||
this.authServerURL = authServerURL;
|
this.authServerURL = authServerURL;
|
||||||
this.authServerPublicURL = authServerPublicURL;
|
this.authServerPublicURL = authServerPublicURL;
|
||||||
}
|
}
|
||||||
|
|
@ -48,11 +50,6 @@ public class OIDCInfo {
|
||||||
return tokenPublicEndpoint;
|
return tokenPublicEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return public log out url. */
|
|
||||||
public String getEndSessionPublicEndpoint() {
|
|
||||||
return endSessionPublicEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return public url to get user profile information. */
|
/** @return public url to get user profile information. */
|
||||||
public String getUserInfoPublicEndpoint() {
|
public String getUserInfoPublicEndpoint() {
|
||||||
return userInfoPublicEndpoint;
|
return userInfoPublicEndpoint;
|
||||||
|
|
@ -85,4 +82,21 @@ public class OIDCInfo {
|
||||||
public String getAuthServerPublicURL() {
|
public String getAuthServerPublicURL() {
|
||||||
return authServerPublicURL;
|
return authServerPublicURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", OIDCInfo.class.getSimpleName() + "[", "]")
|
||||||
|
.add("tokenPublicEndpoint='" + tokenPublicEndpoint + "'")
|
||||||
|
.add("userInfoPublicEndpoint='" + userInfoPublicEndpoint + "'")
|
||||||
|
.add("userInfoInternalEndpoint='" + userInfoInternalEndpoint + "'")
|
||||||
|
.add("jwksPublicUri='" + jwksPublicUri + "'")
|
||||||
|
.add("jwksInternalUri='" + jwksInternalUri + "'")
|
||||||
|
.add("authServerURL='" + authServerURL + "'")
|
||||||
|
.add("authServerPublicURL='" + authServerPublicURL + "'")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getEndSessionPublicEndpoint() {
|
||||||
|
return Optional.ofNullable(endSessionPublicEndpoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,13 +9,9 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Red Hat, Inc. - initial API and implementation
|
* Red Hat, Inc. - initial API and implementation
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.oidc;
|
||||||
|
|
||||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_INTERNAL_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.AUTH_SERVER_URL_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.OIDC_PROVIDER_SETTING;
|
|
||||||
import static org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants.REALM_SETTING;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonFactory;
|
import com.fasterxml.jackson.core.JsonFactory;
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
|
@ -34,32 +30,35 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OIDCInfoProvider retrieves OpenID Connect (OIDC) configuration for well-known endpoint. These
|
* OIDCInfoProvider retrieves OpenID Connect (OIDC) configuration for well-known endpoint. This
|
||||||
* information is useful to provide access to the Keycloak api.
|
* information is useful to provide access to the OIDC api.
|
||||||
*/
|
*/
|
||||||
public class OIDCInfoProvider implements Provider<OIDCInfo> {
|
public class OIDCInfoProvider implements Provider<OIDCInfo> {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(OIDCInfoProvider.class);
|
private static final Logger LOG = LoggerFactory.getLogger(OIDCInfoProvider.class);
|
||||||
|
|
||||||
@Inject
|
private static final String OIDC_SETTING_PREFIX = "che.oidc.";
|
||||||
@Nullable
|
|
||||||
@Named(AUTH_SERVER_URL_SETTING)
|
public static final String AUTH_SERVER_URL_SETTING = OIDC_SETTING_PREFIX + "auth_server_url";
|
||||||
|
public static final String AUTH_SERVER_URL_INTERNAL_SETTING =
|
||||||
|
OIDC_SETTING_PREFIX + "auth_internal_server_url";
|
||||||
|
public static final String OIDC_PROVIDER_SETTING = OIDC_SETTING_PREFIX + "oidc_provider";
|
||||||
|
public static final String OIDC_USERNAME_CLAIM_SETTING = OIDC_SETTING_PREFIX + "username_claim";
|
||||||
|
public static final String OIDC_ALLOWED_CLOCK_SKEW_SEC =
|
||||||
|
OIDC_SETTING_PREFIX + "allowed_clock_skew_sec";
|
||||||
|
|
||||||
protected String serverURL;
|
protected String serverURL;
|
||||||
|
|
||||||
@Inject
|
|
||||||
@Nullable
|
|
||||||
@Named(AUTH_SERVER_URL_INTERNAL_SETTING)
|
|
||||||
protected String serverInternalURL;
|
protected String serverInternalURL;
|
||||||
|
|
||||||
@Inject
|
|
||||||
@Nullable
|
|
||||||
@Named(OIDC_PROVIDER_SETTING)
|
|
||||||
protected String oidcProviderUrl;
|
protected String oidcProviderUrl;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Nullable
|
public OIDCInfoProvider(
|
||||||
@Named(REALM_SETTING)
|
@Nullable @Named(AUTH_SERVER_URL_SETTING) String serverURL,
|
||||||
protected String realm;
|
@Nullable @Named(AUTH_SERVER_URL_INTERNAL_SETTING) String serverInternalURL,
|
||||||
|
@Nullable @Named(OIDC_PROVIDER_SETTING) String oidcProviderUrl) {
|
||||||
|
this.serverURL = serverURL;
|
||||||
|
this.serverInternalURL = serverInternalURL;
|
||||||
|
this.oidcProviderUrl = oidcProviderUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return OIDCInfo with OIDC settings information. */
|
/** @return OIDCInfo with OIDC settings information. */
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -84,7 +83,9 @@ public class OIDCInfoProvider implements Provider<OIDCInfo> {
|
||||||
String userInfoPublicEndpoint =
|
String userInfoPublicEndpoint =
|
||||||
setPublicUrl((String) openIdConfiguration.get("userinfo_endpoint"));
|
setPublicUrl((String) openIdConfiguration.get("userinfo_endpoint"));
|
||||||
String endSessionPublicEndpoint =
|
String endSessionPublicEndpoint =
|
||||||
setPublicUrl((String) openIdConfiguration.get("end_session_endpoint"));
|
openIdConfiguration.containsKey("end_session_endpoint")
|
||||||
|
? setPublicUrl((String) openIdConfiguration.get("end_session_endpoint"))
|
||||||
|
: null;
|
||||||
String jwksPublicUri = setPublicUrl((String) openIdConfiguration.get("jwks_uri"));
|
String jwksPublicUri = setPublicUrl((String) openIdConfiguration.get("jwks_uri"));
|
||||||
String jwksInternalUri = setInternalUrl(jwksPublicUri);
|
String jwksInternalUri = setInternalUrl(jwksPublicUri);
|
||||||
String userInfoInternalEndpoint = setInternalUrl(userInfoPublicEndpoint);
|
String userInfoInternalEndpoint = setInternalUrl(userInfoPublicEndpoint);
|
||||||
|
|
@ -107,7 +108,7 @@ public class OIDCInfoProvider implements Provider<OIDCInfo> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getWellKnownEndpoint(String serverAuthUrl) {
|
private String getWellKnownEndpoint(String serverAuthUrl) {
|
||||||
String wellKnownEndpoint = firstNonNull(oidcProviderUrl, serverAuthUrl + "/realms/" + realm);
|
String wellKnownEndpoint = firstNonNull(oidcProviderUrl, constructServerAuthUrl(serverAuthUrl));
|
||||||
if (!wellKnownEndpoint.endsWith("/")) {
|
if (!wellKnownEndpoint.endsWith("/")) {
|
||||||
wellKnownEndpoint = wellKnownEndpoint + "/";
|
wellKnownEndpoint = wellKnownEndpoint + "/";
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +116,11 @@ public class OIDCInfoProvider implements Provider<OIDCInfo> {
|
||||||
return wellKnownEndpoint;
|
return wellKnownEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate() {
|
protected String constructServerAuthUrl(String serverAuthUrl) {
|
||||||
|
return serverAuthUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validate() {
|
||||||
if (serverURL == null && serverInternalURL == null && oidcProviderUrl == null) {
|
if (serverURL == null && serverInternalURL == null && oidcProviderUrl == null) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Either the '"
|
"Either the '"
|
||||||
|
|
@ -126,10 +131,6 @@ public class OIDCInfoProvider implements Provider<OIDCInfo> {
|
||||||
+ OIDC_PROVIDER_SETTING
|
+ OIDC_PROVIDER_SETTING
|
||||||
+ "' property should be set");
|
+ "' property should be set");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oidcProviderUrl == null && realm == null) {
|
|
||||||
throw new RuntimeException("The '" + REALM_SETTING + "' property should be set");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String setInternalUrl(String endpointUrl) {
|
private String setInternalUrl(String endpointUrl) {
|
||||||
|
|
@ -9,13 +9,12 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Red Hat, Inc. - initial API and implementation
|
* Red Hat, Inc. - initial API and implementation
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.oidc;
|
||||||
|
|
||||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
|
||||||
|
|
||||||
import com.auth0.jwk.GuavaCachedJwkProvider;
|
import com.auth0.jwk.GuavaCachedJwkProvider;
|
||||||
import com.auth0.jwk.JwkProvider;
|
import com.auth0.jwk.JwkProvider;
|
||||||
import com.auth0.jwk.UrlJwkProvider;
|
import com.auth0.jwk.UrlJwkProvider;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
@ -23,14 +22,14 @@ import javax.inject.Provider;
|
||||||
import org.eclipse.che.inject.ConfigurationException;
|
import org.eclipse.che.inject.ConfigurationException;
|
||||||
|
|
||||||
/** Constructs {@link UrlJwkProvider} based on Jwk endpoint from keycloak settings */
|
/** Constructs {@link UrlJwkProvider} based on Jwk endpoint from keycloak settings */
|
||||||
public class KeycloakJwkProvider implements Provider<JwkProvider> {
|
public class OIDCJwkProvider implements Provider<JwkProvider> {
|
||||||
|
|
||||||
private final JwkProvider jwkProvider;
|
private final JwkProvider jwkProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public KeycloakJwkProvider(OIDCInfo oidcInfo) throws MalformedURLException {
|
public OIDCJwkProvider(OIDCInfo oidcInfo) throws MalformedURLException {
|
||||||
final String jwksUrl =
|
final String jwksUrl =
|
||||||
isNullOrEmpty(oidcInfo.getJwksInternalUri())
|
Strings.isNullOrEmpty(oidcInfo.getJwksInternalUri())
|
||||||
? oidcInfo.getJwksPublicUri()
|
? oidcInfo.getJwksPublicUri()
|
||||||
: oidcInfo.getJwksInternalUri();
|
: oidcInfo.getJwksInternalUri();
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||||
* This program and the accompanying materials are made
|
* This program and the accompanying materials are made
|
||||||
* available under the terms of the Eclipse Public License 2.0
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||||
|
|
@ -9,30 +9,32 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Red Hat, Inc. - initial API and implementation
|
* Red Hat, Inc. - initial API and implementation
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.multiuser.keycloak.server;
|
package org.eclipse.che.multiuser.oidc;
|
||||||
|
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_ALLOWED_CLOCK_SKEW_SEC;
|
||||||
|
|
||||||
import io.jsonwebtoken.JwtParser;
|
import io.jsonwebtoken.JwtParser;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SigningKeyResolver;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import org.eclipse.che.multiuser.keycloak.shared.KeycloakConstants;
|
|
||||||
|
|
||||||
/** Provides instance of {@link JwtParser} */
|
/** Provides instance of {@link JwtParser} */
|
||||||
@Singleton
|
@Singleton
|
||||||
public class KeycloakJwtParserProvider implements Provider<JwtParser> {
|
public class OIDCJwtParserProvider implements Provider<JwtParser> {
|
||||||
|
|
||||||
private final JwtParser jwtParser;
|
private final JwtParser jwtParser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public KeycloakJwtParserProvider(
|
public OIDCJwtParserProvider(
|
||||||
@Named(KeycloakConstants.ALLOWED_CLOCK_SKEW_SEC) long allowedClockSkewSec,
|
@Named(OIDC_ALLOWED_CLOCK_SKEW_SEC) long allowedClockSkewSec,
|
||||||
KeycloakSigningKeyResolver keycloakSigningKeyResolver) {
|
SigningKeyResolver signingKeyResolver) {
|
||||||
this.jwtParser =
|
this.jwtParser =
|
||||||
Jwts.parser()
|
Jwts.parserBuilder()
|
||||||
|
.setSigningKeyResolver(signingKeyResolver)
|
||||||
.setAllowedClockSkewSeconds(allowedClockSkewSec)
|
.setAllowedClockSkewSeconds(allowedClockSkewSec)
|
||||||
.setSigningKeyResolver(keycloakSigningKeyResolver);
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.multiuser.oidc;
|
||||||
|
|
||||||
|
import com.auth0.jwk.JwkException;
|
||||||
|
import com.auth0.jwk.JwkProvider;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.JwsHeader;
|
||||||
|
import io.jsonwebtoken.JwtException;
|
||||||
|
import io.jsonwebtoken.SigningKeyResolverAdapter;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/** Resolves signing key based on id from JWT header */
|
||||||
|
@Singleton
|
||||||
|
public class OIDCSigningKeyResolver extends SigningKeyResolverAdapter {
|
||||||
|
|
||||||
|
private final JwkProvider jwkProvider;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(OIDCSigningKeyResolver.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected OIDCSigningKeyResolver(JwkProvider jwkProvider) {
|
||||||
|
this.jwkProvider = jwkProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Key resolveSigningKey(JwsHeader header, String plaintext) {
|
||||||
|
return getJwtPublicKey(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Key resolveSigningKey(JwsHeader header, Claims claims) {
|
||||||
|
return getJwtPublicKey(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized PublicKey getJwtPublicKey(JwsHeader<?> header) {
|
||||||
|
String kid = header.getKeyId();
|
||||||
|
if (kid == null) {
|
||||||
|
LOG.warn(
|
||||||
|
"'kid' is missing in the JWT token header. This is not possible to validate the token with OIDC provider keys");
|
||||||
|
throw new JwtException("'kid' is missing in the JWT token header.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return jwkProvider.get(kid).getPublicKey();
|
||||||
|
} catch (JwkException e) {
|
||||||
|
throw new JwtException(
|
||||||
|
"Error during the retrieval of the public key during JWT token validation", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.multiuser.oidc.filter;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_USERNAME_CLAIM_SETTING;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jws;
|
||||||
|
import io.jsonwebtoken.JwtParser;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import org.eclipse.che.api.core.ConflictException;
|
||||||
|
import org.eclipse.che.api.core.ServerException;
|
||||||
|
import org.eclipse.che.api.core.model.user.User;
|
||||||
|
import org.eclipse.che.api.user.server.UserManager;
|
||||||
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
|
import org.eclipse.che.commons.subject.Subject;
|
||||||
|
import org.eclipse.che.commons.subject.SubjectImpl;
|
||||||
|
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
|
||||||
|
import org.eclipse.che.multiuser.api.authentication.commons.filter.MultiUserEnvironmentInitializationFilter;
|
||||||
|
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
|
||||||
|
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
|
||||||
|
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This filter uses given token directly. It's used for native Kubernetes user authentication.
|
||||||
|
* Requests without token or with invalid token are rejected.
|
||||||
|
*
|
||||||
|
* <p>It also makes sure that User is present in Che database. If not, it will create the User from
|
||||||
|
* JWT token claims. The username claim is configured with {@link
|
||||||
|
* org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_USERNAME_CLAIM_SETTING}.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class OidcTokenInitializationFilter
|
||||||
|
extends MultiUserEnvironmentInitializationFilter<Jws<Claims>> {
|
||||||
|
private static final String EMAIL_CLAIM = "email";
|
||||||
|
private static final String NAME_CLAIM = "name";
|
||||||
|
protected static final String DEFAULT_USERNAME_CLAIM = NAME_CLAIM;
|
||||||
|
|
||||||
|
private final JwtParser jwtParser;
|
||||||
|
private final PermissionChecker permissionChecker;
|
||||||
|
private final UserManager userManager;
|
||||||
|
private final String usernameClaim;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public OidcTokenInitializationFilter(
|
||||||
|
PermissionChecker permissionChecker,
|
||||||
|
JwtParser jwtParser,
|
||||||
|
SessionStore sessionStore,
|
||||||
|
RequestTokenExtractor tokenExtractor,
|
||||||
|
UserManager userManager,
|
||||||
|
@Nullable @Named(OIDC_USERNAME_CLAIM_SETTING) String usernameClaim) {
|
||||||
|
super(sessionStore, tokenExtractor);
|
||||||
|
this.permissionChecker = permissionChecker;
|
||||||
|
this.jwtParser = jwtParser;
|
||||||
|
this.userManager = userManager;
|
||||||
|
this.usernameClaim = isNullOrEmpty(usernameClaim) ? DEFAULT_USERNAME_CLAIM : usernameClaim;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<Jws<Claims>> processToken(String token) {
|
||||||
|
return Optional.ofNullable(jwtParser.parseClaimsJws(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getUserId(Jws<Claims> processedToken) {
|
||||||
|
return processedToken.getBody().getSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Subject extractSubject(String token, Jws<Claims> processedToken) {
|
||||||
|
try {
|
||||||
|
Claims claims = processedToken.getBody();
|
||||||
|
User user =
|
||||||
|
userManager.getOrCreateUser(
|
||||||
|
claims.getSubject(),
|
||||||
|
claims.get(EMAIL_CLAIM, String.class),
|
||||||
|
claims.get(usernameClaim, String.class));
|
||||||
|
return new AuthorizedSubject(
|
||||||
|
new SubjectImpl(user.getName(), user.getId(), token, false), permissionChecker);
|
||||||
|
} catch (ServerException | ConflictException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2021 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.multiuser.oidc.filter;
|
||||||
|
|
||||||
|
import static org.eclipse.che.multiuser.oidc.filter.OidcTokenInitializationFilter.DEFAULT_USERNAME_CLAIM;
|
||||||
|
import static org.mockito.Mockito.lenient;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertTrue;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jws;
|
||||||
|
import io.jsonwebtoken.JwtParser;
|
||||||
|
import org.eclipse.che.api.core.ConflictException;
|
||||||
|
import org.eclipse.che.api.core.ServerException;
|
||||||
|
import org.eclipse.che.api.core.model.user.User;
|
||||||
|
import org.eclipse.che.api.user.server.UserManager;
|
||||||
|
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
|
||||||
|
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
|
||||||
|
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
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 OidcTokenInitializationFilterTest {
|
||||||
|
|
||||||
|
@Mock private PermissionChecker permissionsChecker;
|
||||||
|
@Mock private JwtParser jwtParser;
|
||||||
|
@Mock private SessionStore sessionStore;
|
||||||
|
@Mock private RequestTokenExtractor tokenExtractor;
|
||||||
|
@Mock private UserManager userManager;
|
||||||
|
private final String usernameClaim = "blabolClaim";
|
||||||
|
private final String TEST_USERNAME = "jondoe";
|
||||||
|
private final String TEST_USER_EMAIL = "jon@doe";
|
||||||
|
private final String TEST_USERID = "jon1337";
|
||||||
|
|
||||||
|
private final String TEST_TOKEN = "abcToken";
|
||||||
|
|
||||||
|
@Mock private Jws<Claims> jwsClaims;
|
||||||
|
@Mock private Claims claims;
|
||||||
|
|
||||||
|
OidcTokenInitializationFilter tokenInitializationFilter;
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp() {
|
||||||
|
tokenInitializationFilter =
|
||||||
|
new OidcTokenInitializationFilter(
|
||||||
|
permissionsChecker,
|
||||||
|
jwtParser,
|
||||||
|
sessionStore,
|
||||||
|
tokenExtractor,
|
||||||
|
userManager,
|
||||||
|
usernameClaim);
|
||||||
|
|
||||||
|
lenient().when(jwsClaims.getBody()).thenReturn(claims);
|
||||||
|
lenient().when(claims.getSubject()).thenReturn(TEST_USERID);
|
||||||
|
lenient().when(claims.get("email", String.class)).thenReturn(TEST_USER_EMAIL);
|
||||||
|
lenient().when(claims.get(usernameClaim, String.class)).thenReturn(TEST_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessToken() {
|
||||||
|
when(jwtParser.parseClaimsJws(TEST_TOKEN)).thenReturn(jwsClaims);
|
||||||
|
|
||||||
|
var returnedClaims = tokenInitializationFilter.processToken(TEST_TOKEN).get();
|
||||||
|
|
||||||
|
assertEquals(returnedClaims, jwsClaims);
|
||||||
|
verify(jwtParser).parseClaimsJws(TEST_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessEmptyToken() {
|
||||||
|
var returnedClaims = tokenInitializationFilter.processToken("");
|
||||||
|
|
||||||
|
assertTrue(returnedClaims.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserId() {
|
||||||
|
var userId = tokenInitializationFilter.getUserId(jwsClaims);
|
||||||
|
|
||||||
|
assertEquals(userId, TEST_USERID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtractSubject() throws ServerException, ConflictException {
|
||||||
|
User createdUser = mock(User.class);
|
||||||
|
when(createdUser.getId()).thenReturn(TEST_USERID);
|
||||||
|
when(createdUser.getName()).thenReturn(TEST_USERNAME);
|
||||||
|
when(userManager.getOrCreateUser(TEST_USERID, TEST_USER_EMAIL, TEST_USERNAME))
|
||||||
|
.thenReturn(createdUser);
|
||||||
|
|
||||||
|
var subject = tokenInitializationFilter.extractSubject(TEST_TOKEN, jwsClaims);
|
||||||
|
|
||||||
|
assertEquals(subject.getUserId(), TEST_USERID);
|
||||||
|
assertEquals(subject.getUserName(), TEST_USERNAME);
|
||||||
|
assertEquals(subject.getToken(), TEST_TOKEN);
|
||||||
|
verify(userManager).getOrCreateUser(TEST_USERID, TEST_USER_EMAIL, TEST_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider = "usernameClaims")
|
||||||
|
public void testDefaultUsernameClaimWhenEmpty(String customUsernameClaim)
|
||||||
|
throws ServerException, ConflictException {
|
||||||
|
tokenInitializationFilter =
|
||||||
|
new OidcTokenInitializationFilter(
|
||||||
|
permissionsChecker,
|
||||||
|
jwtParser,
|
||||||
|
sessionStore,
|
||||||
|
tokenExtractor,
|
||||||
|
userManager,
|
||||||
|
customUsernameClaim);
|
||||||
|
User createdUser = mock(User.class);
|
||||||
|
when(createdUser.getId()).thenReturn(TEST_USERID);
|
||||||
|
when(createdUser.getName()).thenReturn(TEST_USERNAME);
|
||||||
|
when(userManager.getOrCreateUser(TEST_USERID, TEST_USER_EMAIL, TEST_USERNAME))
|
||||||
|
.thenReturn(createdUser);
|
||||||
|
when(claims.get(DEFAULT_USERNAME_CLAIM, String.class)).thenReturn(TEST_USERNAME);
|
||||||
|
|
||||||
|
var subject = tokenInitializationFilter.extractSubject(TEST_TOKEN, jwsClaims);
|
||||||
|
|
||||||
|
assertEquals(subject.getUserId(), TEST_USERID);
|
||||||
|
assertEquals(subject.getUserName(), TEST_USERNAME);
|
||||||
|
assertEquals(subject.getToken(), TEST_TOKEN);
|
||||||
|
verify(userManager).getOrCreateUser(TEST_USERID, TEST_USER_EMAIL, TEST_USERNAME);
|
||||||
|
verify(claims).get(DEFAULT_USERNAME_CLAIM, String.class);
|
||||||
|
verify(claims, never()).get(usernameClaim, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DataProvider
|
||||||
|
public static Object[][] usernameClaims() {
|
||||||
|
return new Object[][] {{""}, {null}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,5 +32,6 @@
|
||||||
<module>machine-auth</module>
|
<module>machine-auth</module>
|
||||||
<module>personal-account</module>
|
<module>personal-account</module>
|
||||||
<module>integration-tests</module>
|
<module>integration-tests</module>
|
||||||
|
<module>oidc</module>
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
15
pom.xml
15
pom.xml
|
|
@ -271,6 +271,11 @@
|
||||||
<artifactId>logging-interceptor</artifactId>
|
<artifactId>logging-interceptor</artifactId>
|
||||||
<version>${com.squareup.okhttp3.version}</version>
|
<version>${com.squareup.okhttp3.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>mockwebserver</artifactId>
|
||||||
|
<version>${com.squareup.okhttp3.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.squareup.okhttp3</groupId>
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
<artifactId>okhttp</artifactId>
|
<artifactId>okhttp</artifactId>
|
||||||
|
|
@ -307,6 +312,11 @@
|
||||||
<artifactId>commons-lang</artifactId>
|
<artifactId>commons-lang</artifactId>
|
||||||
<version>${commons-lang.version}</version>
|
<version>${commons-lang.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.fabric8</groupId>
|
||||||
|
<artifactId>openshift-server-mock</artifactId>
|
||||||
|
<version>${io.fabric8.kubernetes-client}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.mweirauch</groupId>
|
<groupId>io.github.mweirauch</groupId>
|
||||||
<artifactId>micrometer-jvm-extras</artifactId>
|
<artifactId>micrometer-jvm-extras</artifactId>
|
||||||
|
|
@ -1115,6 +1125,11 @@
|
||||||
<artifactId>che-multiuser-machine-authentication-shared</artifactId>
|
<artifactId>che-multiuser-machine-authentication-shared</artifactId>
|
||||||
<version>${che.version}</version>
|
<version>${che.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
|
<artifactId>che-multiuser-oidc</artifactId>
|
||||||
|
<version>${che.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.che.multiuser</groupId>
|
<groupId>org.eclipse.che.multiuser</groupId>
|
||||||
<artifactId>che-multiuser-permission-devfile</artifactId>
|
<artifactId>che-multiuser-permission-devfile</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,6 @@
|
||||||
<name>Che Core :: API :: InfraProxy</name>
|
<name>Che Core :: API :: InfraProxy</name>
|
||||||
<description>Provides direct HTTP access to the underlying infrastructure web API.</description>
|
<description>Provides direct HTTP access to the underlying infrastructure web API.</description>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-databind</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,7 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.api.infraproxy.server;
|
package org.eclipse.che.api.infraproxy.server;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import jakarta.ws.rs.DELETE;
|
import jakarta.ws.rs.DELETE;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
|
|
@ -24,7 +22,6 @@ import jakarta.ws.rs.PUT;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.core.Context;
|
import jakarta.ws.rs.core.Context;
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
@ -52,39 +49,17 @@ public class InfrastructureApiService extends Service {
|
||||||
|
|
||||||
private final boolean allowed;
|
private final boolean allowed;
|
||||||
private final RuntimeInfrastructure runtimeInfrastructure;
|
private final RuntimeInfrastructure runtimeInfrastructure;
|
||||||
private final ObjectMapper mapper;
|
|
||||||
|
|
||||||
@Context private MediaType mediaType;
|
private static boolean determineAllowed(String identityProvider) {
|
||||||
|
return identityProvider != null;
|
||||||
private static boolean determineAllowed(
|
|
||||||
String infra, String identityProvider, boolean allowedForKubernetes) {
|
|
||||||
if ("openshift".equals(infra)) {
|
|
||||||
return identityProvider != null && identityProvider.startsWith("openshift");
|
|
||||||
}
|
|
||||||
return allowedForKubernetes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public InfrastructureApiService(
|
public InfrastructureApiService(
|
||||||
@Nullable @Named("che.infra.openshift.oauth_identity_provider") String identityProvider,
|
@Nullable @Named("che.infra.openshift.oauth_identity_provider") String identityProvider,
|
||||||
@Named("che.infra.kubernetes.enable_unsupported_k8s") boolean allowedForKubernetes,
|
|
||||||
RuntimeInfrastructure runtimeInfrastructure) {
|
RuntimeInfrastructure runtimeInfrastructure) {
|
||||||
this(
|
this.runtimeInfrastructure = runtimeInfrastructure;
|
||||||
System.getenv("CHE_INFRASTRUCTURE_ACTIVE"),
|
this.allowed = determineAllowed(identityProvider);
|
||||||
allowedForKubernetes,
|
|
||||||
identityProvider,
|
|
||||||
runtimeInfrastructure);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
InfrastructureApiService(
|
|
||||||
String infraName,
|
|
||||||
boolean allowedForKubernetes,
|
|
||||||
String identityProvider,
|
|
||||||
RuntimeInfrastructure infra) {
|
|
||||||
this.runtimeInfrastructure = infra;
|
|
||||||
this.mapper = new ObjectMapper();
|
|
||||||
this.allowed = determineAllowed(infraName, identityProvider, allowedForKubernetes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
|
|
||||||
|
|
@ -37,49 +37,13 @@ public class InfrastructureApiServiceTest {
|
||||||
|
|
||||||
@BeforeMethod
|
@BeforeMethod
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
apiService =
|
apiService = new InfrastructureApiService("openshift-identityProvider", infra);
|
||||||
new InfrastructureApiService("openshift", false, "openshift-identityProvider", infra);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailsAuthWhenNotAllowedForKubernetesAndNotOnOpenShift() throws Exception {
|
|
||||||
// given
|
|
||||||
apiService =
|
|
||||||
new InfrastructureApiService("not-openshift", false, "openshift-identityProvider", infra);
|
|
||||||
|
|
||||||
// when
|
|
||||||
Response response =
|
|
||||||
given()
|
|
||||||
.contentType("application/json; charset=utf-8")
|
|
||||||
.when()
|
|
||||||
.get("/unsupported/k8s/nazdar/");
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(response.getStatusCode(), 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailsAuthWhenNotUsingOpenShiftIdentityProvider() throws Exception {
|
|
||||||
// given
|
|
||||||
apiService =
|
|
||||||
new InfrastructureApiService("openshift", false, "not-openshift-identityProvider", infra);
|
|
||||||
|
|
||||||
// when
|
|
||||||
Response response =
|
|
||||||
given()
|
|
||||||
.contentType("application/json; charset=utf-8")
|
|
||||||
.when()
|
|
||||||
.get("/unsupported/k8s/nazdar/");
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(response.getStatusCode(), 403);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolvesCallWhenAllowedForKubernetesOnKubernetes() throws Exception {
|
public void testResolvesCallWhenAllowedForKubernetesOnKubernetes() throws Exception {
|
||||||
// given
|
// given
|
||||||
apiService =
|
apiService = new InfrastructureApiService("not-openshift-identityProvider", infra);
|
||||||
new InfrastructureApiService("kubernetes", true, "not-openshift-identityProvider", infra);
|
|
||||||
when(infra.sendDirectInfrastructureRequest(any(), any(), any(), any()))
|
when(infra.sendDirectInfrastructureRequest(any(), any(), any(), any()))
|
||||||
.thenReturn(
|
.thenReturn(
|
||||||
jakarta.ws.rs.core.Response.ok()
|
jakarta.ws.rs.core.Response.ok()
|
||||||
|
|
@ -98,24 +62,6 @@ public class InfrastructureApiServiceTest {
|
||||||
assertEquals(response.getContentType(), "application/json;charset=utf-8");
|
assertEquals(response.getContentType(), "application/json;charset=utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailsAuthWhenAllowedForKubernetesOnOpenshiftWithNonOpenshiftIdentityProvider()
|
|
||||||
throws Exception {
|
|
||||||
// given
|
|
||||||
apiService =
|
|
||||||
new InfrastructureApiService("openshift", true, "not-openshift-identityProvider", infra);
|
|
||||||
|
|
||||||
// when
|
|
||||||
Response response =
|
|
||||||
given()
|
|
||||||
.contentType("application/json; charset=utf-8")
|
|
||||||
.when()
|
|
||||||
.get("/unsupported/k8s/nazdar/");
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(response.getStatusCode(), 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGet() throws Exception {
|
public void testGet() throws Exception {
|
||||||
// given
|
// given
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue