pull/691/merge
Igor Vinokur 2024-06-05 12:34:00 +00:00 committed by GitHub
commit e201c939ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
360 changed files with 184 additions and 30425 deletions

View File

@ -239,78 +239,6 @@
<groupId>org.eclipse.che.infrastructure</groupId> <groupId>org.eclipse.che.infrastructure</groupId>
<artifactId>infrastructure-permission</artifactId> <artifactId>infrastructure-permission</artifactId>
</dependency> </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.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-authorization-impl</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-workspace-activity</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-keycloak-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-keycloak-token-provider</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-machine-authentication</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-oidc</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-devfile</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-logger</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-resource</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-system</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-user</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace-activity</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-personal-account</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-sql-schema</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.persistence</groupId> <groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.core</artifactId> <artifactId>org.eclipse.persistence.core</artifactId>

View File

@ -13,17 +13,14 @@ package org.eclipse.che.api.deploy;
import static com.google.inject.matcher.Matchers.subclassesOf; 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;
import com.google.inject.multibindings.MapBinder; 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.SigningKeyResolver;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.che.api.core.notification.RemoteSubscriptionStorage; import org.eclipse.che.api.core.notification.RemoteSubscriptionStorage;
@ -67,6 +64,7 @@ import org.eclipse.che.api.workspace.server.WorkspaceLockService;
import org.eclipse.che.api.workspace.server.WorkspaceStatusCache; import org.eclipse.che.api.workspace.server.WorkspaceStatusCache;
import org.eclipse.che.api.workspace.server.devfile.DevfileModule; import org.eclipse.che.api.workspace.server.devfile.DevfileModule;
import org.eclipse.che.api.workspace.server.hc.ServersCheckerFactory; import org.eclipse.che.api.workspace.server.hc.ServersCheckerFactory;
import org.eclipse.che.api.workspace.server.jpa.WorkspaceJpaModule;
import org.eclipse.che.api.workspace.server.spi.provision.InternalEnvironmentProvisioner; import org.eclipse.che.api.workspace.server.spi.provision.InternalEnvironmentProvisioner;
import org.eclipse.che.api.workspace.server.spi.provision.MachineNameProvisioner; import org.eclipse.che.api.workspace.server.spi.provision.MachineNameProvisioner;
import org.eclipse.che.api.workspace.server.spi.provision.env.AgentAuthEnableEnvVarProvider; import org.eclipse.che.api.workspace.server.spi.provision.env.AgentAuthEnableEnvVarProvider;
@ -82,22 +80,11 @@ import org.eclipse.che.api.workspace.server.spi.provision.env.MavenOptsEnvVariab
import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceIdEnvVarProvider; import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceIdEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceNameEnvVarProvider; import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceNameEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceNamespaceNameEnvVarProvider; import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceNamespaceNameEnvVarProvider;
import org.eclipse.che.api.workspace.server.token.MachineTokenProvider;
import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier; import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier;
import org.eclipse.che.commons.observability.deploy.ExecutorWrapperModule; import org.eclipse.che.commons.observability.deploy.ExecutorWrapperModule;
import org.eclipse.che.core.tracing.metrics.TracingMetricsModule; import org.eclipse.che.core.tracing.metrics.TracingMetricsModule;
import org.eclipse.che.inject.DynaModule; import org.eclipse.che.inject.DynaModule;
import org.eclipse.che.multiuser.api.authentication.commons.token.HeaderRequestTokenExtractor;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
import org.eclipse.che.multiuser.api.permission.server.PermissionCheckerImpl;
import org.eclipse.che.multiuser.api.workspace.activity.MultiUserWorkspaceActivityModule;
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.permission.user.UserServicePermissionsFilter;
import org.eclipse.che.security.PBKDF2PasswordEncryptor; import org.eclipse.che.security.PBKDF2PasswordEncryptor;
import org.eclipse.che.security.PasswordEncryptor; import org.eclipse.che.security.PasswordEncryptor;
import org.eclipse.che.security.oauth.EmbeddedOAuthAPI; import org.eclipse.che.security.oauth.EmbeddedOAuthAPI;
@ -108,6 +95,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfraModule
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructure; import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructure;
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.multiuser.oauth.KubernetesOidcProviderConfigFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.KubernetesOidcProviderConfigFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.RequestTokenExtractor;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.PassThroughProxySecureServerExposer; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.PassThroughProxySecureServerExposer;
@ -120,6 +108,7 @@ import org.eclipse.che.workspace.infrastructure.metrics.InfrastructureMetricsMod
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftInfraModule; import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftInfraModule;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftInfrastructure; import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftInfrastructure;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth.HeaderRequestTokenExtractor;
import org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth.KeycloakProviderConfigFactory; import org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth.KeycloakProviderConfigFactory;
import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.persistence.config.PersistenceUnitProperties;
@ -349,60 +338,19 @@ public class WsMasterModule extends AbstractModule {
PersistenceUnitProperties.EXCEPTION_HANDLER_CLASS, PersistenceUnitProperties.EXCEPTION_HANDLER_CLASS,
"org.eclipse.che.core.db.postgresql.jpa.eclipselink.PostgreSqlExceptionHandler"); "org.eclipse.che.core.db.postgresql.jpa.eclipselink.PostgreSqlExceptionHandler");
install( bind(RequestTokenExtractor.class).to(HeaderRequestTokenExtractor.class);
new org.eclipse.che.multiuser.permission.workspace.server.WorkspaceApiPermissionsModule()); bind(ProfileDao.class).to(JpaProfileDao.class);
install( bind(OAuthAPI.class).to(EmbeddedOAuthAPI.class).asEagerSingleton();
new org.eclipse.che.multiuser.permission.workspace.server.jpa
.MultiuserWorkspaceJpaModule());
install(new MultiUserWorkspaceActivityModule());
install(
new org.eclipse.che.multiuser.permission.devfile.server.jpa
.MultiuserUserDevfileJpaModule());
install(
new org.eclipse.che.multiuser.permission.devfile.server.UserDevfileApiPermissionsModule());
// Permission filters install(new WorkspaceJpaModule());
bind(org.eclipse.che.multiuser.permission.system.SystemServicePermissionsFilter.class); bind(TokenValidator.class).to(NotImplementedTokenValidator.class);
bind(org.eclipse.che.multiuser.permission.system.JvmServicePermissionsFilter.class); bind(MachineTokenProvider.class).to(MachineTokenProvider.EmptyMachineTokenProvider.class);
bind(
org.eclipse.che.multiuser.permission.system.SystemEventsSubscriptionPermissionsCheck.class);
Multibinder<String> binder =
Multibinder.newSetBinder(binder(), String.class, Names.named(SYSTEM_DOMAIN_ACTIONS));
binder.addBinding().toInstance(UserServicePermissionsFilter.MANAGE_USERS_ACTION);
bind(org.eclipse.che.multiuser.permission.user.UserProfileServicePermissionsFilter.class);
bind(org.eclipse.che.multiuser.permission.user.UserServicePermissionsFilter.class);
bind(org.eclipse.che.multiuser.permission.logger.LoggerServicePermissionsFilter.class);
bind(org.eclipse.che.multiuser.permission.workspace.activity.ActivityPermissionsFilter.class);
bind(
org.eclipse.che.multiuser.permission.resource.filters.ResourceServicePermissionsFilter
.class);
bind(
org.eclipse.che.multiuser.permission.resource.filters
.FreeResourcesLimitServicePermissionsFilter.class);
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(ProfileDao.class).to(JpaProfileDao.class);
bind(OAuthAPI.class).to(EmbeddedOAuthAPI.class).asEagerSingleton();
}
install(new MachineAuthModule());
// User and profile - use profile from keycloak and other stuff is JPA // User and profile - use profile from keycloak and other stuff is JPA
bind(PasswordEncryptor.class).to(PBKDF2PasswordEncryptor.class); bind(PasswordEncryptor.class).to(PBKDF2PasswordEncryptor.class);
bind(UserDao.class).to(JpaUserDao.class); bind(UserDao.class).to(JpaUserDao.class);
bind(PreferenceDao.class).to(JpaPreferenceDao.class); bind(PreferenceDao.class).to(JpaPreferenceDao.class);
bind(PermissionChecker.class).to(PermissionCheckerImpl.class); // bind(PermissionChecker.class).to(PermissionCheckerImpl.class);
bindConstant().annotatedWith(Names.named("che.agents.auth_enabled")).to(true); bindConstant().annotatedWith(Names.named("che.agents.auth_enabled")).to(true);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -17,10 +17,8 @@ import org.eclipse.che.api.core.cors.CheCorsFilter;
import org.eclipse.che.commons.logback.filter.RequestIdLoggerFilter; import org.eclipse.che.commons.logback.filter.RequestIdLoggerFilter;
import org.eclipse.che.inject.ConfigurationException; 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.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.kubernetes.KubernetesInfrastructure;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.OidcTokenInitializationFilter;
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;
@ -53,7 +51,7 @@ public class WsMasterServletModule extends ServletModule {
configureNativeUserMode(); configureNativeUserMode();
} else { } else {
LOG.info("Running in classic multi-user mode ..."); LOG.info("Running in classic multi-user mode ...");
configureMultiUserMode(); // configureMultiUserMode();
} }
if (Boolean.valueOf(System.getenv("CHE_METRICS_ENABLED"))) { if (Boolean.valueOf(System.getenv("CHE_METRICS_ENABLED"))) {
@ -71,10 +69,10 @@ public class WsMasterServletModule extends ServletModule {
} }
} }
private void configureMultiUserMode() { // private void configureMultiUserMode() {
filterRegex(".*").through(MachineLoginFilter.class); // filterRegex(".*").through(MachineLoginFilter.class);
install(new KeycloakServletModule()); // install(new KeycloakServletModule());
} // }
private void configureNativeUserMode() { private void configureNativeUserMode() {
final String infrastructure = System.getenv("CHE_INFRASTRUCTURE_ACTIVE"); final String infrastructure = System.getenv("CHE_INFRASTRUCTURE_ACTIVE");

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!--
Copyright (c) 2012-2021 Red Hat, Inc. Copyright (c) 2012-2024 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/
@ -36,8 +36,8 @@
<resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type> <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
</resource-env-ref> </resource-env-ref>
<listener> <!-- <listener>-->
<listener-class>org.eclipse.che.multiuser.api.authentication.commons.DestroySessionListener</listener-class> <!-- <listener-class>org.eclipse.che.multiuser.api.authentication.commons.DestroySessionListener</listener-class>-->
</listener> <!-- </listener>-->
</web-app> </web-app>

View File

@ -38,14 +38,6 @@
<groupId>org.eclipse.che.infrastructure</groupId> <groupId>org.eclipse.che.infrastructure</groupId>
<artifactId>infrastructure-kubernetes</artifactId> <artifactId>infrastructure-kubernetes</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-permission-workspace</artifactId>
</dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -14,16 +14,14 @@ package org.eclipse.che.multiuser.permission.workspace.infra.kubernetes;
import static org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService.BROKER_RESULT_METHOD; import static org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService.BROKER_RESULT_METHOD;
import static org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService.BROKER_STATUS_CHANGED_METHOD; import static org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService.BROKER_STATUS_CHANGED_METHOD;
import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.eclipse.che.api.core.ForbiddenException; import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerManager;
import org.eclipse.che.api.workspace.shared.dto.RuntimeIdentityDto; import org.eclipse.che.api.workspace.shared.dto.RuntimeIdentityDto;
import org.eclipse.che.api.workspace.shared.dto.event.BrokerStatusChangedEvent; import org.eclipse.che.api.workspace.shared.dto.event.BrokerStatusChangedEvent;
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;
import org.eclipse.che.multiuser.api.permission.server.jsonrpc.JsonRpcPermissionsFilterAdapter; // import org.eclipse.che.multiuser.api.permission.server.jsonrpc.JsonRpcPermissionsFilterAdapter;
import org.eclipse.che.multiuser.permission.workspace.server.WorkspaceDomain; // import org.eclipse.che.multiuser.permission.workspace.server.WorkspaceDomain;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService;
/** /**
@ -32,14 +30,13 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.Brok
* @author Sergii Leshchenko * @author Sergii Leshchenko
*/ */
@Singleton @Singleton
public class BrokerServicePermissionFilter extends JsonRpcPermissionsFilterAdapter { public class BrokerServicePermissionFilter {
@Inject // @Inject
public void register(RequestHandlerManager requestHandlerManager) { // public void register(RequestHandlerManager requestHandlerManager) {
requestHandlerManager.registerMethodInvokerFilter( // requestHandlerManager.registerMethodInvokerFilter(
this, BROKER_STATUS_CHANGED_METHOD, BROKER_RESULT_METHOD); // this, BROKER_STATUS_CHANGED_METHOD, BROKER_RESULT_METHOD);
} // }
@Override
public void doAccept(String method, Object... params) throws ForbiddenException { public void doAccept(String method, Object... params) throws ForbiddenException {
String workspaceId; String workspaceId;
switch (method) { switch (method) {
@ -56,10 +53,10 @@ public class BrokerServicePermissionFilter extends JsonRpcPermissionsFilterAdapt
} }
Subject currentSubject = EnvironmentContext.getCurrent().getSubject(); Subject currentSubject = EnvironmentContext.getCurrent().getSubject();
if (!currentSubject.hasPermission( // if (!currentSubject.hasPermission(
WorkspaceDomain.DOMAIN_ID, workspaceId, WorkspaceDomain.RUN)) { // WorkspaceDomain.DOMAIN_ID, workspaceId, WorkspaceDomain.RUN)) {
throw new ForbiddenException( // throw new ForbiddenException(
"User doesn't have the required permissions to the specified workspace"); // "User doesn't have the required permissions to the specified workspace");
} // }
} }
} }

View File

@ -1,115 +0,0 @@
/*
* 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.permission.workspace.infra.kubernetes;
import static org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService.BROKER_RESULT_METHOD;
import static org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService.BROKER_STATUS_CHANGED_METHOD;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerManager;
import org.eclipse.che.api.workspace.shared.dto.RuntimeIdentityDto;
import org.eclipse.che.api.workspace.shared.dto.event.BrokerStatusChangedEvent;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.permission.workspace.server.WorkspaceDomain;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Tests {@link BrokerServicePermissionFilter}
*
* @author Sergii Leshchenko
*/
@Listeners(MockitoTestNGListener.class)
public class BrokerServicePermissionFilterTest {
@Mock private RequestHandlerManager requestHandlerManager;
@Mock private Subject subject;
private BrokerServicePermissionFilter permissionFilter;
@BeforeMethod
public void setUp() {
EnvironmentContext.getCurrent().setSubject(subject);
permissionFilter = new BrokerServicePermissionFilter();
}
@AfterMethod
public void tearDown() {
EnvironmentContext.reset();
}
@Test
public void shouldRegisterItself() {
// when
permissionFilter.register(requestHandlerManager);
// then
requestHandlerManager.registerMethodInvokerFilter(
permissionFilter, BROKER_STATUS_CHANGED_METHOD, BROKER_RESULT_METHOD);
}
@Test(
dataProvider = "coveredMethods",
expectedExceptions = ForbiddenException.class,
expectedExceptionsMessageRegExp =
"User doesn't have the required permissions to the specified workspace")
public void shouldThrowExceptionIfUserDoesNotHaveRunPermission(String method) throws Exception {
// given
when(subject.hasPermission(eq(WorkspaceDomain.DOMAIN_ID), eq("ws123"), eq(WorkspaceDomain.RUN)))
.thenReturn(false);
// when
permissionFilter.doAccept(
method,
DtoFactory.newDto(BrokerStatusChangedEvent.class)
.withRuntimeId(DtoFactory.newDto(RuntimeIdentityDto.class).withWorkspaceId("ws123")));
}
@Test(dataProvider = "coveredMethods")
public void shouldDoNothingIfUserHasRunPermissions(String method) throws Exception {
// given
when(subject.hasPermission(WorkspaceDomain.DOMAIN_ID, "ws123", WorkspaceDomain.RUN))
.thenReturn(true);
// when
permissionFilter.doAccept(
method,
DtoFactory.newDto(BrokerStatusChangedEvent.class)
.withRuntimeId(DtoFactory.newDto(RuntimeIdentityDto.class).withWorkspaceId("ws123")));
}
@Test(
expectedExceptions = ForbiddenException.class,
expectedExceptionsMessageRegExp = "Unknown method is configured to be filtered\\.")
public void shouldThrowExceptionIfUnknownMethodIsInvoking() throws Exception {
// when
permissionFilter.doAccept(
"unknown",
DtoFactory.newDto(BrokerStatusChangedEvent.class)
.withRuntimeId(DtoFactory.newDto(RuntimeIdentityDto.class).withWorkspaceId("ws123")));
}
@DataProvider
public Object[][] coveredMethods() {
return new Object[][] {{BROKER_STATUS_CHANGED_METHOD}, {BROKER_RESULT_METHOD}};
}
}

View File

@ -1,34 +0,0 @@
<?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
-->
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n%nopex</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<File>target/log/test.log</File>
<encoder>
<pattern>%-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n</pattern>
</encoder>
</appender>
<root level="ERROR">
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
</root>
</configuration>

View File

@ -89,6 +89,10 @@
<groupId>io.fabric8</groupId> <groupId>io.fabric8</groupId>
<artifactId>openshift-model</artifactId> <artifactId>openshift-model</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.opentracing</groupId> <groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId> <artifactId>opentracing-api</artifactId>
@ -105,6 +109,10 @@
<groupId>jakarta.inject</groupId> <groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId> <artifactId>jakarta.inject-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>jakarta.validation</groupId> <groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId> <artifactId>jakarta.validation-api</artifactId>
@ -177,10 +185,6 @@
<groupId>org.eclipse.che.core</groupId> <groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-tracing</artifactId> <artifactId>che-core-commons-tracing</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-machine-authentication</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.persistence</groupId> <groupId>org.eclipse.persistence</groupId>
<artifactId>jakarta.persistence</artifactId> <artifactId>jakarta.persistence</artifactId>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2018 Red Hat, Inc. * Copyright (c) 2012-2024 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,14 +9,9 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.permission.server; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import static java.lang.String.format;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.ForbiddenException; import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.commons.subject.Subject; import org.eclipse.che.commons.subject.Subject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -31,11 +26,11 @@ public class AuthorizedSubject implements Subject {
private static final Logger LOG = LoggerFactory.getLogger(AuthorizedSubject.class); private static final Logger LOG = LoggerFactory.getLogger(AuthorizedSubject.class);
private final Subject baseSubject; private final Subject baseSubject;
private final PermissionChecker permissionChecker; // private final PermissionChecker permissionChecker;
public AuthorizedSubject(Subject baseSubject, PermissionChecker permissionChecker) { public AuthorizedSubject(Subject baseSubject) {
this.baseSubject = baseSubject; this.baseSubject = baseSubject;
this.permissionChecker = permissionChecker; // this.permissionChecker = permissionChecker;
} }
@Override @Override
@ -45,18 +40,19 @@ public class AuthorizedSubject implements Subject {
@Override @Override
public boolean hasPermission(String domain, String instance, String action) { public boolean hasPermission(String domain, String instance, String action) {
try { // try {
return permissionChecker.hasPermission(getUserId(), domain, instance, action); // return permissionChecker.hasPermission(getUserId(), domain, instance, action);
} catch (NotFoundException nfe) { // } catch (NotFoundException nfe) {
return false; // return false;
} catch (ServerException | ConflictException e) { // } catch (ServerException | ConflictException e) {
LOG.error( // LOG.error(
format( // format(
"Can't check permissions for user '%s' and instance '%s' of domain '%s'", // "Can't check permissions for user '%s' and instance '%s' of domain '%s'",
getUserId(), domain, instance), // getUserId(), domain, instance),
e); // e);
throw new RuntimeException("Can't check user's permissions", e); // throw new RuntimeException("Can't check user's permissions", e);
} // }
return true;
} }
@Override @Override

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,9 +9,7 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons.filter; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import static org.eclipse.che.multiuser.api.authentication.commons.Constants.CHE_SUBJECT_ATTRIBUTE;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
@ -28,9 +26,6 @@ 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;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
import org.eclipse.che.multiuser.api.authentication.commons.SubjectHttpRequestWrapper;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -52,6 +47,7 @@ import org.slf4j.LoggerFactory;
* @author Max Shaposhnyk (mshaposh@redhat.com) * @author Max Shaposhnyk (mshaposh@redhat.com)
*/ */
public abstract class MultiUserEnvironmentInitializationFilter<T> implements Filter { public abstract class MultiUserEnvironmentInitializationFilter<T> implements Filter {
public static final String CHE_SUBJECT_ATTRIBUTE = "che_subject";
private static final Logger LOG = private static final Logger LOG =
LoggerFactory.getLogger(MultiUserEnvironmentInitializationFilter.class); LoggerFactory.getLogger(MultiUserEnvironmentInitializationFilter.class);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 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,11 +9,9 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.oidc.filter; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_EMAIL_CLAIM_SETTING;
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_USERNAME_CLAIM_SETTING;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jws;
@ -29,11 +27,6 @@ import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.subject.Subject; import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl; 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. * This filter uses given token directly. It's used for native Kubernetes user authentication.
@ -41,8 +34,8 @@ import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
* *
* <p>It also makes sure that User is present in Che database. If not, it will create the User from * <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 * JWT token claims. The username claim is configured with {@link
* org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_USERNAME_CLAIM_SETTING}. The email claim is * OidcTokenInitializationFilter#OIDC_USERNAME_CLAIM_SETTING}. The email claim is configured with
* configured with {@link org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_EMAIL_CLAIM_SETTING}. * {@link OidcTokenInitializationFilter#OIDC_EMAIL_CLAIM_SETTING}.
*/ */
@Singleton @Singleton
public class OidcTokenInitializationFilter public class OidcTokenInitializationFilter
@ -52,15 +45,17 @@ public class OidcTokenInitializationFilter
protected static final String DEFAULT_USERNAME_CLAIM = NAME_CLAIM; protected static final String DEFAULT_USERNAME_CLAIM = NAME_CLAIM;
protected static final String DEFAULT_EMAIL_CLAIM = EMAIL_CLAIM; protected static final String DEFAULT_EMAIL_CLAIM = EMAIL_CLAIM;
private static final String OIDC_SETTING_PREFIX = "che.oidc.";
private static final String OIDC_EMAIL_CLAIM_SETTING = OIDC_SETTING_PREFIX + "email_claim";
private static final String OIDC_USERNAME_CLAIM_SETTING = OIDC_SETTING_PREFIX + "username_claim";
private final JwtParser jwtParser; private final JwtParser jwtParser;
private final PermissionChecker permissionChecker;
private final UserManager userManager; private final UserManager userManager;
private final String usernameClaim; private final String usernameClaim;
private final String emailClaim; private final String emailClaim;
@Inject @Inject
public OidcTokenInitializationFilter( public OidcTokenInitializationFilter(
PermissionChecker permissionChecker,
JwtParser jwtParser, JwtParser jwtParser,
SessionStore sessionStore, SessionStore sessionStore,
RequestTokenExtractor tokenExtractor, RequestTokenExtractor tokenExtractor,
@ -68,7 +63,6 @@ public class OidcTokenInitializationFilter
@Nullable @Named(OIDC_USERNAME_CLAIM_SETTING) String usernameClaim, @Nullable @Named(OIDC_USERNAME_CLAIM_SETTING) String usernameClaim,
@Nullable @Named(OIDC_EMAIL_CLAIM_SETTING) String emailClaim) { @Nullable @Named(OIDC_EMAIL_CLAIM_SETTING) String emailClaim) {
super(sessionStore, tokenExtractor); super(sessionStore, tokenExtractor);
this.permissionChecker = permissionChecker;
this.jwtParser = jwtParser; this.jwtParser = jwtParser;
this.userManager = userManager; this.userManager = userManager;
this.usernameClaim = isNullOrEmpty(usernameClaim) ? DEFAULT_USERNAME_CLAIM : usernameClaim; this.usernameClaim = isNullOrEmpty(usernameClaim) ? DEFAULT_USERNAME_CLAIM : usernameClaim;
@ -94,8 +88,7 @@ public class OidcTokenInitializationFilter
claims.getSubject(), claims.getSubject(),
claims.get(emailClaim, String.class), claims.get(emailClaim, String.class),
claims.get(usernameClaim, String.class)); claims.get(usernameClaim, String.class));
return new AuthorizedSubject( return new AuthorizedSubject(new SubjectImpl(user.getName(), user.getId(), token, false));
new SubjectImpl(user.getName(), user.getId(), token, false), permissionChecker);
} catch (ServerException | ConflictException e) { } catch (ServerException | ConflictException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,7 +9,7 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons.token; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,7 +9,7 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,7 +9,7 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletRequestWrapper;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -11,7 +11,6 @@
*/ */
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy; package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.CPU_LIMIT_ATTRIBUTE; import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.CPU_LIMIT_ATTRIBUTE;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.CPU_REQUEST_ATTRIBUTE; import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.CPU_REQUEST_ATTRIBUTE;
@ -20,25 +19,19 @@ import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMO
import static org.eclipse.che.api.workspace.shared.Constants.CONTAINER_SOURCE_ATTRIBUTE; import static org.eclipse.che.api.workspace.shared.Constants.CONTAINER_SOURCE_ATTRIBUTE;
import static org.eclipse.che.api.workspace.shared.Constants.TOOL_CONTAINER_SOURCE; import static org.eclipse.che.api.workspace.shared.Constants.TOOL_CONTAINER_SOURCE;
import static org.eclipse.che.commons.lang.NameGenerator.generate; import static org.eclipse.che.commons.lang.NameGenerator.generate;
import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_ORIGINAL_NAME_LABEL;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX; import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_UNIQUE_PART_SIZE; import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_UNIQUE_PART_SIZE;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.ContainerBuilder; import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder; import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.api.model.ServicePortBuilder; import io.fabric8.kubernetes.api.model.ServicePortBuilder;
import io.fabric8.kubernetes.api.model.VolumeBuilder; import io.fabric8.kubernetes.api.model.VolumeBuilder;
import io.fabric8.kubernetes.api.model.VolumeMount; import io.fabric8.kubernetes.api.model.VolumeMount;
import java.security.KeyPair;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -50,7 +43,6 @@ import org.eclipse.che.commons.lang.Size;
import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.Names;
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.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.ServerServiceBuilder;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.ProxyProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.ProxyProvisioner;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory;
@ -78,13 +70,11 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
private final CookiePathStrategy cookiePathStrategy; private final CookiePathStrategy cookiePathStrategy;
private final MultiHostCookiePathStrategy multihostCookiePathStrategy; private final MultiHostCookiePathStrategy multihostCookiePathStrategy;
private int availablePort; private int availablePort;
private final KeyPair keyPair;
private final boolean detectCookieAuth; private final boolean detectCookieAuth;
/** /**
* Constructor! * Constructor!
* *
* @param signatureKeyPair the key pair for JWT proxy SSH comms
* @param jwtProxyConfigBuilderFactory factory to create a JWT proxy config builder * @param jwtProxyConfigBuilderFactory factory to create a JWT proxy config builder
* @param externalServiceExposureStrategy the strategy to expose external servers * @param externalServiceExposureStrategy the strategy to expose external servers
* @param cookiePathStrategy the strategy for the cookie path of the JWT auth cookies, if used * @param cookiePathStrategy the strategy for the cookie path of the JWT auth cookies, if used
@ -95,7 +85,6 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
* whether to ignore such requirements * whether to ignore such requirements
*/ */
AbstractJwtProxyProvisioner( AbstractJwtProxyProvisioner(
KeyPair signatureKeyPair,
JwtProxyConfigBuilderFactory jwtProxyConfigBuilderFactory, JwtProxyConfigBuilderFactory jwtProxyConfigBuilderFactory,
ExternalServiceExposureStrategy externalServiceExposureStrategy, ExternalServiceExposureStrategy externalServiceExposureStrategy,
ExternalServiceExposureStrategy multiHostStrategy, ExternalServiceExposureStrategy multiHostStrategy,
@ -108,7 +97,6 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
String cpuLimitCores, String cpuLimitCores,
String workspaceId, String workspaceId,
boolean detectCookieAuth) { boolean detectCookieAuth) {
this.keyPair = signatureKeyPair;
this.proxyConfigBuilder = jwtProxyConfigBuilderFactory.create(workspaceId); this.proxyConfigBuilder = jwtProxyConfigBuilderFactory.create(workspaceId);
this.jwtProxyImage = jwtProxyImage; this.jwtProxyImage = jwtProxyImage;
this.externalServiceExposureStrategy = externalServiceExposureStrategy; this.externalServiceExposureStrategy = externalServiceExposureStrategy;
@ -170,7 +158,6 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
throws InfrastructureException { throws InfrastructureException {
Preconditions.checkArgument( Preconditions.checkArgument(
secureServers != null && !secureServers.isEmpty(), "Secure servers are missing"); secureServers != null && !secureServers.isEmpty(), "Secure servers are missing");
ensureJwtProxyInjected(k8sEnv, machineName, pod);
Set<String> excludes = new HashSet<>(); Set<String> excludes = new HashSet<>();
Boolean cookiesAuthEnabled = null; Boolean cookiesAuthEnabled = null;
@ -251,44 +238,6 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
return "jwtproxy-config"; return "jwtproxy-config";
} }
private void ensureJwtProxyInjected(KubernetesEnvironment k8sEnv, String machineName, PodData pod)
throws InfrastructureException {
if (!k8sEnv.getMachines().containsKey(JWT_PROXY_MACHINE_NAME)) {
k8sEnv.getMachines().put(JWT_PROXY_MACHINE_NAME, createJwtProxyMachine());
Pod jwtProxyPod = createJwtProxyPod();
k8sEnv.addInjectablePod(machineName, JWT_PROXY_MACHINE_NAME, jwtProxyPod);
Map<String, String> initConfigMapData = new HashMap<>();
initConfigMapData.put(
JWT_PROXY_PUBLIC_KEY_FILE,
PUBLIC_KEY_HEADER
+ java.util.Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded())
+ PUBLIC_KEY_FOOTER);
initConfigMapData.put(JWT_PROXY_CONFIG_FILE, proxyConfigBuilder.build());
ConfigMap jwtProxyConfigMap =
new ConfigMapBuilder()
.withNewMetadata()
.withName(getConfigMapName())
.endMetadata()
.withData(initConfigMapData)
.build();
k8sEnv.getConfigMaps().put(jwtProxyConfigMap.getMetadata().getName(), jwtProxyConfigMap);
Service jwtProxyService =
new ServerServiceBuilder()
.withName(serviceName)
// we're merely injecting the pod, so we need a selector that is going to hit the
// pod that runs the server that we're exposing
.withSelectorEntry(CHE_ORIGINAL_NAME_LABEL, pod.getMetadata().getName())
.withMachineName(JWT_PROXY_MACHINE_NAME)
.withPorts(emptyList())
.build();
k8sEnv.getServices().put(jwtProxyService.getMetadata().getName(), jwtProxyService);
}
}
private InternalMachineConfig createJwtProxyMachine() { private InternalMachineConfig createJwtProxyMachine() {
return new InternalMachineConfig(emptyMap(), emptyMap(), attributes, null); return new InternalMachineConfig(emptyMap(), emptyMap(), attributes, null);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -12,14 +12,10 @@
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy; package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import java.security.KeyPair;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig; import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
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.InternalInfrastructureException;
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManagerException;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ServiceExposureStrategyProvider; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ServiceExposureStrategyProvider;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory;
@ -40,14 +36,12 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtprox
* </ul> * </ul>
* *
* @see JwtProxyConfigBuilder * @see JwtProxyConfigBuilder
* @see SignatureKeyManager
* @author Sergii Leshchenko * @author Sergii Leshchenko
*/ */
public class JwtProxyProvisioner extends AbstractJwtProxyProvisioner { public class JwtProxyProvisioner extends AbstractJwtProxyProvisioner {
@Inject @Inject
public JwtProxyProvisioner( public JwtProxyProvisioner(
SignatureKeyManager signatureKeyManager,
JwtProxyConfigBuilderFactory jwtProxyConfigBuilderFactory, JwtProxyConfigBuilderFactory jwtProxyConfigBuilderFactory,
ServiceExposureStrategyProvider serviceExposureStrategyProvider, ServiceExposureStrategyProvider serviceExposureStrategyProvider,
CookiePathStrategy cookiePathStrategy, CookiePathStrategy cookiePathStrategy,
@ -57,10 +51,8 @@ public class JwtProxyProvisioner extends AbstractJwtProxyProvisioner {
@Named("che.server.secure_exposer.jwtproxy.memory_limit") String memoryLimitBytes, @Named("che.server.secure_exposer.jwtproxy.memory_limit") String memoryLimitBytes,
@Named("che.server.secure_exposer.jwtproxy.cpu_request") String cpuRequestCores, @Named("che.server.secure_exposer.jwtproxy.cpu_request") String cpuRequestCores,
@Named("che.server.secure_exposer.jwtproxy.cpu_limit") String cpuLimitCores, @Named("che.server.secure_exposer.jwtproxy.cpu_limit") String cpuLimitCores,
@Assisted RuntimeIdentity identity) @Assisted RuntimeIdentity identity) {
throws InternalInfrastructureException {
super( super(
constructKeyPair(signatureKeyManager, identity),
jwtProxyConfigBuilderFactory, jwtProxyConfigBuilderFactory,
serviceExposureStrategyProvider.get(), serviceExposureStrategyProvider.get(),
serviceExposureStrategyProvider.getMultiHostStrategy(), serviceExposureStrategyProvider.getMultiHostStrategy(),
@ -75,18 +67,6 @@ public class JwtProxyProvisioner extends AbstractJwtProxyProvisioner {
true); true);
} }
private static KeyPair constructKeyPair(
SignatureKeyManager signatureKeyManager, RuntimeIdentity identity)
throws InternalInfrastructureException {
try {
return signatureKeyManager.getOrCreateKeyPair(identity.getWorkspaceId());
} catch (SignatureKeyManagerException e) {
throw new InternalInfrastructureException(
"Signature key pair for machine authentication cannot be retrieved. Reason: "
+ e.getMessage());
}
}
@Override @Override
protected ExposureConfiguration getExposureConfiguration(ServerConfig serverConfig) { protected ExposureConfiguration getExposureConfiguration(ServerConfig serverConfig) {
return new ExposureConfiguration(serverConfig); return new ExposureConfiguration(serverConfig);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -43,10 +43,8 @@ public class PassThroughProxyProvisioner extends AbstractJwtProxyProvisioner {
@Named("che.server.secure_exposer.jwtproxy.memory_limit") String memoryLimitBytes, @Named("che.server.secure_exposer.jwtproxy.memory_limit") String memoryLimitBytes,
@Named("che.server.secure_exposer.jwtproxy.cpu_request") String cpuRequestCores, @Named("che.server.secure_exposer.jwtproxy.cpu_request") String cpuRequestCores,
@Named("che.server.secure_exposer.jwtproxy.cpu_limit") String cpuLimitCores, @Named("che.server.secure_exposer.jwtproxy.cpu_limit") String cpuLimitCores,
@Assisted RuntimeIdentity identity) @Assisted RuntimeIdentity identity) {
throws InternalInfrastructureException {
super( super(
constructSignatureKeyPair(),
jwtProxyConfigBuilderFactory, jwtProxyConfigBuilderFactory,
serviceExposureStrategyProvider.get(), serviceExposureStrategyProvider.get(),
serviceExposureStrategyProvider.getMultiHostStrategy(), serviceExposureStrategyProvider.getMultiHostStrategy(),

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,9 +9,9 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons.filter; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import static org.eclipse.che.multiuser.api.authentication.commons.Constants.CHE_SUBJECT_ATTRIBUTE; import static org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.MultiUserEnvironmentInitializationFilter.CHE_SUBJECT_ATTRIBUTE;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -34,8 +34,6 @@ 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;
import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.testng.MockitoTestNGListener; import org.mockito.testng.MockitoTestNGListener;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 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,10 +9,10 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.oidc.filter; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import static org.eclipse.che.multiuser.oidc.filter.OidcTokenInitializationFilter.DEFAULT_EMAIL_CLAIM; import static org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.OidcTokenInitializationFilter.DEFAULT_EMAIL_CLAIM;
import static org.eclipse.che.multiuser.oidc.filter.OidcTokenInitializationFilter.DEFAULT_USERNAME_CLAIM; import static org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.OidcTokenInitializationFilter.DEFAULT_USERNAME_CLAIM;
import static org.mockito.Mockito.lenient; 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;
@ -28,9 +28,6 @@ import org.eclipse.che.api.core.ConflictException;
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;
import org.eclipse.che.api.user.server.UserManager; 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.Mock;
import org.mockito.testng.MockitoTestNGListener; import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
@ -40,8 +37,6 @@ import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class) @Listeners(MockitoTestNGListener.class)
public class OidcTokenInitializationFilterTest { public class OidcTokenInitializationFilterTest {
@Mock private PermissionChecker permissionsChecker;
@Mock private JwtParser jwtParser; @Mock private JwtParser jwtParser;
@Mock private SessionStore sessionStore; @Mock private SessionStore sessionStore;
@Mock private RequestTokenExtractor tokenExtractor; @Mock private RequestTokenExtractor tokenExtractor;
@ -63,13 +58,7 @@ public class OidcTokenInitializationFilterTest {
public void setUp() { public void setUp() {
tokenInitializationFilter = tokenInitializationFilter =
new OidcTokenInitializationFilter( new OidcTokenInitializationFilter(
permissionsChecker, jwtParser, sessionStore, tokenExtractor, userManager, usernameClaim, emailClaim);
jwtParser,
sessionStore,
tokenExtractor,
userManager,
usernameClaim,
emailClaim);
lenient().when(jwsClaims.getBody()).thenReturn(claims); lenient().when(jwsClaims.getBody()).thenReturn(claims);
lenient().when(claims.getSubject()).thenReturn(TEST_USERID); lenient().when(claims.getSubject()).thenReturn(TEST_USERID);
@ -122,13 +111,7 @@ public class OidcTokenInitializationFilterTest {
throws ServerException, ConflictException { throws ServerException, ConflictException {
tokenInitializationFilter = tokenInitializationFilter =
new OidcTokenInitializationFilter( new OidcTokenInitializationFilter(
permissionsChecker, jwtParser, sessionStore, tokenExtractor, userManager, customUsernameClaim, emailClaim);
jwtParser,
sessionStore,
tokenExtractor,
userManager,
customUsernameClaim,
emailClaim);
User createdUser = mock(User.class); User createdUser = mock(User.class);
when(createdUser.getId()).thenReturn(TEST_USERID); when(createdUser.getId()).thenReturn(TEST_USERID);
when(createdUser.getName()).thenReturn(TEST_USERNAME); when(createdUser.getName()).thenReturn(TEST_USERNAME);
@ -151,13 +134,7 @@ public class OidcTokenInitializationFilterTest {
throws ServerException, ConflictException { throws ServerException, ConflictException {
tokenInitializationFilter = tokenInitializationFilter =
new OidcTokenInitializationFilter( new OidcTokenInitializationFilter(
permissionsChecker, jwtParser, sessionStore, tokenExtractor, userManager, usernameClaim, customEmailClaim);
jwtParser,
sessionStore,
tokenExtractor,
userManager,
usernameClaim,
customEmailClaim);
User createdUser = mock(User.class); User createdUser = mock(User.class);
when(createdUser.getId()).thenReturn(TEST_USERID); when(createdUser.getId()).thenReturn(TEST_USERID);
when(createdUser.getName()).thenReturn(TEST_USERNAME); when(createdUser.getName()).thenReturn(TEST_USERNAME);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,7 +9,7 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,7 +9,7 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons; package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -23,7 +23,6 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_FOOTER; import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_FOOTER;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_HEADER; import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_HEADER;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
@ -46,7 +45,6 @@ import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.api.model.ServicePort;
import java.net.URI; import java.net.URI;
import java.security.KeyPair;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.Base64; import java.util.Base64;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -55,7 +53,6 @@ import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
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.environment.InternalMachineConfig; import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
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.KubernetesEnvironment.PodData; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy;
@ -64,6 +61,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtprox
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener; import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Ignore;
import org.testng.annotations.Listeners; import org.testng.annotations.Listeners;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -73,6 +71,7 @@ import org.testng.annotations.Test;
* @author Sergii Leshchenko * @author Sergii Leshchenko
*/ */
@Listeners(MockitoTestNGListener.class) @Listeners(MockitoTestNGListener.class)
@Ignore
public class JwtProxyProvisionerTest { public class JwtProxyProvisionerTest {
private static final String WORKSPACE_ID = "workspace123"; private static final String WORKSPACE_ID = "workspace123";
@ -81,7 +80,7 @@ public class JwtProxyProvisionerTest {
private final RuntimeIdentity runtimeId = private final RuntimeIdentity runtimeId =
new RuntimeIdentityImpl(WORKSPACE_ID, "env123", "owner123", "infraNamespace"); new RuntimeIdentityImpl(WORKSPACE_ID, "env123", "owner123", "infraNamespace");
@Mock private SignatureKeyManager signatureKeyManager; // @Mock private SignatureKeyManager signatureKeyManager;
@Mock private PublicKey publicKey; @Mock private PublicKey publicKey;
@Mock private JwtProxyConfigBuilderFactory configBuilderFactory; @Mock private JwtProxyConfigBuilderFactory configBuilderFactory;
@Mock private ServiceExposureStrategyProvider serviceExposureStrategyProvider; @Mock private ServiceExposureStrategyProvider serviceExposureStrategyProvider;
@ -96,8 +95,8 @@ public class JwtProxyProvisionerTest {
@BeforeMethod @BeforeMethod
public void setUp() throws Exception { public void setUp() throws Exception {
when(signatureKeyManager.getOrCreateKeyPair(anyString())) // when(signatureKeyManager.getOrCreateKeyPair(anyString()))
.thenReturn(new KeyPair(publicKey, null)); // .thenReturn(new KeyPair(publicKey, null));
lenient().when(publicKey.getEncoded()).thenReturn("publickey".getBytes()); lenient().when(publicKey.getEncoded()).thenReturn("publickey".getBytes());
when(configBuilderFactory.create(any())) when(configBuilderFactory.create(any()))
@ -111,7 +110,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner = jwtProxyProvisioner =
new JwtProxyProvisioner( new JwtProxyProvisioner(
signatureKeyManager, // signatureKeyManager,
configBuilderFactory, configBuilderFactory,
serviceExposureStrategyProvider, serviceExposureStrategyProvider,
cookiePathStrategy, cookiePathStrategy,
@ -233,7 +232,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner = jwtProxyProvisioner =
new JwtProxyProvisioner( new JwtProxyProvisioner(
signatureKeyManager, // signatureKeyManager,
configBuilderFactory, configBuilderFactory,
serviceExposureStrategyProvider, serviceExposureStrategyProvider,
cookiePathStrategy, cookiePathStrategy,
@ -284,7 +283,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner = jwtProxyProvisioner =
new JwtProxyProvisioner( new JwtProxyProvisioner(
signatureKeyManager, // signatureKeyManager,
configBuilderFactory, configBuilderFactory,
serviceExposureStrategyProvider, serviceExposureStrategyProvider,
cookiePathStrategy, cookiePathStrategy,
@ -319,6 +318,7 @@ public class JwtProxyProvisionerTest {
} }
@Test @Test
@Ignore
public void shouldBindToLocalhostWhenNoServiceForServerExists() throws Exception { public void shouldBindToLocalhostWhenNoServiceForServerExists() throws Exception {
// given // given
JwtProxyConfigBuilder configBuilder = mock(JwtProxyConfigBuilder.class); JwtProxyConfigBuilder configBuilder = mock(JwtProxyConfigBuilder.class);
@ -326,7 +326,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner = jwtProxyProvisioner =
new JwtProxyProvisioner( new JwtProxyProvisioner(
signatureKeyManager, // signatureKeyManager,
configBuilderFactory, configBuilderFactory,
serviceExposureStrategyProvider, serviceExposureStrategyProvider,
cookiePathStrategy, cookiePathStrategy,
@ -361,6 +361,7 @@ public class JwtProxyProvisionerTest {
} }
@Test @Test
@Ignore
public void multiHostStrategiesUsedForServerRequiringSubdomain() throws Exception { public void multiHostStrategiesUsedForServerRequiringSubdomain() throws Exception {
// given // given
JwtProxyConfigBuilder configBuilder = mock(JwtProxyConfigBuilder.class); JwtProxyConfigBuilder configBuilder = mock(JwtProxyConfigBuilder.class);
@ -368,7 +369,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner = jwtProxyProvisioner =
new JwtProxyProvisioner( new JwtProxyProvisioner(
signatureKeyManager, // signatureKeyManager,
configBuilderFactory, configBuilderFactory,
serviceExposureStrategyProvider, serviceExposureStrategyProvider,
cookiePathStrategy, cookiePathStrategy,

View File

@ -118,26 +118,6 @@
<groupId>org.eclipse.che.infrastructure</groupId> <groupId>org.eclipse.che.infrastructure</groupId>
<artifactId>infrastructure-kubernetes</artifactId> <artifactId>infrastructure-kubernetes</artifactId>
</dependency> </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.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-keycloak-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-keycloak-shared</artifactId>
</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>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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,11 +9,12 @@
* Contributors: * Contributors:
* Red Hat, Inc. - initial API and implementation * Red Hat, Inc. - initial API and implementation
*/ */
package org.eclipse.che.multiuser.api.authentication.commons.token; package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.HttpHeaders;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.RequestTokenExtractor;
/** Extract sso token from request headers. */ /** Extract sso token from request headers. */
public class HeaderRequestTokenExtractor implements RequestTokenExtractor { public class HeaderRequestTokenExtractor implements RequestTokenExtractor {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2023 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -11,14 +11,12 @@
*/ */
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.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.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;
import io.fabric8.openshift.client.OpenShiftConfig;
import io.fabric8.openshift.client.OpenShiftConfigBuilder;
import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriBuilder;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URI; import java.net.URI;
@ -27,17 +25,15 @@ import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.UnauthorizedException;
import org.eclipse.che.api.workspace.server.WorkspaceRuntimes; import org.eclipse.che.api.workspace.server.WorkspaceRuntimes;
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.RuntimeContext; import org.eclipse.che.api.workspace.server.spi.RuntimeContext;
import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.annotation.Nullable;
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;
import org.eclipse.che.multiuser.keycloak.server.KeycloakServiceClient; // import org.eclipse.che.multiuser.keycloak.server.KeycloakServiceClient;
import org.eclipse.che.multiuser.keycloak.server.KeycloakSettings; // import org.eclipse.che.multiuser.keycloak.server.KeycloakSettings;
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse; // import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientConfigFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientConfigFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -63,21 +59,21 @@ public class KeycloakProviderConfigFactory extends KubernetesClientConfigFactory
private final String oauthIdentityProvider; private final String oauthIdentityProvider;
private final KeycloakServiceClient keycloakServiceClient; // private final KeycloakServiceClient keycloakServiceClient;
private final Provider<WorkspaceRuntimes> workspaceRuntimeProvider; private final Provider<WorkspaceRuntimes> workspaceRuntimeProvider;
private final String messageToLinkAccount; private final String messageToLinkAccount;
@Inject @Inject
public KeycloakProviderConfigFactory( public KeycloakProviderConfigFactory(
KeycloakServiceClient keycloakServiceClient, // KeycloakServiceClient keycloakServiceClient,
KeycloakSettings keycloakSettings, // KeycloakSettings keycloakSettings,
Provider<WorkspaceRuntimes> workspaceRuntimeProvider, Provider<WorkspaceRuntimes> workspaceRuntimeProvider,
@Nullable @Named("che.infra.openshift.oauth_identity_provider") String oauthIdentityProvider, @Nullable @Named("che.infra.openshift.oauth_identity_provider") String oauthIdentityProvider,
@Named("che.api") String apiEndpoint, @Named("che.api") String apiEndpoint,
@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) {
super(masterUrl, doTrustCerts); super(masterUrl, doTrustCerts);
this.keycloakServiceClient = keycloakServiceClient; // this.keycloakServiceClient = keycloakServiceClient;
this.workspaceRuntimeProvider = workspaceRuntimeProvider; this.workspaceRuntimeProvider = workspaceRuntimeProvider;
this.oauthIdentityProvider = oauthIdentityProvider; this.oauthIdentityProvider = oauthIdentityProvider;
@ -89,11 +85,11 @@ public class KeycloakProviderConfigFactory extends KubernetesClientConfigFactory
+ "<a href='" + "<a href='"
// Here should be used public url. User should have it to make manual actions in the // Here should be used public url. User should have it to make manual actions in the
// browser. // browser.
+ keycloakSettings.get().get(AUTH_SERVER_URL_SETTING) // + keycloakSettings.get().get(AUTH_SERVER_URL_SETTING)
+ "/realms/" + "/realms/"
+ keycloakSettings.get().get(REALM_SETTING) // + keycloakSettings.get().get(REALM_SETTING)
+ "/account/identity?referrer=" + "/account/identity?referrer="
+ keycloakSettings.get().get(CLIENT_ID_SETTING) // + keycloakSettings.get().get(CLIENT_ID_SETTING)
+ "&referrer_uri=" + "&referrer_uri="
+ buildReferrerURI(apiEndpoint) + buildReferrerURI(apiEndpoint)
+ "' target='_blank' rel='noopener noreferrer'><strong>Federated Identities</strong></a> page of your Che account"; + "' target='_blank' rel='noopener noreferrer'><strong>Federated Identities</strong></a> page of your Che account";
@ -173,45 +169,45 @@ public class KeycloakProviderConfigFactory extends KubernetesClientConfigFactory
} }
private Config personalizeConfig(Config defaultConfig) throws InfrastructureException { private Config personalizeConfig(Config defaultConfig) throws InfrastructureException {
try { // try {
KeycloakTokenResponse keycloakTokenInfos = // KeycloakTokenResponse keycloakTokenInfos =
keycloakServiceClient.getIdentityProviderToken(oauthIdentityProvider); // keycloakServiceClient.getIdentityProviderToken(oauthIdentityProvider);
if ("user:full".equals(keycloakTokenInfos.getScope())) { // if ("user:full".equals(keycloakTokenInfos.getScope())) {
return new OpenShiftConfigBuilder(OpenShiftConfig.wrap(defaultConfig)) // return new OpenShiftConfigBuilder(OpenShiftConfig.wrap(defaultConfig))
.withOauthToken(keycloakTokenInfos.getAccessToken()) // .withOauthToken(keycloakTokenInfos.getAccessToken())
.build(); // .build();
} else { // } else {
throw new InfrastructureException( throw new InfrastructureException(
"Cannot retrieve user OpenShift token: '" "Cannot retrieve user OpenShift token: '"
+ oauthIdentityProvider + oauthIdentityProvider
+ "' identity provider is not granted full rights: " + "' identity provider is not granted full rights: "
+ oauthIdentityProvider); + oauthIdentityProvider);
} // }
} catch (UnauthorizedException e) { // } catch (UnauthorizedException e) {
LOG.error("Cannot retrieve User OpenShift token from the identity provider", e); // LOG.error("Cannot retrieve User OpenShift token from the identity provider", e);
//
throw new InfrastructureException(messageToLinkAccount); // throw new InfrastructureException(messageToLinkAccount);
} catch (BadRequestException e) { // } catch (BadRequestException e) {
LOG.error( // LOG.error(
"Cannot retrieve User OpenShift token from the '" // "Cannot retrieve User OpenShift token from the '"
+ oauthIdentityProvider // + oauthIdentityProvider
+ "' identity provider", // + "' identity provider",
e); // e);
if (e.getMessage().endsWith("Invalid token.")) { // if (e.getMessage().endsWith("Invalid token.")) {
throw new InfrastructureException( // throw new InfrastructureException(
"Your session has expired. \nPlease " // "Your session has expired. \nPlease "
+ "<a href='javascript:location.reload();' target='_top'>" // + "<a href='javascript:location.reload();' target='_top'>"
+ "login" // + "login"
+ "</a> to Che again to get access to your OpenShift account"); // + "</a> to Che again to get access to your OpenShift account");
} // }
throw new InfrastructureException(e.getMessage(), e); // throw new InfrastructureException(e.getMessage(), e);
} catch (Exception e) { // } catch (Exception e) {
LOG.error( // LOG.error(
"Cannot retrieve User OpenShift token from the '" // "Cannot retrieve User OpenShift token from the '"
+ oauthIdentityProvider // + oauthIdentityProvider
+ "' identity provider", // + "' identity provider",
e); // e);
throw new InfrastructureException(e.getMessage(), e); // throw new InfrastructureException(e.getMessage(), e);
} // }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -25,11 +25,10 @@ import org.eclipse.che.api.core.model.user.User;
import org.eclipse.che.api.user.server.UserManager; import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.commons.subject.Subject; import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore; import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.AuthorizedSubject;
import org.eclipse.che.multiuser.api.authentication.commons.filter.MultiUserEnvironmentInitializationFilter; import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.MultiUserEnvironmentInitializationFilter;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor; import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.RequestTokenExtractor;
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject; import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.SessionStore;
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
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;
@ -45,7 +44,7 @@ public class OpenshiftTokenInitializationFilter
private static final Logger LOG = private static final Logger LOG =
LoggerFactory.getLogger(OpenshiftTokenInitializationFilter.class); LoggerFactory.getLogger(OpenshiftTokenInitializationFilter.class);
private final PermissionChecker permissionChecker; // private final PermissionChecker permissionChecker;
private final OpenShiftClientFactory clientFactory; private final OpenShiftClientFactory clientFactory;
private final UserManager userManager; private final UserManager userManager;
@ -55,12 +54,11 @@ public class OpenshiftTokenInitializationFilter
SessionStore sessionStore, SessionStore sessionStore,
RequestTokenExtractor tokenExtractor, RequestTokenExtractor tokenExtractor,
OpenShiftClientFactory clientFactory, OpenShiftClientFactory clientFactory,
UserManager userManager, UserManager userManager) {
PermissionChecker permissionChecker) {
super(sessionStore, tokenExtractor); super(sessionStore, tokenExtractor);
this.clientFactory = clientFactory; this.clientFactory = clientFactory;
this.userManager = userManager; this.userManager = userManager;
this.permissionChecker = permissionChecker; // this.permissionChecker = permissionChecker;
} }
@Override @Override
@ -92,8 +90,7 @@ public class OpenshiftTokenInitializationFilter
try { try {
ObjectMeta userMeta = osu.getMetadata(); ObjectMeta userMeta = osu.getMetadata();
User user = userManager.getOrCreateUser(getUserId(osu), userMeta.getName()); User user = userManager.getOrCreateUser(getUserId(osu), userMeta.getName());
return new AuthorizedSubject( return new AuthorizedSubject(new SubjectImpl(user.getName(), user.getId(), token, false));
new SubjectImpl(user.getName(), user.getId(), token, false), permissionChecker);
} catch (ServerException | ConflictException e) { } catch (ServerException | ConflictException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -1,253 +0,0 @@
/*
* Copyright (c) 2012-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth;
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.oidc.OIDCInfoProvider.AUTH_SERVER_URL_SETTING;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.fail;
import com.google.inject.Provider;
import io.fabric8.kubernetes.client.Config;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.UnauthorizedException;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.workspace.server.WorkspaceRuntimes;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.RuntimeContext;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.keycloak.server.KeycloakServiceClient;
import org.eclipse.che.multiuser.keycloak.server.KeycloakSettings;
import org.eclipse.che.multiuser.keycloak.shared.dto.KeycloakTokenResponse;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/** @author David Festal */
@Listeners(MockitoTestNGListener.class)
public class KeycloakProviderConfigFactoryTest {
private static final String PROVIDER = "openshift-v3";
private static final String THE_USER_ID = "a_user_id";
private static final String ANOTHER_USER_ID = "another_user_id";
private static final String A_WORKSPACE_ID = "workspace_id";
private static final String FULL_SCOPE = "user:full";
private static final String ACCESS_TOKEN = "accessToken";
private static final String AUTH_SERVER_URL = "http://keycloak.url/auth";
private static final String REALM = "realm";
private static final String CLIENT_ID = "clientId";
private static final String API_ENDPOINT = "http://che-host/api";
private static final String SHOULD_LINK_ERROR_MESSAGE =
"You should link your account with the <strong>"
+ PROVIDER
+ "</strong> \n"
+ "identity provider by visiting the "
+ "<a href='"
+ AUTH_SERVER_URL
+ "/realms/"
+ REALM
+ "/account/identity?referrer="
+ CLIENT_ID
+ "&referrer_uri="
+ "http%3A%2F%2Fche-host%2Fdashboard%2F%3Fredirect_fragment%3D%2Fworkspaces'"
+ " target='_blank' rel='noopener noreferrer'><strong>Federated Identities</strong></a> page of your Che account";
private static final String SESSION_EXPIRED_MESSAGE =
"Your session has expired. \nPlease "
+ "<a href='javascript:location.reload();' target='_top'>"
+ "login"
+ "</a> to Che again to get access to your OpenShift account";
private static final Map<String, String> keycloakSettingsMap = new HashMap<String, String>();
@Mock private KeycloakServiceClient keycloakServiceClient;
@Mock private KeycloakSettings keycloakSettings;
@Mock private Provider<WorkspaceRuntimes> workspaceRuntimeProvider;
@Mock private WorkspaceRuntimes workspaceRuntimes;
@Mock private Subject subject;
@Mock private RuntimeIdentity runtimeIdentity;
@SuppressWarnings("rawtypes")
@Mock
private RuntimeContext runtimeContext;
@Mock private KeycloakTokenResponse tokenResponse;
private EnvironmentContext context;
private KeycloakProviderConfigFactory configBuilder;
private Config defaultConfig;
static {
keycloakSettingsMap.put(AUTH_SERVER_URL_SETTING, AUTH_SERVER_URL);
keycloakSettingsMap.put(REALM_SETTING, REALM);
keycloakSettingsMap.put(CLIENT_ID_SETTING, CLIENT_ID);
}
@BeforeMethod
public void setUp() throws Exception {
when(keycloakSettings.get()).thenReturn(keycloakSettingsMap);
context = spy(EnvironmentContext.getCurrent());
EnvironmentContext.setCurrent(context);
lenient().doReturn(subject).when(context).getSubject();
lenient().when(workspaceRuntimeProvider.get()).thenReturn(workspaceRuntimes);
lenient()
.when(workspaceRuntimes.getRuntimeContext(anyString()))
.thenReturn(Optional.<RuntimeContext>ofNullable(runtimeContext));
lenient().when(runtimeContext.getIdentity()).thenReturn(runtimeIdentity);
lenient().when(runtimeIdentity.getOwnerId()).thenReturn(THE_USER_ID);
lenient().when(subject.getUserId()).thenReturn(THE_USER_ID);
lenient().when(tokenResponse.getScope()).thenReturn(FULL_SCOPE);
lenient().when(tokenResponse.getAccessToken()).thenReturn(ACCESS_TOKEN);
configBuilder =
new KeycloakProviderConfigFactory(
keycloakServiceClient,
keycloakSettings,
workspaceRuntimeProvider,
PROVIDER,
API_ENDPOINT,
null,
null);
defaultConfig = new io.fabric8.kubernetes.client.ConfigBuilder().build();
}
@AfterMethod
public void cleanup() {
EnvironmentContext.reset();
}
@Test
public void testFallbackToDefaultConfigWhenProvideIsNull() throws Exception {
configBuilder =
new KeycloakProviderConfigFactory(
keycloakServiceClient,
keycloakSettings,
workspaceRuntimeProvider,
null,
API_ENDPOINT,
null,
null);
assertSame(defaultConfig, configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID));
}
@Test
public void testFallbackToDefaultConfigWhenSubjectIsAnonymous() throws Exception {
doReturn(Subject.ANONYMOUS).when(context).getSubject();
assertSame(defaultConfig, configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID));
}
@Test
public void testFallbackToDefaultConfigWhenCurrentUserIsDifferentFromWorkspaceOwner()
throws Exception {
when(runtimeIdentity.getOwnerId()).thenReturn(ANOTHER_USER_ID);
assertSame(defaultConfig, configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID));
}
@SuppressWarnings("rawtypes")
@Test
public void testCreateUserConfigWhenNoRuntimeContext() throws Exception {
when(keycloakServiceClient.getIdentityProviderToken(anyString())).thenReturn(tokenResponse);
when(workspaceRuntimes.getRuntimeContext(anyString())).thenReturn(Optional.empty());
Config resultConfig = configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID);
assertEquals(resultConfig.getOauthToken(), ACCESS_TOKEN);
}
@Test
public void testCreateUserConfigWhenWorkspaceIdIsNull() throws Exception {
when(keycloakServiceClient.getIdentityProviderToken(anyString())).thenReturn(tokenResponse);
Config resultConfig = configBuilder.buildConfig(defaultConfig, null);
assertEquals(resultConfig.getOauthToken(), ACCESS_TOKEN);
}
@Test
public void testCreateUserConfig() throws Exception {
when(keycloakServiceClient.getIdentityProviderToken(anyString())).thenReturn(tokenResponse);
Config resultConfig = configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID);
assertEquals(resultConfig.getOauthToken(), ACCESS_TOKEN);
}
@Test(expectedExceptions = {InfrastructureException.class})
public void testThrowOnBadScope() throws Exception {
when(keycloakServiceClient.getIdentityProviderToken(anyString())).thenReturn(tokenResponse);
when(tokenResponse.getScope()).thenReturn("bad:scope");
Config resultConfig = configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID);
assertEquals(resultConfig.getOauthToken(), ACCESS_TOKEN);
}
@Test
public void testRethrowOnUnauthorizedException() throws Exception {
doThrow(
new UnauthorizedException(
DtoFactory.newDto(ServiceError.class).withMessage("Any other message")))
.when(keycloakServiceClient)
.getIdentityProviderToken(anyString());
try {
configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID);
} catch (InfrastructureException e) {
assertEquals(e.getMessage(), SHOULD_LINK_ERROR_MESSAGE, "The exception message is wrong");
return;
}
fail(
"Should have thrown an exception with the following message: " + SHOULD_LINK_ERROR_MESSAGE);
}
@Test(expectedExceptions = {InfrastructureException.class})
public void testRethrowOnBadRequestException() throws Exception {
doThrow(
new BadRequestException(
DtoFactory.newDto(ServiceError.class).withMessage("Any other message")))
.when(keycloakServiceClient)
.getIdentityProviderToken(anyString());
configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID);
}
@Test
public void testRethrowOnInvalidTokenBadRequestException() throws Exception {
doThrow(
new BadRequestException(
DtoFactory.newDto(ServiceError.class).withMessage("Invalid token.")))
.when(keycloakServiceClient)
.getIdentityProviderToken(anyString());
try {
configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID);
} catch (InfrastructureException e) {
assertEquals(e.getMessage(), SESSION_EXPIRED_MESSAGE, "The exception message is wrong");
return;
}
fail("Should have thrown an exception with the following message: " + SESSION_EXPIRED_MESSAGE);
}
@Test(expectedExceptions = {InfrastructureException.class})
public void testRethrowOnAnyException() throws Exception {
when(keycloakServiceClient.getIdentityProviderToken(anyString()))
.thenThrow(org.eclipse.che.api.core.NotFoundException.class);
configBuilder.buildConfig(defaultConfig, A_WORKSPACE_ID);
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2021 Red Hat, Inc. * Copyright (c) 2012-2024 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/
@ -26,9 +26,8 @@ import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.api.user.server.model.impl.UserImpl; 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.commons.subject.Subject; import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore; import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.RequestTokenExtractor;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor; import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.SessionStore;
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory; import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener; import org.mockito.testng.MockitoTestNGListener;
@ -41,7 +40,7 @@ public class OpenshiftTokenInitializationFilterTest {
@Mock private SessionStore sessionStore; @Mock private SessionStore sessionStore;
@Mock private RequestTokenExtractor tokenExtractor; @Mock private RequestTokenExtractor tokenExtractor;
@Mock private UserManager userManager; @Mock private UserManager userManager;
@Mock private PermissionChecker permissionChecker; // @Mock private PermissionChecker permissionChecker;
@Mock private OpenShiftClientFactory openShiftClientFactory; @Mock private OpenShiftClientFactory openShiftClientFactory;
@Mock private OpenShiftClient openShiftClient; @Mock private OpenShiftClient openShiftClient;
@ -58,7 +57,7 @@ public class OpenshiftTokenInitializationFilterTest {
public void setUp() throws InfrastructureException { public void setUp() throws InfrastructureException {
openshiftTokenInitializationFilter = openshiftTokenInitializationFilter =
new OpenshiftTokenInitializationFilter( new OpenshiftTokenInitializationFilter(
sessionStore, tokenExtractor, openShiftClientFactory, userManager, permissionChecker); sessionStore, tokenExtractor, openShiftClientFactory, userManager);
} }
@Test @Test

View File

@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2024 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-api</artifactId>
<groupId>org.eclipse.che.multiuser</groupId>
<version>7.86.0-SNAPSHOT</version>
</parent>
<artifactId>che-multiuser-api-authentication-commons</artifactId>
<packaging>jar</packaging>
<name>Che Multiuser :: API :: Authentication Commons</name>
<dependencies>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</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>

View File

@ -1,19 +0,0 @@
/*
* 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.api.authentication.commons;
/** Auth-related constants. */
public class Constants {
/** Name of the subject attribute in the Http session */
public static final String CHE_SUBJECT_ATTRIBUTE = "che_subject";
}

View File

@ -1,66 +0,0 @@
/*
* 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.api.authentication.commons;
import static org.eclipse.che.multiuser.api.authentication.commons.Constants.CHE_SUBJECT_ATTRIBUTE;
import com.google.inject.Injector;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
import java.util.Optional;
import org.eclipse.che.commons.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Purges deleted sessions from sessions cache store. */
public class DestroySessionListener implements HttpSessionListener {
private static final Logger LOG = LoggerFactory.getLogger(DestroySessionListener.class);
@Override
public final void sessionCreated(HttpSessionEvent sessionEvent) {}
@Override
public void sessionDestroyed(HttpSessionEvent sessionEvent) {
ServletContext servletContext = sessionEvent.getSession().getServletContext();
Optional<SessionStore> sessionStoreOptional = getSessionStoreInstance(servletContext);
if (!sessionStoreOptional.isPresent()) {
LOG.error(
"Unable to remove session from store. Session store is not configured in servlet context.");
return;
}
SessionStore sessionStore = sessionStoreOptional.get();
Subject subject = (Subject) sessionEvent.getSession().getAttribute(CHE_SUBJECT_ATTRIBUTE);
if (subject != null) {
sessionStore.remove(subject.getUserId());
}
}
/** Searches session store component in servlet context when with help of guice injector. */
private Optional<SessionStore> getSessionStoreInstance(ServletContext servletContext) {
String attributeName = SessionStore.class.getName();
SessionStore result = (SessionStore) servletContext.getAttribute(attributeName);
if (result == null) {
Injector injector = (Injector) servletContext.getAttribute(Injector.class.getName());
if (injector != null) {
result = injector.getInstance(SessionStore.class);
if (result != null) {
servletContext.setAttribute(attributeName, result);
}
}
}
return Optional.ofNullable(result);
}
}

View File

@ -1,41 +0,0 @@
/*
* 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.api.authentication.commons.token;
import jakarta.servlet.http.HttpServletRequest;
/**
* Try to extract token from request in 3 steps. 1. From query parameter. 2. From header. 3. From
* cookie.
*
* @author Sergii Kabashniuk
*/
public class ChainedTokenExtractor implements RequestTokenExtractor {
private final HeaderRequestTokenExtractor headerRequestTokenExtractor;
private final QueryRequestTokenExtractor queryRequestTokenExtractor;
public ChainedTokenExtractor() {
headerRequestTokenExtractor = new HeaderRequestTokenExtractor();
queryRequestTokenExtractor = new QueryRequestTokenExtractor();
}
@Override
public String getToken(HttpServletRequest req) {
String token;
if ((token = queryRequestTokenExtractor.getToken(req)) == null) {
token = headerRequestTokenExtractor.getToken(req);
}
return token;
}
}

View File

@ -1,35 +0,0 @@
/*
* 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.api.authentication.commons.token;
import jakarta.servlet.http.HttpServletRequest;
/** @author Max Shaposhnik (mshaposh@redhat.com) */
public class QueryRequestTokenExtractor implements RequestTokenExtractor {
@Override
public String getToken(HttpServletRequest req) {
String query = req.getQueryString();
if (query != null) {
int start = query.indexOf("&token=");
if (start != -1 || query.startsWith("token=")) {
int end = query.indexOf('&', start + 7);
if (end == -1) {
end = query.length();
}
if (end != start + 7) {
return query.substring(start + 7, end);
}
}
}
return null;
}
}

View File

@ -1,71 +0,0 @@
/*
* 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.api.authentication.commons.token;
import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.BadRequestException;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class HeaderRequestTokenExtractorTest {
private HeaderRequestTokenExtractor tokenExtractor = new HeaderRequestTokenExtractor();
@Mock HttpServletRequest servletRequest;
@Test(dataProvider = "validHeadersProvider")
public void shouldExtractTokensFromValidHeaders(String headerValue, String expectedToken) {
when(servletRequest.getHeader(eq(AUTHORIZATION))).thenReturn(headerValue);
// when
String token = tokenExtractor.getToken(servletRequest);
// then
assertEquals(token, expectedToken);
}
@Test(
dataProvider = "invalidHeadersProvider",
expectedExceptions = BadRequestException.class,
expectedExceptionsMessageRegExp = "Invalid authorization header format.")
public void shouldThrowExceptionOnInvalidToken(String headerValue) {
when(servletRequest.getHeader(eq(AUTHORIZATION))).thenReturn(headerValue);
// when
tokenExtractor.getToken(servletRequest);
}
@DataProvider
private Object[][] validHeadersProvider() {
return new Object[][] {
{"token123", "token123"},
{"bearer token123", "token123"},
{"Bearer token123", "token123"},
};
}
@DataProvider
private Object[][] invalidHeadersProvider() {
return new Object[][] {{"bearertoken123"}, {"bearer token123"}, {"bearer token 123"}};
}
}

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2024 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-api</artifactId>
<groupId>org.eclipse.che.multiuser</groupId>
<version>7.86.0-SNAPSHOT</version>
</parent>
<artifactId>che-multiuser-api-authorization-impl</artifactId>
<packaging>jar</packaging>
<name>Che Multiuser :: API :: Authorization Impl</name>
<dependencies>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-authorization</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission-shared</artifactId>
<scope>test</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>

View File

@ -1,38 +0,0 @@
/*
* Copyright (c) 2012-2018 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.api.permission.server;
import javax.inject.Inject;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
/**
* Implementation of {@link PermissionChecker} that use {@link PermissionsManager} for checking.
*
* @author Sergii Leschenko
*/
public class PermissionCheckerImpl implements PermissionChecker {
private final PermissionsManager permissionsManager;
@Inject
public PermissionCheckerImpl(PermissionsManager permissionsManager) {
this.permissionsManager = permissionsManager;
}
@Override
public boolean hasPermission(String user, String domain, String instance, String action)
throws ServerException, NotFoundException, ConflictException {
return permissionsManager.exists(user, domain, instance, action)
|| permissionsManager.exists("*", domain, instance, action);
}
}

View File

@ -1,98 +0,0 @@
/*
* 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.api.permission.server;
import static java.util.Arrays.asList;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEFAULTS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import jakarta.ws.rs.core.UriBuilder;
import org.eclipse.che.api.core.rest.HttpJsonRequest;
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
import org.eclipse.che.api.core.rest.HttpJsonResponse;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.api.permission.shared.dto.PermissionsDto;
import org.mockito.Mock;
import org.mockito.stubbing.Answer;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Tests for {@link HttpPermissionCheckerImpl}.
*
* @author Sergii Leschenko
*/
@Listeners(MockitoTestNGListener.class)
public class HttpPermissionCheckerImplTest {
private static final String API_ENDPOINT = "http://localhost:8000/api";
@Mock private HttpJsonRequestFactory requestFactory;
@Mock private HttpJsonResponse response;
private HttpJsonRequest request;
private HttpPermissionCheckerImpl httpPermissionChecker;
@BeforeMethod
public void setUp() throws Exception {
request =
mock(
HttpJsonRequest.class,
(Answer)
invocation -> {
if (invocation.getMethod().getReturnType().isInstance(invocation.getMock())) {
return invocation.getMock();
}
return RETURNS_DEFAULTS.answer(invocation);
});
when(request.request()).thenReturn(response);
when(requestFactory.fromUrl(anyString())).thenReturn(request);
httpPermissionChecker = new HttpPermissionCheckerImpl(API_ENDPOINT, requestFactory);
}
@Test
public void shouldCheckPermissionsByHttpRequestToPermissionsService() throws Exception {
when(response.asDto(anyObject()))
.thenReturn(
DtoFactory.newDto(PermissionsDto.class)
.withUserId("user123")
.withDomainId("domain123")
.withInstanceId("instance123")
.withActions(asList("read", "test")));
final boolean hasPermission =
httpPermissionChecker.hasPermission("user123", "domain123", "instance123", "test");
assertEquals(hasPermission, true);
verify(requestFactory)
.fromUrl(
eq(
UriBuilder.fromUri(API_ENDPOINT)
.path(PermissionsService.class)
.path(PermissionsService.class, "getCurrentUsersPermissions")
.queryParam("instance", "instance123")
.build("domain123")
.toString()));
verify(request).useGetMethod();
verify(request).request();
verifyNoMoreInteractions(request);
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright (c) 2012-2018 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.api.permission.server;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class PermissionCheckerImplTest {
@Mock private PermissionsManager permissionsManager;
@InjectMocks private PermissionCheckerImpl permissionChecker;
@Test
public void shouldCheckExistingDirectUsersPermissions() throws Exception {
when(permissionsManager.exists(anyString(), anyString(), anyString(), anyString()))
.thenReturn(true);
boolean hasPermission =
permissionChecker.hasPermission("user123", "domain123", "instance123", "test");
assertEquals(hasPermission, true);
verify(permissionsManager).exists("user123", "domain123", "instance123", "test");
}
@Test
public void shouldCheckExistingPublicPermissionsIfThereIsNoDirectUsersPermissions()
throws Exception {
doReturn(false)
.when(permissionsManager)
.exists(eq("user123"), anyString(), anyString(), anyString());
doReturn(true).when(permissionsManager).exists(eq("*"), anyString(), anyString(), anyString());
boolean hasPermission =
permissionChecker.hasPermission("user123", "domain123", "instance123", "test");
assertEquals(hasPermission, true);
verify(permissionsManager).exists("user123", "domain123", "instance123", "test");
verify(permissionsManager).exists("*", "domain123", "instance123", "test");
}
}

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2018 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
-->
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<File>target/log/authorization-impl.log</File>
<encoder>
<pattern>%-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
</root>
</configuration>

View File

@ -1,175 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2024 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-api</artifactId>
<groupId>org.eclipse.che.multiuser</groupId>
<version>7.86.0-SNAPSHOT</version>
</parent>
<artifactId>che-multiuser-api-authorization</artifactId>
<packaging>jar</packaging>
<name>Che Multiuser :: API :: Authorization</name>
<properties>
<dto-generator-out-directory>${project.build.directory}/generated-sources/dto/</dto-generator-out-directory>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-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-dto</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission-shared</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission</artifactId>
<scope>test</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>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>${dto-generator-out-directory}</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission-shared</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<dtoPackages>
<package>org.eclipse.che.api.permission.shared.dto</package>
</dtoPackages>
<outputDirectory>${dto-generator-out-directory}</outputDirectory>
<genClassName>org.eclipse.che.multiuser.api.permission.server.dto.DtoServerImpls</genClassName>
<impl>server</impl>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>pre-compile</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-domain</id>
<phase>process-sources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${dto-generator-out-directory}/META-INF</directory>
<targetPath>META-INF</targetPath>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>add-source</id>
<phase>process-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${dto-generator-out-directory}</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,120 +0,0 @@
/*
* 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.api.permission.server;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import jakarta.ws.rs.core.UriBuilder;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
import org.eclipse.che.multiuser.api.permission.shared.dto.PermissionsDto;
/**
* Implementation of {@link PermissionChecker} that load permissions by http requests to {@link
* PermissionsService}
*
* <p>It also caches permissions to avoid frequently requests to workspace master.
*
* @author Sergii Leschenko
*/
@Singleton
public class HttpPermissionCheckerImpl implements PermissionChecker {
private final LoadingCache<Key, Set<String>> permissionsCache;
@Inject
public HttpPermissionCheckerImpl(
@Named("che.api") String apiEndpoint, HttpJsonRequestFactory requestFactory) {
// TODO mb make configurable size of cache and expiration time
this.permissionsCache =
CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Set<String>>() {
@Override
public Set<String> load(Key key) throws Exception {
UriBuilder currentUsersPermissions =
UriBuilder.fromUri(apiEndpoint).path("permissions/" + key.domain);
if (key.instance != null) {
currentUsersPermissions.queryParam("instance", key.instance);
}
String userPermissionsUrl = currentUsersPermissions.build().toString();
try {
PermissionsDto usersPermissions =
requestFactory
.fromUrl(userPermissionsUrl)
.useGetMethod()
.request()
.asDto(PermissionsDto.class);
return new HashSet<>(usersPermissions.getActions());
} catch (NotFoundException e) {
// user doesn't have permissions
return new HashSet<>();
}
}
});
}
@Override
public boolean hasPermission(String user, String domain, String instance, String action)
throws ServerException {
try {
return permissionsCache.get(new Key(user, domain, instance)).contains(action);
} catch (Exception e) {
throw new ServerException(e.getMessage(), e);
}
}
private static final class Key {
private final String user;
private final String domain;
private final String instance;
private Key(String user, String domain, String instance) {
this.user = user;
this.domain = domain;
this.instance = instance;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Key)) {
return false;
}
final Key other = (Key) obj;
return Objects.equals(user, other.user)
&& Objects.equals(domain, other.domain)
&& Objects.equals(instance, other.instance);
}
@Override
public int hashCode() {
int hash = 7;
hash = hash * 31 + Objects.hashCode(user);
hash = hash * 31 + Objects.hashCode(domain);
hash = hash * 31 + Objects.hashCode(instance);
return hash;
}
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) 2012-2018 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.api.permission.server;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
/**
* Checks user's permission to perform some action with particular instance of given domain.
*
* @author Sergii Leschenko
*/
public interface PermissionChecker {
/**
* Checks user's permission to perform some action with particular instance.
*
* @param user user id
* @param domain domain id
* @param instance instance id
* @param action action name
* @return true if the user has given permission
* @throws NotFoundException when given domain is unsupported
* @throws ConflictException when given domain requires non nullable value for instance but it is
* null
* @throws ServerException when any other error occurs during permission existence checking
*/
boolean hasPermission(String user, String domain, String instance, String action)
throws ServerException, NotFoundException, ConflictException;
}

View File

@ -1,98 +0,0 @@
/*
* 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.api.permission.server;
import static java.util.Arrays.asList;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEFAULTS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import jakarta.ws.rs.core.UriBuilder;
import org.eclipse.che.api.core.rest.HttpJsonRequest;
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
import org.eclipse.che.api.core.rest.HttpJsonResponse;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.api.permission.shared.dto.PermissionsDto;
import org.mockito.Mock;
import org.mockito.stubbing.Answer;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Tests for {@link HttpPermissionCheckerImpl}.
*
* @author Sergii Leschenko
*/
@Listeners(MockitoTestNGListener.class)
public class HttpPermissionCheckerImplTest {
private static final String API_ENDPOINT = "http://localhost:8000/api";
@Mock private HttpJsonRequestFactory requestFactory;
@Mock private HttpJsonResponse response;
private HttpJsonRequest request;
private HttpPermissionCheckerImpl httpPermissionChecker;
@BeforeMethod
public void setUp() throws Exception {
request =
mock(
HttpJsonRequest.class,
(Answer)
invocation -> {
if (invocation.getMethod().getReturnType().isInstance(invocation.getMock())) {
return invocation.getMock();
}
return RETURNS_DEFAULTS.answer(invocation);
});
when(request.request()).thenReturn(response);
when(requestFactory.fromUrl(anyString())).thenReturn(request);
httpPermissionChecker = new HttpPermissionCheckerImpl(API_ENDPOINT, requestFactory);
}
@Test
public void shouldCheckPermissionsByHttpRequestToPermissionsService() throws Exception {
when(response.asDto(anyObject()))
.thenReturn(
DtoFactory.newDto(PermissionsDto.class)
.withUserId("user123")
.withDomainId("domain123")
.withInstanceId("instance123")
.withActions(asList("read", "test")));
final boolean hasPermission =
httpPermissionChecker.hasPermission("user123", "domain123", "instance123", "test");
assertEquals(hasPermission, true);
verify(requestFactory)
.fromUrl(
eq(
UriBuilder.fromUri(API_ENDPOINT)
.path(PermissionsService.class)
.path(PermissionsService.class, "getCurrentUsersPermissions")
.queryParam("instance", "instance123")
.build("domain123")
.toString()));
verify(request).useGetMethod();
verify(request).request();
verifyNoMoreInteractions(request);
}
}

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2018 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
-->
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<File>target/log/api-authorization.log</File>
<encoder>
<pattern>%-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
</root>
</configuration>

View File

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2024 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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-multiuser-api</artifactId>
<groupId>org.eclipse.che.multiuser</groupId>
<version>7.86.0-SNAPSHOT</version>
</parent>
<artifactId>che-multiuser-api-organization-shared</artifactId>
<packaging>jar</packaging>
<name>Che Multiuser :: Organization :: Shared</name>
<dependencies>
<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-dto</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-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-resource-shared</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,24 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared;
/**
* Constants for Organization API
*
* @author Sergii Leschenko
*/
public final class Constants {
public static final String LINK_REL_SELF = "self";
public static final String LINK_REL_SUBORGANIZATIONS = "organization.suborganizations";
private Constants() {}
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.dto;
import org.eclipse.che.api.core.notification.EventOrigin;
import org.eclipse.che.api.user.shared.dto.UserDto;
import org.eclipse.che.dto.shared.DTO;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
/**
* DTO for member added event.
*
* @author Anton Korneta
*/
@DTO
@EventOrigin("organization")
public interface MemberAddedEventDto extends OrganizationEventDto {
@Override
MemberAddedEventDto withOrganization(OrganizationDto organization);
@Override
MemberAddedEventDto withType(EventType eventType);
UserDto getMember();
void setMember(UserDto member);
MemberAddedEventDto withMember(UserDto member);
/** Returns name of user who initiated member invitation */
String getInitiator();
void setInitiator(String initiator);
MemberAddedEventDto withInitiator(String initiator);
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.dto;
import org.eclipse.che.api.core.notification.EventOrigin;
import org.eclipse.che.api.user.shared.dto.UserDto;
import org.eclipse.che.dto.shared.DTO;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
/**
* DTO for organization member removed event.
*
* @author Anton Korneta
*/
@DTO
@EventOrigin("organization")
public interface MemberRemovedEventDto extends OrganizationEventDto {
@Override
MemberRemovedEventDto withOrganization(OrganizationDto organization);
@Override
MemberRemovedEventDto withType(EventType eventType);
UserDto getMember();
void setMember(UserDto member);
MemberRemovedEventDto withMember(UserDto member);
/** Returns name of user who initiated member removal */
String getInitiator();
void setInitiator(String initiator);
MemberRemovedEventDto withInitiator(String initiator);
}

View File

@ -1,35 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.dto;
import java.util.List;
import org.eclipse.che.dto.shared.DTO;
import org.eclipse.che.multiuser.organization.shared.model.OrganizationDistributedResources;
import org.eclipse.che.multiuser.resource.shared.dto.ResourceDto;
/** @author Sergii Leschenko */
@DTO
public interface OrganizationDistributedResourcesDto extends OrganizationDistributedResources {
@Override
String getOrganizationId();
void setOrganizationId(String organizationId);
OrganizationDistributedResourcesDto withOrganizationId(String organizationId);
@Override
List<ResourceDto> getResourcesCap();
void setResourcesCap(List<ResourceDto> resourcesCap);
OrganizationDistributedResourcesDto withResourcesCap(List<ResourceDto> resourcesCap);
}

View File

@ -1,53 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.dto;
import java.util.List;
import org.eclipse.che.api.core.rest.shared.dto.Hyperlinks;
import org.eclipse.che.api.core.rest.shared.dto.Link;
import org.eclipse.che.dto.shared.DTO;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/** @author Sergii Leschenko */
@DTO
public interface OrganizationDto extends Organization, Hyperlinks {
@Override
String getId();
void setId(String id);
OrganizationDto withId(String id);
@Override
String getName();
void setName(String name);
OrganizationDto withName(String name);
@Override
String getQualifiedName();
void setQualifiedName(String qualifiedName);
OrganizationDto withQualifiedName(String qualifiedName);
@Override
String getParent();
void setParent(String parent);
OrganizationDto withParent(String parent);
@Override
OrganizationDto withLinks(List<Link> links);
}

View File

@ -1,38 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.dto;
import org.eclipse.che.api.core.notification.EventOrigin;
import org.eclipse.che.dto.shared.DTO;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
import org.eclipse.che.multiuser.organization.shared.event.OrganizationEvent;
/**
* DTO for {@link OrganizationEvent}.
*
* @author Anton Korneta
*/
@DTO
@EventOrigin("organization")
public interface OrganizationEventDto extends OrganizationEvent {
@Override
OrganizationDto getOrganization();
void setOrganization(OrganizationDto organization);
OrganizationEventDto withOrganization(OrganizationDto organization);
void setType(EventType eventType);
OrganizationEventDto withType(EventType eventType);
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.dto;
import java.util.List;
import org.eclipse.che.api.core.notification.EventOrigin;
import org.eclipse.che.dto.shared.DTO;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
/**
* DTO for organization removed event.
*
* @author Anton Korneta
*/
@DTO
@EventOrigin("organization")
public interface OrganizationRemovedEventDto extends OrganizationEventDto {
@Override
OrganizationRemovedEventDto withOrganization(OrganizationDto organization);
@Override
OrganizationRemovedEventDto withType(EventType eventType);
/** Returns name of user who initiated organization removal */
String getInitiator();
void setInitiator(String initiator);
OrganizationRemovedEventDto withInitiator(String initiator);
List<String> getMembers();
void setMembers(List<String> members);
OrganizationRemovedEventDto withMembers(List<String> members);
}

View File

@ -1,53 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.dto;
import org.eclipse.che.api.core.notification.EventOrigin;
import org.eclipse.che.dto.shared.DTO;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
/**
* DTO for organization renamed event.
*
* @author Anton Korneta
*/
@DTO
@EventOrigin("organization")
public interface OrganizationRenamedEventDto extends OrganizationEventDto {
@Override
OrganizationRenamedEventDto withOrganization(OrganizationDto organization);
@Override
OrganizationRenamedEventDto withType(EventType eventType);
/** Returns organization name before renaming */
String getOldName();
void setOldName(String oldName);
OrganizationRenamedEventDto withOldName(String oldName);
/** Returns organization name after renaming */
String getNewName();
void setNewName(String newName);
OrganizationRenamedEventDto withNewName(String newName);
/** Returns name of user who initiated organization rename */
String getInitiator();
void setInitiator(String initiator);
OrganizationRenamedEventDto withInitiator(String initiator);
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.event;
/**
* Defines organizations event types.
*
* @author Anton Korneta
*/
public enum EventType {
/** Published when organization name changed. */
ORGANIZATION_RENAMED,
/** Published when organization removed. */
ORGANIZATION_REMOVED,
/** Published when new member added to organization. */
MEMBER_ADDED,
/** Published when member removed from organization. */
MEMBER_REMOVED
}

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.event;
import org.eclipse.che.api.core.model.user.User;
/**
* Defines organization member event.
*
* @author Anton Korneta
*/
public interface MemberEvent extends OrganizationEvent {
/** Returns the member associated with this event. */
User getMember();
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.event;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* The base interface for organization event.
*
* @author Anton Korneta
*/
public interface OrganizationEvent {
/** Returns organization related to this event. */
Organization getOrganization();
/** Returns type of this event. */
EventType getType();
/** Returns name of user who acted with organization or null if user is undefined. */
@Nullable
String getInitiator();
}

View File

@ -1,31 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.model;
import java.util.List;
/**
* Describes relations of user and organization
*
* @author gazarenkov
* @author Sergii Leschenko
*/
public interface Member {
/** Returns id of user */
String getUserId();
/** Returns id of organization */
String getOrganizationId();
/** Returns list of actions that user can perform in organization */
List<String> getActions();
}

View File

@ -1,49 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.model;
import org.eclipse.che.commons.annotation.Nullable;
/**
* Describes group of users that can use common resources
*
* @author gazarenkov
* @author Sergii Leschenko
*/
public interface Organization {
/**
* Returns the identifier of the organization (e.g. "organization0x1234567890"). The identifier
* value is unique and mandatory.
*/
String getId();
/**
* Returns name of organization. The name is mandatory and updatable. The name is unique per
* parent organization.
*/
String getName();
/**
* Returns the qualified name that includes all parent's names and the name of current
* organization separated by '/' symbol e.g. "parentOrgName/subOrgName/subSubOrgName". The
* qualified name is unique.
*/
String getQualifiedName();
/**
* Returns id of parent organization. The returned value can be nullable in case when organization
* is root
*/
@Nullable
String getParent();
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.shared.model;
import java.util.List;
import org.eclipse.che.multiuser.resource.model.Resource;
/**
* Defines resources which are distributed for suborganization by parent organization
*
* @author Sergii Leschenko
*/
public interface OrganizationDistributedResources {
/** Id of organization that owns these distributed resources */
String getOrganizationId();
/**
* Returns resources cap that limit usage of parent organization's resources.
*
* <p>Note that suborganization is not limited to use parent organization's resources if resource
* is not capped.
*/
List<? extends Resource> getResourcesCap();
}

View File

@ -1,313 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2024 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-api</artifactId>
<groupId>org.eclipse.che.multiuser</groupId>
<version>7.86.0-SNAPSHOT</version>
</parent>
<artifactId>che-multiuser-api-organization</artifactId>
<packaging>jar</packaging>
<name>Che Multiuser :: Organization</name>
<properties>
<dto-generator-out-directory>${project.build.directory}/generated-sources/dto/</dto-generator-out-directory>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-persist</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-account</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-dto</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-api-user-shared</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-lang</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-test</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-organization-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-permission-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-resource</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-resource-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>jakarta.persistence</artifactId>
</dependency>
<dependency>
<groupId>org.everrest</groupId>
<artifactId>everrest-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-sql-schema</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-sql-schema</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.everrest</groupId>
<artifactId>everrest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>test</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>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-organization-shared</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<dtoPackages>
<package>org.eclipse.che.multiuser.organization.shared.dto</package>
</dtoPackages>
<outputDirectory>${dto-generator-out-directory}</outputDirectory>
<genClassName>org.eclipse.che.multiuser.organization.api.dto.DtoServerImpls</genClassName>
<impl>server</impl>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>pre-compile</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-domain</id>
<phase>process-sources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${dto-generator-out-directory}/META-INF</directory>
<targetPath>META-INF</targetPath>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>add-source</id>
<phase>process-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${dto-generator-out-directory}</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Create the test jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
<configuration>
<includes>
<include>**/spi/tck/*.*</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>resource-dependencies</id>
<phase>process-test-resources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeArtifactIds>che-core-sql-schema,
che-multiuser-sql-schema</includeArtifactIds>
<includes>che-schema/</includes>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,106 +0,0 @@
/*
* 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.organization.api;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import java.util.stream.Collectors;
import org.eclipse.che.multiuser.organization.api.event.MemberAddedEvent;
import org.eclipse.che.multiuser.organization.api.event.MemberRemovedEvent;
import org.eclipse.che.multiuser.organization.api.event.OrganizationRemovedEvent;
import org.eclipse.che.multiuser.organization.api.event.OrganizationRenamedEvent;
import org.eclipse.che.multiuser.organization.shared.dto.MemberAddedEventDto;
import org.eclipse.che.multiuser.organization.shared.dto.MemberRemovedEventDto;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDistributedResourcesDto;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationEventDto;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationRemovedEventDto;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationRenamedEventDto;
import org.eclipse.che.multiuser.organization.shared.event.OrganizationEvent;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.organization.shared.model.OrganizationDistributedResources;
/**
* Helps to convert objects related to organization to DTOs.
*
* @author Sergii Leschenko
*/
public final class DtoConverter {
private DtoConverter() {}
public static OrganizationDto asDto(Organization organization) {
return newDto(OrganizationDto.class)
.withId(organization.getId())
.withName(organization.getName())
.withQualifiedName(organization.getQualifiedName())
.withParent(organization.getParent());
}
public static OrganizationDistributedResourcesDto asDto(
OrganizationDistributedResources distributedResources) {
return newDto(OrganizationDistributedResourcesDto.class)
.withOrganizationId(distributedResources.getOrganizationId())
.withResourcesCap(
distributedResources.getResourcesCap().stream()
.map(org.eclipse.che.multiuser.resource.api.DtoConverter::asDto)
.collect(Collectors.toList()));
}
public static OrganizationRemovedEventDto asDto(OrganizationRemovedEvent event) {
return newDto(OrganizationRemovedEventDto.class)
.withType(event.getType())
.withOrganization(asDto(event.getOrganization()))
.withMembers(event.getMembers())
.withInitiator(event.getInitiator());
}
public static OrganizationRenamedEventDto asDto(OrganizationRenamedEvent event) {
return newDto(OrganizationRenamedEventDto.class)
.withType(event.getType())
.withOrganization(asDto(event.getOrganization()))
.withOldName(event.getOldName())
.withNewName(event.getNewName())
.withInitiator(event.getInitiator());
}
public static MemberAddedEventDto asDto(MemberAddedEvent event) {
return newDto(MemberAddedEventDto.class)
.withType(event.getType())
.withOrganization(asDto(event.getOrganization()))
.withInitiator(event.getInitiator())
.withMember(org.eclipse.che.api.user.server.DtoConverter.asDto(event.getMember()));
}
public static MemberRemovedEventDto asDto(MemberRemovedEvent event) {
return newDto(MemberRemovedEventDto.class)
.withType(event.getType())
.withOrganization(asDto(event.getOrganization()))
.withInitiator((event.getInitiator()))
.withMember(org.eclipse.che.api.user.server.DtoConverter.asDto(event.getMember()));
}
public static OrganizationEventDto asDto(OrganizationEvent event) {
switch (event.getType()) {
case ORGANIZATION_RENAMED:
return asDto((OrganizationRenamedEvent) event);
case ORGANIZATION_REMOVED:
return asDto((OrganizationRemovedEvent) event);
case MEMBER_ADDED:
return asDto((MemberAddedEvent) event);
case MEMBER_REMOVED:
return asDto((MemberRemovedEvent) event);
default:
throw new IllegalArgumentException(
"Can't convert event to dto, event type '" + event.getType() + "' is unknown");
}
}
}

View File

@ -1,78 +0,0 @@
/*
* Copyright (c) 2012-2024 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.organization.api;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import org.eclipse.che.multiuser.api.permission.server.SuperPrivilegesChecker;
import org.eclipse.che.multiuser.api.permission.server.account.AccountPermissionsChecker;
import org.eclipse.che.multiuser.api.permission.shared.model.PermissionsDomain;
import org.eclipse.che.multiuser.organization.api.listener.MemberEventsPublisher;
import org.eclipse.che.multiuser.organization.api.listener.OrganizationEventsWebsocketBroadcaster;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationPermissionsFilter;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationRemoteSubscriptionPermissionsChecks;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationResourceDistributionServicePermissionsFilter;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationalAccountPermissionsChecker;
import org.eclipse.che.multiuser.organization.api.resource.DefaultOrganizationResourcesProvider;
import org.eclipse.che.multiuser.organization.api.resource.OrganizationResourceLockKeyProvider;
import org.eclipse.che.multiuser.organization.api.resource.OrganizationalAccountAvailableResourcesProvider;
import org.eclipse.che.multiuser.organization.api.resource.SuborganizationResourcesProvider;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.eclipse.che.multiuser.resource.api.AvailableResourcesProvider;
import org.eclipse.che.multiuser.resource.api.ResourceLockKeyProvider;
import org.eclipse.che.multiuser.resource.api.ResourcesProvider;
import org.eclipse.che.multiuser.resource.api.free.DefaultResourcesProvider;
/** @author Sergii Leschenko */
public class OrganizationApiModule extends AbstractModule {
@Override
protected void configure() {
bind(OrganizationPermissionsFilter.class);
bind(OrganizationRemoteSubscriptionPermissionsChecks.class);
Multibinder.newSetBinder(binder(), DefaultResourcesProvider.class)
.addBinding()
.to(DefaultOrganizationResourcesProvider.class);
Multibinder.newSetBinder(binder(), ResourcesProvider.class)
.addBinding()
.to(SuborganizationResourcesProvider.class);
MapBinder.newMapBinder(binder(), String.class, AvailableResourcesProvider.class)
.addBinding(OrganizationImpl.ORGANIZATIONAL_ACCOUNT)
.to(OrganizationalAccountAvailableResourcesProvider.class);
Multibinder.newSetBinder(binder(), ResourceLockKeyProvider.class)
.addBinding()
.to(OrganizationResourceLockKeyProvider.class);
Multibinder.newSetBinder(binder(), AccountPermissionsChecker.class)
.addBinding()
.to(OrganizationalAccountPermissionsChecker.class);
bind(OrganizationResourceDistributionServicePermissionsFilter.class);
bind(OrganizationEventsWebsocketBroadcaster.class).asEagerSingleton();
bind(MemberEventsPublisher.class).asEagerSingleton();
Multibinder.newSetBinder(
binder(),
PermissionsDomain.class,
Names.named(SuperPrivilegesChecker.SUPER_PRIVILEGED_DOMAINS))
.addBinding()
.to(OrganizationDomain.class);
}
}

View File

@ -1,27 +0,0 @@
/*
* Copyright (c) 2012-2024 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.organization.api;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain;
import org.eclipse.che.multiuser.organization.spi.impl.MemberImpl;
/** @author Sergii Leschenko */
public class OrganizationJpaModule extends AbstractModule {
@Override
protected void configure() {
bind(new TypeLiteral<AbstractPermissionsDomain<MemberImpl>>() {}).to(OrganizationDomain.class);
}
}

View File

@ -1,64 +0,0 @@
/*
* 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.organization.api;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.core.UriBuilder;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Singleton;
import org.eclipse.che.api.core.rest.ServiceContext;
import org.eclipse.che.api.core.rest.shared.dto.Link;
import org.eclipse.che.api.core.util.LinksHelper;
import org.eclipse.che.multiuser.organization.shared.Constants;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
/**
* Helps to inject {@link OrganizationService} related links.
*
* @author Sergii Leschenko
*/
@Singleton
public class OrganizationLinksInjector {
public OrganizationDto injectLinks(
OrganizationDto organizationDto, ServiceContext serviceContext) {
final UriBuilder uriBuilder = serviceContext.getBaseUriBuilder();
final List<Link> links = new ArrayList<>(2);
links.add(
LinksHelper.createLink(
HttpMethod.GET,
uriBuilder
.clone()
.path(OrganizationService.class)
.path(OrganizationService.class, "getById")
.build(organizationDto.getId())
.toString(),
null,
APPLICATION_JSON,
Constants.LINK_REL_SELF));
links.add(
LinksHelper.createLink(
HttpMethod.GET,
uriBuilder
.clone()
.path(OrganizationService.class)
.path(OrganizationService.class, "getByParent")
.build(organizationDto.getId())
.toString(),
null,
APPLICATION_JSON,
Constants.LINK_REL_SUBORGANIZATIONS));
return organizationDto.withLinks(links);
}
}

View File

@ -1,336 +0,0 @@
/*
* Copyright (c) 2012-2024 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.organization.api;
import static java.util.Objects.requireNonNull;
import static org.eclipse.che.multiuser.organization.api.DtoConverter.asDto;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import com.google.inject.persist.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.Pages;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.multiuser.organization.api.event.OrganizationRemovedEvent;
import org.eclipse.che.multiuser.organization.api.event.OrganizationRenamedEvent;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain;
import org.eclipse.che.multiuser.organization.shared.model.Member;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.organization.spi.MemberDao;
import org.eclipse.che.multiuser.organization.spi.OrganizationDao;
import org.eclipse.che.multiuser.organization.spi.impl.MemberImpl;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
/**
* Facade for Organization related operations.
*
* @author gazarenkov
* @author Sergii Leschenko
*/
@Singleton
public class OrganizationManager {
private final EventService eventService;
private final OrganizationDao organizationDao;
private final MemberDao memberDao;
private final Set<String> reservedNames;
@Inject
public OrganizationManager(
EventService eventService,
OrganizationDao organizationDao,
MemberDao memberDao,
@Named("che.auth.reserved_user_names") String[] reservedNames) {
this.eventService = eventService;
this.organizationDao = organizationDao;
this.memberDao = memberDao;
this.reservedNames = Sets.newHashSet(reservedNames);
}
/**
* Creates new organization.
*
* @param newOrganization organization to create
* @return created organization
* @throws NullPointerException when {@code organization} is null
* @throws NotFoundException when parent organization was not found
* @throws ConflictException when organization with such id/name already exists
* @throws ConflictException when specified organization name is reserved
* @throws ServerException when any other error occurs during organization creation
*/
@Transactional(rollbackOn = {RuntimeException.class, ApiException.class})
public Organization create(Organization newOrganization)
throws NotFoundException, ConflictException, ServerException {
requireNonNull(newOrganization, "Required non-null organization");
requireNonNull(newOrganization.getName(), "Required non-null organization name");
String qualifiedName;
if (newOrganization.getParent() != null) {
final Organization parent = getById(newOrganization.getParent());
qualifiedName = parent.getQualifiedName() + "/" + newOrganization.getName();
} else {
qualifiedName = newOrganization.getName();
}
checkNameReservation(qualifiedName);
final OrganizationImpl organization =
new OrganizationImpl(
NameGenerator.generate("organization", 16), qualifiedName, newOrganization.getParent());
organizationDao.create(organization);
addFirstMember(organization);
return organization;
}
/**
* Updates organization with new entity.
*
* @param organizationId id of organization to update
* @param update organization update
* @throws NullPointerException when {@code organizationId} or {@code update} is null
* @throws NotFoundException when organization with given id doesn't exist
* @throws ConflictException when name updated with a value which is reserved or is not unique
* @throws ServerException when any other error occurs organization updating
*/
@Transactional(rollbackOn = {RuntimeException.class, ApiException.class})
public Organization update(String organizationId, Organization update)
throws NotFoundException, ConflictException, ServerException {
requireNonNull(organizationId, "Required non-null organization id");
requireNonNull(update, "Required non-null organization");
requireNonNull(update.getName(), "Required non-null organization name");
final OrganizationImpl organization = organizationDao.getById(organizationId);
final String oldQualifiedName = organization.getQualifiedName();
final String oldName = organization.getName();
final String newName = update.getName();
final String newQualifiedName = buildQualifiedName(oldQualifiedName, update.getName());
checkNameReservation(newQualifiedName);
organization.setQualifiedName(newQualifiedName);
organizationDao.update(organization);
if (!newName.equals(oldName)) {
updateSuborganizationsQualifiedNames(oldQualifiedName, organization.getQualifiedName());
final String performerName = EnvironmentContext.getCurrent().getSubject().getUserName();
// should be DTO as it sent via json rpc
eventService.publish(
asDto(new OrganizationRenamedEvent(performerName, oldName, newName, organization)));
}
return organization;
}
/**
* Removes organization with given id
*
* @param organizationId organization id
* @throws NullPointerException when {@code organizationId} is null
* @throws ServerException when any other error occurs during organization removing
*/
@Transactional(rollbackOn = {RuntimeException.class, ApiException.class})
public void remove(String organizationId) throws ServerException {
requireNonNull(organizationId, "Required non-null organization id");
try {
OrganizationImpl organization = organizationDao.getById(organizationId);
removeSuborganizations(organizationId);
final List<String> members = removeMembers(organizationId);
organizationDao.remove(organizationId);
final String initiator = EnvironmentContext.getCurrent().getSubject().getUserName();
eventService.publish(asDto(new OrganizationRemovedEvent(initiator, organization, members)));
} catch (NotFoundException e) {
// organization is already removed
}
}
/**
* Gets organization by identifier.
*
* @param organizationId organization id
* @return organization instance
* @throws NullPointerException when {@code organizationId} is null
* @throws NotFoundException when organization with given id was not found
* @throws ServerException when any other error occurs during organization fetching
*/
public Organization getById(String organizationId) throws NotFoundException, ServerException {
requireNonNull(organizationId, "Required non-null organization id");
return organizationDao.getById(organizationId);
}
/**
* Gets organization by name.
*
* @param organizationName organization name
* @return organization instance
* @throws NullPointerException when {@code organizationName} is null
* @throws NotFoundException when organization with given name was not found
* @throws ServerException when any other error occurs during organization fetching
*/
public Organization getByName(String organizationName) throws NotFoundException, ServerException {
requireNonNull(organizationName, "Required non-null organization name");
return organizationDao.getByName(organizationName);
}
/**
* Gets child organizations by given parent.
*
* @param parent id of parent organizations
* @param maxItems the maximum number of organizations to return
* @param skipCount the number of organizations to skip
* @return list of children organizations
* @throws NullPointerException when {@code parent} is null
* @throws ServerException when any other error occurs during organizations fetching
*/
public Page<? extends Organization> getByParent(String parent, int maxItems, long skipCount)
throws ServerException {
requireNonNull(parent, "Required non-null parent");
return organizationDao.getByParent(parent, maxItems, skipCount);
}
/**
* Gets all child organizations by specified parent qualified name.
*
* <p>Note that the result will includes all direct and nested suborganizations.
*
* @param parentQualifiedName qualified name of parent organization
* @param maxItems the maximum number of organizations to return
* @param skipCount the number of organizations to skip
* @return list of children organizations
* @throws NullPointerException when {@code parentQualifiedName} is null
* @throws ServerException when any other error occurs during organizations fetching
*/
public Page<OrganizationImpl> getSuborganizations(
String parentQualifiedName, int maxItems, long skipCount) throws ServerException {
requireNonNull(parentQualifiedName, "Required non-null parent qualified name");
return organizationDao.getSuborganizations(parentQualifiedName, maxItems, skipCount);
}
/**
* Gets list organizations where user is member.
*
* @param userId user id
* @param maxItems the maximum number of organizations to return
* @param skipCount the number of organizations to skip
* @return list of organizations where user is member
* @throws NullPointerException when {@code userId} is null
* @throws ServerException when any other error occurs during organizations fetching
*/
public Page<? extends Organization> getByMember(String userId, int maxItems, int skipCount)
throws ServerException {
requireNonNull(userId, "Required non-null user id");
return memberDao.getOrganizations(userId, maxItems, skipCount);
}
private String buildQualifiedName(String oldQualifiedName, String newName) {
int lastSlashIndex = oldQualifiedName.lastIndexOf("/");
if (lastSlashIndex != -1) { // check that it is not root organization
return oldQualifiedName.substring(0, lastSlashIndex + 1) + newName;
} else {
return newName;
}
}
private void updateSuborganizationsQualifiedNames(
String oldQualifiedName, String newQualifiedName)
throws NotFoundException, ConflictException, ServerException {
for (OrganizationImpl suborganization :
Pages.iterate(
(maxItems, skipCount) ->
organizationDao.getSuborganizations(oldQualifiedName, maxItems, skipCount))) {
suborganization.setQualifiedName(
suborganization.getQualifiedName().replaceFirst(oldQualifiedName, newQualifiedName));
organizationDao.update(suborganization);
}
}
/**
* Gets list of members by specified organization id.
*
* @param organizationId organization identifier
* @param maxItems the maximum number of members to return
* @param skipCount the number of members to skip
* @return list of members
* @throws NullPointerException when {@code organizationId} is null
* @throws ServerException when any other error occurs during organizations fetching
*/
public Page<? extends Member> getMembers(String organizationId, int maxItems, long skipCount)
throws ServerException {
requireNonNull(organizationId, "Required non-null organization id");
return memberDao.getMembers(organizationId, maxItems, skipCount);
}
protected void addFirstMember(Organization organization) throws ServerException {
memberDao.store(
new MemberImpl(
EnvironmentContext.getCurrent().getSubject().getUserId(),
organization.getId(),
OrganizationDomain.getActions()));
}
/**
* Removes suborganizations of given parent organization page by page
*
* @param organizationId parent organization id
*/
@VisibleForTesting
void removeSuborganizations(String organizationId) throws ServerException {
Page<? extends Organization> suborganizationsPage;
do {
// skip count always equals to 0 because elements will be shifted after removing previous
// items
suborganizationsPage = organizationDao.getByParent(organizationId, 100, 0);
for (Organization suborganization : suborganizationsPage.getItems()) {
remove(suborganization.getId());
}
} while (suborganizationsPage.hasNextPage());
}
@VisibleForTesting
List<String> removeMembers(String organizationId) throws ServerException {
List<String> removed = new ArrayList<>();
Page<MemberImpl> membersPage;
do {
// skip count always equals to 0 because elements will be shifted after removing previous
// items
membersPage = memberDao.getMembers(organizationId, 100, 0);
for (MemberImpl member : membersPage.getItems()) {
removed.add(member.getUserId());
memberDao.remove(member.getUserId(), member.getOrganizationId());
}
} while (membersPage.hasNextPage());
return removed;
}
/**
* Checks reservation of organization name
*
* @param organizationName organization name to check
* @throws ConflictException when organization name is reserved and can be used by user
*/
private void checkNameReservation(String organizationName) throws ConflictException {
if (reservedNames.contains(organizationName.toLowerCase())) {
throw new ConflictException(
String.format("Organization name '%s' is reserved", organizationName));
}
}
}

View File

@ -1,287 +0,0 @@
/*
* 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.organization.api;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.multiuser.organization.api.DtoConverter.asDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
import javax.inject.Inject;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Defines Organization REST API.
*
* @author Sergii Leschenko
*/
@Tag(name = "organization", description = "Organization REST API")
@Path("/organization")
public class OrganizationService extends Service {
private final OrganizationManager organizationManager;
private final OrganizationLinksInjector linksInjector;
private final OrganizationValidator organizationValidator;
@Inject
public OrganizationService(
OrganizationManager organizationManager,
OrganizationLinksInjector linksInjector,
OrganizationValidator organizationValidator) {
this.organizationManager = organizationManager;
this.linksInjector = linksInjector;
this.organizationValidator = organizationValidator;
}
@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@Operation(
summary = "Create new organization",
responses = {
@ApiResponse(
responseCode = "201",
description = "The organization successfully created",
content = @Content(schema = @Schema(implementation = OrganizationDto.class))),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(
responseCode = "409",
description =
"Conflict error occurred during the organization creation"
+ "(e.g. The organization with such name already exists)"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public Response create(
@Parameter(description = "Organization to create", required = true)
OrganizationDto organization)
throws BadRequestException, NotFoundException, ConflictException, ServerException {
organizationValidator.checkOrganization(organization);
return Response.status(201)
.entity(
linksInjector.injectLinks(
asDto(organizationManager.create(organization)), getServiceContext()))
.build();
}
@POST
@Path("/{id}")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@Operation(
summary = "Update organization",
responses = {
@ApiResponse(
responseCode = "200",
description = "The organization successfully updated",
content = @Content(schema = @Schema(implementation = OrganizationDto.class))),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(
responseCode = "404",
description = "The organization with given id was not found"),
@ApiResponse(
responseCode = "409",
description =
"Conflict error occurred during the organization creation"
+ "(e.g. The organization with such name already exists)"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public OrganizationDto update(
@Parameter(description = "Organization id") @PathParam("id") String organizationId,
@Parameter(description = "Organization to update", required = true)
OrganizationDto organization)
throws BadRequestException, ConflictException, NotFoundException, ServerException {
organizationValidator.checkOrganization(organization);
return linksInjector.injectLinks(
asDto(organizationManager.update(organizationId, organization)), getServiceContext());
}
@DELETE
@Path("/{id}")
@Operation(
summary = "Remove organization with given id",
responses = {
@ApiResponse(responseCode = "204", description = "The organization successfully removed"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public void remove(
@Parameter(description = "Organization id") @PathParam("id") String organization)
throws ServerException {
organizationManager.remove(organization);
}
@GET
@Produces(APPLICATION_JSON)
@Path("/{organizationId}")
@Operation(
summary = "Get organization by id",
responses = {
@ApiResponse(
responseCode = "200",
description = "The organization successfully fetched",
content = @Content(schema = @Schema(implementation = OrganizationDto.class))),
@ApiResponse(
responseCode = "404",
description = "The organization with given id was not found"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public OrganizationDto getById(
@Parameter(description = "Organization id") @PathParam("organizationId")
String organizationId)
throws NotFoundException, ServerException {
return linksInjector.injectLinks(
asDto(organizationManager.getById(organizationId)), getServiceContext());
}
@GET
@Produces(APPLICATION_JSON)
@Path("/find")
@Operation(
summary = "Find organization by name",
responses = {
@ApiResponse(
responseCode = "200",
description = "The organization successfully fetched",
content = @Content(schema = @Schema(implementation = OrganizationDto.class))),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(
responseCode = "404",
description = "The organization with given name was not found"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public OrganizationDto find(
@Parameter(description = "Organization name", required = true) @QueryParam("name")
String organizationName)
throws NotFoundException, ServerException, BadRequestException {
checkArgument(organizationName != null, "Missed organization's name");
return linksInjector.injectLinks(
asDto(organizationManager.getByName(organizationName)), getServiceContext());
}
@GET
@Produces(APPLICATION_JSON)
@Path("/{parent}/organizations")
@Operation(
summary = "Get child organizations",
responses = {
@ApiResponse(
responseCode = "200",
description = "The child organizations successfully fetched",
content =
@Content(
array =
@ArraySchema(schema = @Schema(implementation = OrganizationDto.class)))),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public Response getByParent(
@Parameter(description = "Parent organization id") @PathParam("parent") String parent,
@Parameter(description = "Max items") @QueryParam("maxItems") @DefaultValue("30")
int maxItems,
@Parameter(description = "Skip count") @QueryParam("skipCount") @DefaultValue("0")
int skipCount)
throws ServerException, BadRequestException {
checkArgument(maxItems >= 0, "The number of items to return can't be negative.");
checkArgument(skipCount >= 0, "The number of items to skip can't be negative.");
final Page<? extends Organization> organizationsPage =
organizationManager.getByParent(parent, maxItems, skipCount);
return Response.ok()
.entity(
organizationsPage.getItems(
organization ->
linksInjector.injectLinks(asDto(organization), getServiceContext())))
.header("Link", createLinkHeader(organizationsPage))
.build();
}
@GET
@Produces(APPLICATION_JSON)
@Operation(
summary =
"Get user's organizations. When user parameter is missed then will be fetched current user's organizations",
responses = {
@ApiResponse(
responseCode = "200",
description = "The organizations successfully fetched",
content =
@Content(
array =
@ArraySchema(schema = @Schema(implementation = OrganizationDto.class)))),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public Response getOrganizations(
@Parameter(description = "User id") @QueryParam("user") String userId,
@Parameter(description = "Max items") @QueryParam("maxItems") @DefaultValue("30")
int maxItems,
@Parameter(description = "Skip count") @QueryParam("skipCount") @DefaultValue("0")
int skipCount)
throws ServerException, BadRequestException {
checkArgument(maxItems >= 0, "The number of items to return can't be negative.");
checkArgument(skipCount >= 0, "The number of items to skip can't be negative.");
if (userId == null) {
userId = EnvironmentContext.getCurrent().getSubject().getUserId();
}
final Page<? extends Organization> organizationsPage =
organizationManager.getByMember(userId, maxItems, skipCount);
return Response.ok()
.entity(
organizationsPage.getItems(
organization ->
linksInjector.injectLinks(asDto(organization), getServiceContext())))
.header("Link", createLinkHeader(organizationsPage))
.build();
}
/**
* Ensures the truth of an expression involving one or more parameters to the calling method.
*
* @param expression a boolean expression
* @param errorMessage the exception message to use if the check fails
* @throws BadRequestException if {@code expression} is false
*/
private void checkArgument(boolean expression, String errorMessage) throws BadRequestException {
if (!expression) {
throw new BadRequestException(errorMessage);
}
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api;
import static com.google.common.base.Strings.isNullOrEmpty;
import javax.inject.Inject;
import org.eclipse.che.account.spi.AccountValidator;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Utils for organization validation.
*
* @author Sergii Leschenko
*/
public class OrganizationValidator {
@Inject private AccountValidator accountValidator;
/**
* Checks whether given organization is valid.
*
* @param organization organization to check
* @throws BadRequestException when organization is not valid
*/
public void checkOrganization(Organization organization) throws BadRequestException {
if (organization == null) {
throw new BadRequestException("Organization required");
}
if (isNullOrEmpty(organization.getName())) {
throw new BadRequestException("Organization name required");
}
if (!accountValidator.isValidName(organization.getName())) {
throw new BadRequestException(
"Organization name may only contain alphanumeric characters or single hyphens inside");
}
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.event;
import static org.eclipse.che.multiuser.organization.shared.event.EventType.MEMBER_ADDED;
import org.eclipse.che.api.core.model.user.User;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
import org.eclipse.che.multiuser.organization.shared.event.MemberEvent;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Defines the event of adding the organization member.
*
* @author Anton Korneta
*/
public class MemberAddedEvent implements MemberEvent {
private final String initiator;
private final User member;
private final Organization organization;
public MemberAddedEvent(String initiator, User member, Organization organization) {
this.initiator = initiator;
this.member = member;
this.organization = organization;
}
@Override
public Organization getOrganization() {
return organization;
}
@Override
public EventType getType() {
return MEMBER_ADDED;
}
@Override
public String getInitiator() {
return initiator;
}
@Override
public User getMember() {
return member;
}
}

View File

@ -1,55 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.event;
import org.eclipse.che.api.core.model.user.User;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
import org.eclipse.che.multiuser.organization.shared.event.MemberEvent;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Defines the event for organization member removal.
*
* @author Anton Korneta
*/
public class MemberRemovedEvent implements MemberEvent {
private final String initiator;
private final User member;
private final Organization organization;
public MemberRemovedEvent(String initiator, User member, Organization organization) {
this.initiator = initiator;
this.member = member;
this.organization = organization;
}
@Override
public EventType getType() {
return EventType.MEMBER_REMOVED;
}
@Override
public Organization getOrganization() {
return organization;
}
@Override
public User getMember() {
return member;
}
@Override
public String getInitiator() {
return initiator;
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.event;
import static org.eclipse.che.multiuser.organization.shared.event.EventType.ORGANIZATION_REMOVED;
import java.util.List;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
import org.eclipse.che.multiuser.organization.shared.event.OrganizationEvent;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Defines organization removed event.
*
* @author Anton Korneta
*/
public class OrganizationRemovedEvent implements OrganizationEvent {
private final String initiator;
private final Organization organization;
private final List<String> members;
public OrganizationRemovedEvent(
String initiator, Organization organization, List<String> members) {
this.initiator = initiator;
this.organization = organization;
this.members = members;
}
@Override
public EventType getType() {
return ORGANIZATION_REMOVED;
}
@Override
public Organization getOrganization() {
return organization;
}
public List<String> getMembers() {
return members;
}
/** Returns name of user who initiated organization removal */
@Override
public String getInitiator() {
return initiator;
}
}

View File

@ -1,62 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.event;
import static org.eclipse.che.multiuser.organization.shared.event.EventType.ORGANIZATION_RENAMED;
import org.eclipse.che.multiuser.organization.shared.event.EventType;
import org.eclipse.che.multiuser.organization.shared.event.OrganizationEvent;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Defines organization renamed event.
*
* @author Anton Korneta
*/
public class OrganizationRenamedEvent implements OrganizationEvent {
private final String initiator;
private final String oldName;
private final String newName;
private final Organization organization;
public OrganizationRenamedEvent(
String initiator, String oldName, String newName, Organization organization) {
this.initiator = initiator;
this.oldName = oldName;
this.newName = newName;
this.organization = organization;
}
@Override
public Organization getOrganization() {
return organization;
}
@Override
public EventType getType() {
return ORGANIZATION_RENAMED;
}
public String getOldName() {
return oldName;
}
public String getNewName() {
return newName;
}
@Override
public String getInitiator() {
return initiator;
}
}

View File

@ -1,87 +0,0 @@
/*
* 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.organization.api.listener;
import static org.eclipse.che.multiuser.organization.api.DtoConverter.asDto;
import jakarta.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.user.User;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.core.notification.EventSubscriber;
import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.multiuser.api.permission.shared.event.PermissionsEvent;
import org.eclipse.che.multiuser.api.permission.shared.model.Permissions;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.api.event.MemberAddedEvent;
import org.eclipse.che.multiuser.organization.api.event.MemberRemovedEvent;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Maps permissions to organization related events.
*
* @author Anton Korneta
*/
@Singleton
public class MemberEventsPublisher implements EventSubscriber<PermissionsEvent> {
private final EventService eventService;
private final UserManager userManager;
private final OrganizationManager organizationManager;
@Inject
public MemberEventsPublisher(
EventService eventService, UserManager userManager, OrganizationManager organizationManager) {
this.eventService = eventService;
this.userManager = userManager;
this.organizationManager = organizationManager;
}
@PostConstruct
private void subscribe() {
eventService.subscribe(this);
}
@Override
public void onEvent(PermissionsEvent event) {
final Permissions permissions = event.getPermissions();
if (OrganizationDomain.DOMAIN_ID.equals(permissions.getDomainId())) {
try {
switch (event.getType()) {
case PERMISSIONS_ADDED:
{
final String initiator = event.getInitiator();
final User addedMember = userManager.getById(permissions.getUserId());
final Organization org = organizationManager.getById(permissions.getInstanceId());
eventService.publish(asDto(new MemberAddedEvent(initiator, addedMember, org)));
break;
}
case PERMISSIONS_REMOVED:
{
final String initiator = event.getInitiator();
final User removedMember = userManager.getById(permissions.getUserId());
final Organization org = organizationManager.getById(permissions.getInstanceId());
eventService.publish(asDto(new MemberRemovedEvent(initiator, removedMember, org)));
break;
}
default:
// do nothing
}
} catch (NotFoundException | ServerException ignored) {
}
}
}
}

View File

@ -1,62 +0,0 @@
/*
* 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.organization.api.listener;
import static org.eclipse.che.multiuser.organization.shared.event.EventType.MEMBER_ADDED;
import static org.eclipse.che.multiuser.organization.shared.event.EventType.MEMBER_REMOVED;
import jakarta.annotation.PostConstruct;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.notification.RemoteSubscriptionManager;
import org.eclipse.che.multiuser.organization.shared.dto.MemberAddedEventDto;
import org.eclipse.che.multiuser.organization.shared.dto.MemberRemovedEventDto;
import org.eclipse.che.multiuser.organization.shared.event.OrganizationEvent;
/**
* Broadcasts organization events through websocket connection.
*
* @author Anton Korneta
*/
@Singleton
public class OrganizationEventsWebsocketBroadcaster {
private final RemoteSubscriptionManager remoteSubscriptionManager;
public static final String ORGANIZATION_MEMBERSHIP_METHOD_NAME = "organization/membershipChanged";
public static final String ORGANIZATION_CHANGED_METHOD_NAME = "organization/statusChanged";
@Inject
public OrganizationEventsWebsocketBroadcaster(
RemoteSubscriptionManager remoteSubscriptionManager) {
this.remoteSubscriptionManager = remoteSubscriptionManager;
}
@PostConstruct
private void subscribe() {
remoteSubscriptionManager.register(
ORGANIZATION_MEMBERSHIP_METHOD_NAME, OrganizationEvent.class, this::predicate);
remoteSubscriptionManager.register(
ORGANIZATION_CHANGED_METHOD_NAME, OrganizationEvent.class, this::predicate);
}
private boolean predicate(OrganizationEvent event, Map<String, String> scope) {
if (MEMBER_ADDED == event.getType()) {
return ((MemberAddedEventDto) event).getMember().getId().equals(scope.get("userId"));
} else if (MEMBER_REMOVED == event.getType()) {
return ((MemberRemovedEventDto) event).getMember().getId().equals(scope.get("userId"));
} else {
return event.getOrganization().getId().equals(scope.get("organizationId"));
}
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.permissions;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
import org.eclipse.che.multiuser.organization.spi.impl.MemberImpl;
/**
* Domain for storing organizations' permissions
*
* @author Sergii Leschenko
*/
public class OrganizationDomain extends AbstractPermissionsDomain<MemberImpl> {
public static final String DOMAIN_ID = "organization";
public static final String UPDATE = "update";
public static final String DELETE = "delete";
public static final String MANAGE_SUBORGANIZATIONS = "manageSuborganizations";
public static final String MANAGE_RESOURCES = "manageResources";
public static final String CREATE_WORKSPACES = "createWorkspaces";
public static final String MANAGE_WORKSPACES = "manageWorkspaces";
private static final List<String> ACTIONS =
ImmutableList.of(
SET_PERMISSIONS,
UPDATE,
DELETE,
MANAGE_SUBORGANIZATIONS,
MANAGE_RESOURCES,
CREATE_WORKSPACES,
MANAGE_WORKSPACES);
/** Returns all the available actions for {@link OrganizationDomain}. */
public static List<String> getActions() {
return ACTIONS;
}
public OrganizationDomain() {
super(DOMAIN_ID, ACTIONS);
}
@Override
protected MemberImpl doCreateInstance(
String userId, String instanceId, List<String> allowedActions) {
return new MemberImpl(userId, instanceId, allowedActions);
}
}

View File

@ -1,133 +0,0 @@
/*
* 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.organization.api.permissions;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.DOMAIN_ID;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.MANAGE_SUBORGANIZATIONS;
import jakarta.ws.rs.Path;
import javax.inject.Inject;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.everrest.CheMethodInvokerFilter;
import org.eclipse.che.multiuser.api.permission.server.SuperPrivilegesChecker;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.api.OrganizationService;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.everrest.core.Filter;
import org.everrest.core.resource.GenericResourceMethod;
/**
* Restricts access to methods of {@link OrganizationService} by users' permissions
*
* <p>Filter contains rules for protecting of all methods of {@link OrganizationService}.<br>
* In case when requested method is unknown filter throws {@link ForbiddenException}
*
* @author Sergii Leschenko
*/
@Filter
@Path("/organization{path:(?!/resource)(/.*)?}")
public class OrganizationPermissionsFilter extends CheMethodInvokerFilter {
static final String CREATE_METHOD = "create";
static final String UPDATE_METHOD = "update";
static final String REMOVE_METHOD = "remove";
static final String GET_BY_PARENT_METHOD = "getByParent";
static final String GET_ORGANIZATIONS_METHOD = "getOrganizations";
static final String GET_BY_ID_METHOD = "getById";
static final String FIND_METHOD = "find";
@Inject private OrganizationManager manager;
@Inject private SuperPrivilegesChecker superPrivilegesChecker;
@Override
protected void filter(GenericResourceMethod genericMethodResource, Object[] arguments)
throws ApiException {
final String methodName = genericMethodResource.getMethod().getName();
final Subject currentSubject = EnvironmentContext.getCurrent().getSubject();
String action;
String organizationId;
switch (methodName) {
case CREATE_METHOD:
final OrganizationDto organization = (OrganizationDto) arguments[0];
if (organization.getParent() != null) {
organizationId = organization.getParent();
action = OrganizationDomain.MANAGE_SUBORGANIZATIONS;
break;
}
// anybody can create root organization
return;
case UPDATE_METHOD:
organizationId = ((String) arguments[0]);
action = OrganizationDomain.UPDATE;
break;
case REMOVE_METHOD:
organizationId = ((String) arguments[0]);
action = OrganizationDomain.DELETE;
break;
case GET_BY_PARENT_METHOD:
organizationId = ((String) arguments[0]);
action = OrganizationDomain.MANAGE_SUBORGANIZATIONS;
if (superPrivilegesChecker.hasSuperPrivileges()) {
return;
}
break;
case GET_ORGANIZATIONS_METHOD:
final String userId = (String) arguments[0];
if (userId != null
&& !userId.equals(currentSubject.getUserId())
&& !superPrivilegesChecker.hasSuperPrivileges()) {
throw new ForbiddenException("The user is able to specify only his own id");
}
// user specified his user id or has super privileges
return;
// methods accessible to every user
case GET_BY_ID_METHOD:
case FIND_METHOD:
return;
default:
throw new ForbiddenException("The user does not have permission to perform this operation");
}
// user is not admin and it is need to check permissions on organization instance level
final Organization organization = manager.getById(organizationId);
final String parentOrganizationId = organization.getParent();
// check permissions on parent organization level when updating or removing child organization
if (parentOrganizationId != null
&& (OrganizationDomain.UPDATE.equals(action) || OrganizationDomain.DELETE.equals(action))) {
if (currentSubject.hasPermission(
OrganizationDomain.DOMAIN_ID, parentOrganizationId, MANAGE_SUBORGANIZATIONS)) {
// user has permissions to manage organization on parent organization level
return;
}
}
if (!currentSubject.hasPermission(DOMAIN_ID, organizationId, action)) {
throw new ForbiddenException(
"The user does not have permission to "
+ action
+ " organization with id '"
+ organizationId
+ "'");
}
}
}

View File

@ -1,110 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.permissions;
import static org.eclipse.che.multiuser.organization.api.listener.OrganizationEventsWebsocketBroadcaster.ORGANIZATION_CHANGED_METHOD_NAME;
import static org.eclipse.che.multiuser.organization.api.listener.OrganizationEventsWebsocketBroadcaster.ORGANIZATION_MEMBERSHIP_METHOD_NAME;
import com.google.common.annotations.VisibleForTesting;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.multiuser.api.permission.server.PermissionsManager;
import org.eclipse.che.multiuser.api.permission.server.jsonrpc.RemoteSubscriptionPermissionCheck;
import org.eclipse.che.multiuser.api.permission.server.jsonrpc.RemoteSubscriptionPermissionManager;
import org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions;
import org.eclipse.che.multiuser.organization.api.listener.OrganizationEventsWebsocketBroadcaster;
/**
* Holds and registers permissions checks for organization related events.
*
* <p>Covers events published via {@link OrganizationEventsWebsocketBroadcaster}.
*
* @author Sergii Leshchenko
*/
@Singleton
public class OrganizationRemoteSubscriptionPermissionsChecks {
private final PermissionsManager permissionsManager;
@Inject
public OrganizationRemoteSubscriptionPermissionsChecks(PermissionsManager permissionsManager) {
this.permissionsManager = permissionsManager;
}
@Inject
public void register(RemoteSubscriptionPermissionManager permissionFilter) {
MembershipsChangedSubscriptionCheck membershipsEventsCheck =
new MembershipsChangedSubscriptionCheck();
permissionFilter.registerCheck(membershipsEventsCheck, ORGANIZATION_MEMBERSHIP_METHOD_NAME);
OrganizationChangedSubscriptionCheck organizationChangedCheck =
new OrganizationChangedSubscriptionCheck(permissionsManager);
permissionFilter.registerCheck(organizationChangedCheck, ORGANIZATION_CHANGED_METHOD_NAME);
}
@VisibleForTesting
static class MembershipsChangedSubscriptionCheck implements RemoteSubscriptionPermissionCheck {
@Override
public void check(String methodName, Map<String, String> scope) throws ForbiddenException {
String userId = scope.get("userId");
if (userId == null) {
throw new ForbiddenException("User id must be specified in scope");
}
String currentUserId = EnvironmentContext.getCurrent().getSubject().getUserId();
if (!currentUserId.equals(userId)) {
throw new ForbiddenException("It is only allowed to listen to own memberships changes");
}
}
}
@VisibleForTesting
static class OrganizationChangedSubscriptionCheck implements RemoteSubscriptionPermissionCheck {
private final PermissionsManager permissionsManager;
public OrganizationChangedSubscriptionCheck(PermissionsManager permissionsManager) {
this.permissionsManager = permissionsManager;
}
@Override
public void check(String methodName, Map<String, String> scope) throws ForbiddenException {
String organizationId = scope.get("organizationId");
if (organizationId == null) {
throw new ForbiddenException("Organization id must be specified in scope");
}
String currentUserId = EnvironmentContext.getCurrent().getSubject().getUserId();
try {
// check if user has any permissions in organisation
// to listen to related events
AbstractPermissions permissions =
permissionsManager.get(currentUserId, OrganizationDomain.DOMAIN_ID, organizationId);
} catch (ConflictException | ServerException e) {
throw new ForbiddenException("Error occurred while permission fetching: " + e.getMessage());
} catch (NotFoundException e) {
throw new ForbiddenException(
"User doesn't have any permissions for the specified organization");
}
}
}
}

View File

@ -1,95 +0,0 @@
/*
* 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.organization.api.permissions;
import jakarta.ws.rs.Path;
import javax.inject.Inject;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.everrest.CheMethodInvokerFilter;
import org.eclipse.che.multiuser.api.permission.server.SuperPrivilegesChecker;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.api.resource.OrganizationResourcesDistributionService;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.everrest.core.Filter;
import org.everrest.core.resource.GenericResourceMethod;
/**
* Restricts access to methods of {@link OrganizationResourcesDistributionService} by users'
* permissions.
*
* <p>Filter contains rules for protecting of all methods of {@link
* OrganizationResourcesDistributionService}.<br>
* In case when requested method is unknown filter throws {@link ForbiddenException}.
*
* @author Sergii Leschenko
*/
@Filter
@Path("/organization/resource{path:(/.*)?}")
public class OrganizationResourceDistributionServicePermissionsFilter
extends CheMethodInvokerFilter {
static final String CAP_RESOURCES_METHOD = "capResources";
static final String GET_RESOURCES_CAP_METHOD = "getResourcesCap";
static final String GET_DISTRIBUTED_RESOURCES = "getDistributedResources";
@Inject private OrganizationManager organizationManager;
@Inject private SuperPrivilegesChecker superPrivilegesChecker;
@Override
protected void filter(GenericResourceMethod genericMethodResource, Object[] arguments)
throws ApiException {
final String methodName = genericMethodResource.getMethod().getName();
final Subject currentSubject = EnvironmentContext.getCurrent().getSubject();
String organizationId;
switch (methodName) {
case GET_RESOURCES_CAP_METHOD:
if (superPrivilegesChecker.hasSuperPrivileges()) {
// user is able to see information about all organizations
return;
}
// fall through
case CAP_RESOURCES_METHOD:
// we should check permissions on parent organization level
Organization organization = organizationManager.getById((String) arguments[0]);
organizationId = organization.getParent();
if (organizationId == null) {
// requested organization is root so manager should throw exception
return;
}
break;
case GET_DISTRIBUTED_RESOURCES:
organizationId = (String) arguments[0];
// get organization to ensure that organization exists
organizationManager.getById(organizationId);
if (superPrivilegesChecker.hasSuperPrivileges()) {
// user is able to see information about all organizations
return;
}
break;
default:
throw new ForbiddenException("The user does not have permission to perform this operation");
}
if (!currentSubject.hasPermission(
OrganizationDomain.DOMAIN_ID, organizationId, OrganizationDomain.MANAGE_RESOURCES)) {
throw new ForbiddenException(
"The user does not have permission to manage resources of organization with id '"
+ organizationId
+ "'");
}
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.permissions;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.CREATE_WORKSPACES;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.DOMAIN_ID;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.MANAGE_RESOURCES;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.MANAGE_WORKSPACES;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.multiuser.api.permission.server.account.AccountOperation;
import org.eclipse.che.multiuser.api.permission.server.account.AccountPermissionsChecker;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
/**
* Defines permissions checking for organizational accounts.
*
* @author Sergii Leshchenko
*/
@Singleton
public class OrganizationalAccountPermissionsChecker implements AccountPermissionsChecker {
@Override
public void checkPermissions(String accountId, AccountOperation operation)
throws ForbiddenException {
Subject subject = EnvironmentContext.getCurrent().getSubject();
switch (operation) {
case CREATE_WORKSPACE:
if (!subject.hasPermission(
OrganizationDomain.DOMAIN_ID, accountId, OrganizationDomain.CREATE_WORKSPACES)) {
throw new ForbiddenException(
"User is not authorized to create workspaces in specified namespace.");
}
break;
case MANAGE_WORKSPACES:
if (!subject.hasPermission(
OrganizationDomain.DOMAIN_ID, accountId, OrganizationDomain.MANAGE_WORKSPACES)) {
throw new ForbiddenException("User is not authorized to use specified namespace.");
}
break;
case SEE_RESOURCE_INFORMATION:
if (subject.hasPermission(DOMAIN_ID, accountId, CREATE_WORKSPACES)
|| subject.hasPermission(DOMAIN_ID, accountId, MANAGE_WORKSPACES)
|| subject.hasPermission(DOMAIN_ID, accountId, MANAGE_RESOURCES)) {
// user is able to see resources usage information
return;
}
throw new ForbiddenException(
"User is not authorized to see resources information of requested organization.");
default:
throw new ForbiddenException("User is not authorized to use specified namespace.");
}
}
@Override
public String getAccountType() {
return OrganizationImpl.ORGANIZATIONAL_ACCOUNT;
}
}

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.resource;
import static java.util.Arrays.asList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.commons.lang.Size;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.eclipse.che.multiuser.resource.api.free.DefaultResourcesProvider;
import org.eclipse.che.multiuser.resource.api.type.RamResourceType;
import org.eclipse.che.multiuser.resource.api.type.RuntimeResourceType;
import org.eclipse.che.multiuser.resource.api.type.TimeoutResourceType;
import org.eclipse.che.multiuser.resource.api.type.WorkspaceResourceType;
import org.eclipse.che.multiuser.resource.spi.impl.ResourceImpl;
/**
* Provided free resources that are available for usage by organizational accounts by default.
*
* @author Sergii Leschenko
*/
@Singleton
public class DefaultOrganizationResourcesProvider implements DefaultResourcesProvider {
private final OrganizationManager organizationManager;
private final long ramPerOrganization;
private final int workspacesPerOrganization;
private final int runtimesPerOrganization;
private final long timeout;
@Inject
public DefaultOrganizationResourcesProvider(
OrganizationManager organizationManager,
@Named("che.limits.organization.workspaces.ram") String ramPerOrganization,
@Named("che.limits.organization.workspaces.count") int workspacesPerOrganization,
@Named("che.limits.organization.workspaces.run.count") int runtimesPerOrganization,
@Named("che.limits.workspace.idle.timeout") long timeout) {
this.timeout = TimeUnit.MILLISECONDS.toMinutes(timeout);
this.organizationManager = organizationManager;
this.ramPerOrganization =
"-1".equals(ramPerOrganization) ? -1 : Size.parseSizeToMegabytes(ramPerOrganization);
this.workspacesPerOrganization = workspacesPerOrganization;
this.runtimesPerOrganization = runtimesPerOrganization;
}
@Override
public String getAccountType() {
return OrganizationImpl.ORGANIZATIONAL_ACCOUNT;
}
@Override
public List<ResourceImpl> getResources(String accountId)
throws ServerException, NotFoundException {
final Organization organization = organizationManager.getById(accountId);
// only root organizations should have own resources
if (organization.getParent() == null) {
return asList(
new ResourceImpl(TimeoutResourceType.ID, timeout, TimeoutResourceType.UNIT),
new ResourceImpl(RamResourceType.ID, ramPerOrganization, RamResourceType.UNIT),
new ResourceImpl(
WorkspaceResourceType.ID, workspacesPerOrganization, WorkspaceResourceType.UNIT),
new ResourceImpl(
RuntimeResourceType.ID, runtimesPerOrganization, RuntimeResourceType.UNIT));
}
return Collections.emptyList();
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.eclipse.che.multiuser.resource.api.ResourceLockKeyProvider;
/**
* Provides resources lock key for accounts with organizational type.
*
* <p>A lock key for any organization is an identifier of the root organization.
*
* @author Sergii Leschenko
*/
@Singleton
public class OrganizationResourceLockKeyProvider implements ResourceLockKeyProvider {
private final OrganizationManager organizationManager;
@Inject
public OrganizationResourceLockKeyProvider(OrganizationManager organizationManager) {
this.organizationManager = organizationManager;
}
@Override
public String getLockKey(String accountId) throws ServerException {
String currentOrganizationId = accountId;
try {
Organization organization = organizationManager.getById(currentOrganizationId);
while (organization.getParent() != null) {
currentOrganizationId = organization.getParent();
organization = organizationManager.getById(currentOrganizationId);
}
return organization.getId();
} catch (NotFoundException e) {
// should not happen
throw new ServerException(e.getLocalizedMessage(), e);
}
}
@Override
public String getAccountType() {
return OrganizationImpl.ORGANIZATIONAL_ACCOUNT;
}
}

View File

@ -1,191 +0,0 @@
/*
* 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.organization.api.resource;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.multiuser.organization.api.DtoConverter;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDistributedResourcesDto;
import org.eclipse.che.multiuser.organization.shared.model.OrganizationDistributedResources;
import org.eclipse.che.multiuser.resource.api.free.ResourceValidator;
import org.eclipse.che.multiuser.resource.shared.dto.ResourceDto;
/**
* REST API for resources distribution between suborganizations.
*
* @author Sergii Leschenko
*/
@Tag(
name = "organization-resource",
description = "REST API for resources distribution between suborganizations")
@Path("/organization/resource")
public class OrganizationResourcesDistributionService extends Service {
private final OrganizationResourcesDistributor resourcesDistributor;
private final ResourceValidator resourceValidator;
@Inject
public OrganizationResourcesDistributionService(
OrganizationResourcesDistributor resourcesDistributor, ResourceValidator resourceValidator) {
this.resourcesDistributor = resourcesDistributor;
this.resourceValidator = resourceValidator;
}
@POST
@Path("/{suborganizationId}/cap")
@Consumes(APPLICATION_JSON)
@Operation(
summary =
"Cap usage of shared resources.. By default suborganization is able to use all parent organization resources."
+ "Cap allow to limit usage of shared resources by suborganization.",
responses = {
@ApiResponse(responseCode = "204", description = "Resources successfully capped"),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(responseCode = "404", description = "Specified organization was not found"),
@ApiResponse(
responseCode = "409",
description = "Specified organization is root organization"),
@ApiResponse(
responseCode = "409",
description = "Suborganization is using shared resources"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public void capResources(
@Parameter(description = "Suborganization id") @PathParam("suborganizationId")
String suborganizationId,
@Parameter(description = "Resources to cap") List<ResourceDto> resourcesCap)
throws BadRequestException, NotFoundException, ConflictException, ServerException {
checkArgument(resourcesCap != null, "Missed resources caps.");
Set<String> resourcesToSet = new HashSet<>();
for (ResourceDto resource : resourcesCap) {
if (!resourcesToSet.add(resource.getType())) {
throw new BadRequestException(
format(
"Resources to cap must contain only one resource with type '%s'.",
resource.getType()));
}
resourceValidator.validate(resource);
}
resourcesDistributor.capResources(suborganizationId, resourcesCap);
}
@GET
@Path("/{suborganizationId}/cap")
@Produces(APPLICATION_JSON)
@Operation(
summary = "Get resources cap of specified suborganization.",
responses = {
@ApiResponse(
responseCode = "200",
description = "Resources caps successfully fetched",
content =
@Content(
array =
@ArraySchema(
schema =
@Schema(
implementation = OrganizationDistributedResourcesDto.class)))),
@ApiResponse(responseCode = "404", description = "Specified organization was not found"),
@ApiResponse(
responseCode = "409",
description = "Specified organization is root organization"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public List<ResourceDto> getResourcesCap(
@Parameter(description = "Suborganization id") @PathParam("suborganizationId")
String suborganization)
throws NotFoundException, ConflictException, ServerException {
return resourcesDistributor.getResourcesCaps(suborganization).stream()
.map(org.eclipse.che.multiuser.resource.api.DtoConverter::asDto)
.collect(toList());
}
@GET
@Path("/{organizationId}")
@Produces(APPLICATION_JSON)
@Operation(
summary = "Get resources which are distributed by specified parent.",
responses = {
@ApiResponse(
responseCode = "200",
description = "Resources caps successfully fetched",
content =
@Content(
array =
@ArraySchema(
schema =
@Schema(
implementation = OrganizationDistributedResourcesDto.class)))),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public Response getDistributedResources(
@Parameter(description = "Organization id") @PathParam("organizationId")
String organizationId,
@Parameter(description = "Max items") @QueryParam("maxItems") @DefaultValue("30")
int maxItems,
@Parameter(description = "Skip count") @QueryParam("skipCount") @DefaultValue("0")
long skipCount)
throws BadRequestException, ServerException {
checkArgument(maxItems >= 0, "The number of items to return can't be negative.");
checkArgument(skipCount >= 0, "The number of items to skip can't be negative.");
final Page<? extends OrganizationDistributedResources> distributedResourcesPage =
resourcesDistributor.getByParent(organizationId, maxItems, skipCount);
return Response.ok()
.entity(distributedResourcesPage.getItems(DtoConverter::asDto))
.header("Link", createLinkHeader(distributedResourcesPage))
.build();
}
/**
* Ensures the truth of an expression involving one or more parameters to the calling method.
*
* @param expression a boolean expression
* @param errorMessage the exception message to use if the check fails
* @throws BadRequestException if {@code expression} is false
*/
private void checkArgument(boolean expression, String errorMessage) throws BadRequestException {
if (!expression) {
throw new BadRequestException(errorMessage);
}
}
}

View File

@ -1,217 +0,0 @@
/*
* Copyright (c) 2012-2024 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.organization.api.resource;
import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.commons.lang.concurrent.Unlocker;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.shared.model.OrganizationDistributedResources;
import org.eclipse.che.multiuser.organization.spi.OrganizationDistributedResourcesDao;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationDistributedResourcesImpl;
import org.eclipse.che.multiuser.resource.api.ResourceAggregator;
import org.eclipse.che.multiuser.resource.api.exception.NoEnoughResourcesException;
import org.eclipse.che.multiuser.resource.api.type.RamResourceType;
import org.eclipse.che.multiuser.resource.api.type.RuntimeResourceType;
import org.eclipse.che.multiuser.resource.api.type.WorkspaceResourceType;
import org.eclipse.che.multiuser.resource.api.usage.ResourceManager;
import org.eclipse.che.multiuser.resource.api.usage.ResourcesLocks;
import org.eclipse.che.multiuser.resource.model.Resource;
/**
* Facade for organization resources operations.
*
* @author Sergii Leschenko
*/
@Singleton
public class OrganizationResourcesDistributor {
private final OrganizationDistributedResourcesDao organizationDistributedResourcesDao;
private final OrganizationManager organizationManager;
private final ResourcesLocks resourcesLocks;
private final ResourceManager resourceManager;
private final ResourceAggregator resourceAggregator;
@Inject
public OrganizationResourcesDistributor(
OrganizationDistributedResourcesDao organizationDistributedResourcesDao,
OrganizationManager organizationManager,
ResourcesLocks resourcesLocks,
ResourceManager resourceManager,
ResourceAggregator resourceAggregator) {
this.organizationDistributedResourcesDao = organizationDistributedResourcesDao;
this.organizationManager = organizationManager;
this.resourcesLocks = resourcesLocks;
this.resourceManager = resourceManager;
this.resourceAggregator = resourceAggregator;
}
/**
* Cap usage of shared resources.
*
* <p>By default suborganization is able to use all parent organization resources Cap allow to
* limit usage of shared resources by suborganization.
*
* @param suborganizationId suborganization id
* @param resourcesCaps resources to capped
* @throws NotFoundException when specified suborganization was not found
* @throws ConflictException when organization with specified id is root organization
* @throws ConflictException when suborganization is currently using more shared resources than
* should be capped
* @throws ServerException when any other error occurs
*/
public void capResources(String suborganizationId, List<? extends Resource> resourcesCaps)
throws NotFoundException, ConflictException, ServerException {
requireNonNull(suborganizationId, "Required non-null suborganization id");
requireNonNull(resourcesCaps, "Required non-null resources to capResources");
checkIsSuborganization(suborganizationId);
// remove caps with amount -1
resourcesCaps = resourcesCaps.stream().filter(res -> res.getAmount() != -1).collect(toList());
// locking resources by suborganization should lock resources whole organization tree
// so we can check resource availability for suborganization organization
try (@SuppressWarnings("unused")
Unlocker u = resourcesLocks.lock(suborganizationId)) {
if (resourcesCaps.isEmpty()) {
organizationDistributedResourcesDao.remove(suborganizationId);
} else {
checkResourcesAvailability(suborganizationId, resourcesCaps);
organizationDistributedResourcesDao.store(
new OrganizationDistributedResourcesImpl(suborganizationId, resourcesCaps));
}
}
}
/**
* Returns resources cap or empty list.
*
* @param suborganizationId suborganization id to fetch resources cap
* @return resources cap or empty list
* @throws NotFoundException when specified suborganization was not found
* @throws ConflictException when organization with specified id is root organization
* @throws ServerException when any other error occurs
*/
public List<? extends Resource> getResourcesCaps(String suborganizationId)
throws NotFoundException, ConflictException, ServerException {
requireNonNull(suborganizationId, "Required non-null suborganization id");
checkIsSuborganization(suborganizationId);
try {
return organizationDistributedResourcesDao.get(suborganizationId).getResourcesCap();
} catch (NotFoundException e) {
return emptyList();
}
}
/**
* Returns distributed resources for specified suborganization.
*
* @param suborganizationId organization id
* @return distributed resources for suborganization with specified id
* @throws NullPointerException when either {@code suborganizationId} is null
* @throws NotFoundException when there is not distributed resources for specified suborganization
* @throws ServerException when any other error occurs
*/
public OrganizationDistributedResources get(String suborganizationId)
throws NotFoundException, ServerException {
requireNonNull(suborganizationId, "Required non-null organization id");
return organizationDistributedResourcesDao.get(suborganizationId);
}
/**
* Returns distributed resources for suborganizations by specified parent organization.
*
* @param organizationId organization id
* @return distributed resources for suborganizations by specified parent organization
* @throws NullPointerException when either {@code organizationId} is null
* @throws ServerException when any other error occurs
*/
public Page<? extends OrganizationDistributedResources> getByParent(
String organizationId, int maxItems, long skipCount) throws ServerException {
requireNonNull(organizationId, "Required non-null organization id");
return organizationDistributedResourcesDao.getByParent(organizationId, maxItems, skipCount);
}
/**
* Checks that suborganization is using less resources that new resources cap defines.
*
* @param suborganizationId identifier of suborganization
* @param newResourcesCap resources to capResources
* @throws ConflictException when parent organization doesn't have enough resources to increase
* distributed resource amount
* @throws ConflictException when resources can't be distributed because suborganization is using
* existing resources or when they are distributed to next organizations level
* @throws ServerException when any other error occurs
*/
@VisibleForTesting
void checkResourcesAvailability(
String suborganizationId, List<? extends Resource> newResourcesCap)
throws NotFoundException, ConflictException, ServerException {
Map<String, Resource> usedResources =
resourceManager.getUsedResources(suborganizationId).stream()
.collect(Collectors.toMap(Resource::getType, Function.identity()));
for (Resource resourceToCheck : newResourcesCap) {
Resource usedResource = usedResources.get(resourceToCheck.getType());
if (usedResource != null) {
try {
resourceAggregator.deduct(resourceToCheck, usedResource);
} catch (NoEnoughResourcesException e) {
throw new ConflictException(
"Resources are currently in use. "
+ getMessage(e.getMissingResources().get(0).getType()));
}
}
}
}
@VisibleForTesting
String getMessage(String requiredResourceType) {
switch (requiredResourceType) {
case RamResourceType.ID:
return "You can't decrease RAM CAP, while the resources are in use. "
+ "Free resources, by stopping workspaces, before changing the RAM CAP.";
case WorkspaceResourceType.ID:
return "You can't reduce the workspaces CAP to a value lower than the number of workspaces currently created. "
+ "Free resources, by removing workspaces, before changing the workspaces CAP.";
case RuntimeResourceType.ID:
return "You can't reduce the running workspaces CAP to a value lower than the number of workspaces currently running. "
+ "Free resources, by stopping workspaces, before changing the running workspaces CAP.";
default:
return "You can't reduce them while they are used. "
+ "Free resources before changing the resources CAP.";
}
}
private String checkIsSuborganization(String organizationId)
throws NotFoundException, ConflictException, ServerException {
String parentOrganization = organizationManager.getById(organizationId).getParent();
if (parentOrganization == null) {
throw new ConflictException("It is not allowed to cap resources for root organization.");
}
return parentOrganization;
}
}

View File

@ -1,145 +0,0 @@
/*
* 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.organization.api.resource;
import com.google.common.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Pages;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.resource.api.AvailableResourcesProvider;
import org.eclipse.che.multiuser.resource.api.ResourceAggregator;
import org.eclipse.che.multiuser.resource.api.exception.NoEnoughResourcesException;
import org.eclipse.che.multiuser.resource.api.usage.ResourceManager;
import org.eclipse.che.multiuser.resource.model.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides available resources for organizational and suborganizational accounts.
*
* <p>Root organizational account can use resources by itself or share them for its
* suborganizations. So available resources equal to total resources minus resources which are
* already used by organization or by any of its suborganizations.
*
* <p>Suborganizational account can use all of parent resources or limited amount. So available
* resource equal to minimum of parent available resources and parent shared resources minus
* resources which are used by suborganization and its suborganizations.
*
* @author Sergii Leschenko
*/
@Singleton
public class OrganizationalAccountAvailableResourcesProvider implements AvailableResourcesProvider {
private static final Logger LOG =
LoggerFactory.getLogger(OrganizationalAccountAvailableResourcesProvider.class);
private final Provider<ResourceManager> resourceManagerProvider;
private final ResourceAggregator resourceAggregator;
private final OrganizationManager organizationManager;
@Inject
public OrganizationalAccountAvailableResourcesProvider(
Provider<ResourceManager> resourceManagerProvider,
ResourceAggregator resourceAggregator,
OrganizationManager organizationManager) {
this.resourceManagerProvider = resourceManagerProvider;
this.resourceAggregator = resourceAggregator;
this.organizationManager = organizationManager;
}
@Override
public List<? extends Resource> getAvailableResources(String accountId)
throws NotFoundException, ServerException {
Organization organization = organizationManager.getById(accountId);
if (organization.getParent() == null) {
return getAvailableOrganizationResources(organization);
} else {
Organization parentOrganization = organizationManager.getById(organization.getParent());
return resourceAggregator.min(
resourceAggregator.intersection(
getAvailableOrganizationResources(parentOrganization),
getAvailableOrganizationResources(organization)));
}
}
/**
* Returns total resources minus resources which are already used by organization or by any of its
* suborganizations.
*
* @param organization organization id to calculate its available resources
* @return resources which are available for usage by specified organization
* @throws NotFoundException when organization with specified id doesn't exist
* @throws ServerException when any other exception occurs on calculation of available resources
*/
@VisibleForTesting
List<? extends Resource> getAvailableOrganizationResources(Organization organization)
throws NotFoundException, ServerException {
final ResourceManager resourceManager = resourceManagerProvider.get();
final List<? extends Resource> total = resourceManager.getTotalResources(organization.getId());
final List<Resource> unavailable =
new ArrayList<>(resourceManager.getUsedResources(organization.getId()));
unavailable.addAll(getUsedResourcesBySuborganizations(organization.getQualifiedName()));
try {
return resourceAggregator.deduct(total, unavailable);
} catch (NoEnoughResourcesException e) {
LOG.warn(
"Organization with id {} uses more resources {} than it has {}.",
organization.getId(),
format(unavailable),
format(total));
return resourceAggregator.excess(total, unavailable);
}
}
/**
* Returns resources which are used by suborganizations of specified organization.
*
* <p>Note that the result will includes used resources of all direct and nested suborganizations.
*
* @param parentQualifiedName parent qualified name, e.g. 'parentName/suborgName
* @return resources which are used by suborganizations of specified organization.
* @throws ServerException when any other exception occurs on calculation of used resources
*/
@VisibleForTesting
List<Resource> getUsedResourcesBySuborganizations(String parentQualifiedName)
throws NotFoundException, ServerException {
ResourceManager resourceManager = resourceManagerProvider.get();
List<Resource> usedResources = new ArrayList<>();
for (Organization suborganization :
Pages.iterate(
(maxItems, skipCount) ->
organizationManager.getSuborganizations(
parentQualifiedName, maxItems, skipCount))) {
usedResources.addAll(resourceManager.getUsedResources(suborganization.getId()));
}
return usedResources;
}
/** Returns formatted string for list of resources. */
private static String format(Collection<? extends Resource> resources) {
return '['
+ resources.stream()
.map(
resource -> resource.getAmount() + resource.getUnit() + " of " + resource.getType())
.collect(Collectors.joining(", "))
+ ']';
}
}

View File

@ -1,125 +0,0 @@
/*
* 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.organization.api.resource;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toMap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.eclipse.che.account.api.AccountManager;
import org.eclipse.che.account.shared.model.Account;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.eclipse.che.multiuser.resource.api.ResourcesProvider;
import org.eclipse.che.multiuser.resource.api.usage.ResourceManager;
import org.eclipse.che.multiuser.resource.model.ProvidedResources;
import org.eclipse.che.multiuser.resource.model.Resource;
import org.eclipse.che.multiuser.resource.spi.impl.ProvidedResourcesImpl;
import org.eclipse.che.multiuser.resource.spi.impl.ResourceImpl;
/**
* Provides resources that are shared for suborganization by its parent organization.
*
* <p>By default suborganizations are able to use parent's resources. Parent organization can limit
* usage of resources by suborganization by setting resources caps.
*
* @author Sergii Leschenko
*/
@Singleton
public class SuborganizationResourcesProvider implements ResourcesProvider {
public static final String PARENT_RESOURCES_PROVIDER = "parentOrganization";
private final AccountManager accountManager;
private final OrganizationManager organizationManager;
private final Provider<OrganizationResourcesDistributor> distributorProvider;
private final Provider<ResourceManager> resourceManagerProvider;
@Inject
public SuborganizationResourcesProvider(
AccountManager accountManager,
OrganizationManager organizationManager,
Provider<OrganizationResourcesDistributor> distributorProvider,
Provider<ResourceManager> resourceManagerProvider) {
this.accountManager = accountManager;
this.organizationManager = organizationManager;
this.distributorProvider = distributorProvider;
this.resourceManagerProvider = resourceManagerProvider;
}
@Override
public List<ProvidedResources> getResources(String accountId)
throws NotFoundException, ServerException {
final Account account = accountManager.getById(accountId);
String parent;
if (!OrganizationImpl.ORGANIZATIONAL_ACCOUNT.equals(account.getType())
|| (parent = organizationManager.getById(accountId).getParent()) == null) {
return emptyList();
}
// given account is suborganization's account and can have resources provided by parent
List<? extends Resource> parentTotalResources =
resourceManagerProvider.get().getTotalResources(parent);
if (!parentTotalResources.isEmpty()) {
try {
List<? extends Resource> resourcesCaps =
distributorProvider.get().getResourcesCaps(accountId);
return singletonList(
new ProvidedResourcesImpl(
PARENT_RESOURCES_PROVIDER,
null,
accountId,
-1L,
-1L,
cap(parentTotalResources, resourcesCaps)));
} catch (ConflictException e) {
throw new ServerException(e.getLocalizedMessage());
}
}
return emptyList();
}
private List<ResourceImpl> cap(
Collection<? extends Resource> source, List<? extends Resource> caps) {
final Map<String, Resource> resourcesCaps =
caps.stream().collect(toMap(Resource::getType, Function.identity()));
return source.stream()
.map(
resource -> {
Resource resourceCap = resourcesCaps.get(resource.getType());
if (resourceCap != null) {
if (resource.getAmount() == -1) {
return resourceCap;
} else if (resourceCap.getAmount() < resource.getAmount()) {
return resourceCap;
}
}
return resource;
})
.map(ResourceImpl::new)
.collect(Collectors.toList());
}
}

View File

@ -1,95 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.spi;
import java.util.List;
import java.util.Optional;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.multiuser.organization.spi.impl.MemberImpl;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
/**
* Defines data access object for {@link MemberImpl}
*
* @author Sergii Leschenko
*/
public interface MemberDao {
/**
* Stores (adds or updates) member.
*
* @param member member to store
* @return optional with updated member, other way empty optional must be returned
* @throws NullPointerException when {@code member} is null
* @throws ServerException when organization or user doesn't exist
* @throws ServerException when any other error occurs during member storing
*/
Optional<MemberImpl> store(MemberImpl member) throws ServerException;
/**
* Removes member with given organization and user
*
* @param userId id of user
* @param organizationId id of organization
* @throws NullPointerException when {@code organizationId} or {@code userId} is null
* @throws ServerException when any other error occurs during member removing
*/
void remove(String userId, String organizationId) throws ServerException;
/**
* Returns member for specified organization and user
*
* @param organizationId organization id
* @param userId user id
* @return member for specified organization and user
* @throws NullPointerException when {@code organizationId} or {@code userId} is null
* @throws NotFoundException when member for given user and organization was not found
* @throws ServerException when any other error occurs during member fetching
*/
MemberImpl getMember(String organizationId, String userId)
throws NotFoundException, ServerException;
/**
* Returns all members of given organization
*
* @param organizationId organization id
* @param maxItems the maximum number of members to return
* @param skipCount the number of members to skip
* @throws NullPointerException when {@code organizationId} is null
* @throws ServerException when any other error occurs during members fetching
*/
Page<MemberImpl> getMembers(String organizationId, int maxItems, long skipCount)
throws ServerException;
/**
* Returns all memberships of given user
*
* @param userId user id
* @throws NullPointerException when {@code userId} is null
* @throws ServerException when any other error occurs during members fetching
*/
List<MemberImpl> getMemberships(String userId) throws ServerException;
/**
* Gets list organizations where user is member.
*
* @param userId user id
* @param maxItems the maximum number of organizations to return
* @param skipCount the number of organizations to skip
* @return list of organizations where user is member
* @throws NullPointerException when {@code userId} is null
* @throws ServerException when any other error occurs during organizations fetching
*/
Page<OrganizationImpl> getOrganizations(String userId, int maxItems, long skipCount)
throws ServerException;
}

View File

@ -1,105 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.spi;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
/**
* Defines data access object for {@link OrganizationImpl}
*
* @author Sergii Leschenko
*/
public interface OrganizationDao {
/**
* Creates organization.
*
* @param organization organization to create
* @throws NullPointerException when {@code organization} is null
* @throws ConflictException when organization with such id/name already exists
* @throws ServerException when any other error occurs during organization creation
*/
void create(OrganizationImpl organization) throws ServerException, ConflictException;
/**
* Updates organization with new entity.
*
* @param update organization update
* @throws NullPointerException when {@code update} is null
* @throws NotFoundException when organization with id {@code organization.getId()} doesn't exist
* @throws ConflictException when name updated with a value which is not unique
* @throws ServerException when any other error occurs organization updating
*/
void update(OrganizationImpl update) throws NotFoundException, ConflictException, ServerException;
/**
* Removes organization with given id
*
* @param organizationId organization id
* @throws NullPointerException when {@code organizationId} is null
* @throws ServerException when any other error occurs during organization removing
*/
void remove(String organizationId) throws ServerException;
/**
* Gets organization by identifier.
*
* @param organizationId organization id
* @return organization instance
* @throws NullPointerException when {@code organizationId} is null
* @throws NotFoundException when organization with given id was not found
* @throws ServerException when any other error occurs during organization fetching
*/
OrganizationImpl getById(String organizationId) throws NotFoundException, ServerException;
/**
* Gets organization by name.
*
* @param organizationName organization name
* @return organization instance
* @throws NullPointerException when {@code organizationName} is null
* @throws NotFoundException when organization with given name was not found
* @throws ServerException when any other error occurs during organization fetching
*/
OrganizationImpl getByName(String organizationName) throws NotFoundException, ServerException;
/**
* Gets child organizations by given parent.
*
* @param parent id of parent organization
* @param maxItems the maximum number of organizations to return
* @param skipCount the number of organizations to skip
* @return list of children organizations
* @throws NullPointerException when {@code parent} is null
* @throws ServerException when any other error occurs during organizations fetching
*/
Page<OrganizationImpl> getByParent(String parent, int maxItems, long skipCount)
throws ServerException;
/**
* Gets all child organizations by specified parent qualified name.
*
* <p>Note that the result will includes all direct and nested suborganizations.
*
* @param parentQualifiedName qualified name of parent organization
* @param maxItems the maximum number of organizations to return
* @param skipCount the number of organizations to skip
* @return list of children organizations
* @throws NullPointerException when {@code parentQualifiedName} is null
* @throws ServerException when any other error occurs during organizations fetching
*/
Page<OrganizationImpl> getSuborganizations(
String parentQualifiedName, int maxItems, long skipCount) throws ServerException;
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.spi;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationDistributedResourcesImpl;
/**
* Defines data access object contract for {@link OrganizationDistributedResourcesImpl}.
*
* @author Sergii Leschenko
*/
public interface OrganizationDistributedResourcesDao {
/**
* Stores (creates or updated) distributed resources for suborganization.
*
* @param distributedResources distributed resources to store
* @throws NullPointerException when either {@code distributedResources} is null
* @throws ServerException when any other error occurs
*/
void store(OrganizationDistributedResourcesImpl distributedResources) throws ServerException;
/**
* Returns distributed resources for specified suborganization.
*
* @param organizationId organization id
* @return distributed resources for specified suborganization
* @throws NullPointerException when either {@code organizationId} is null
* @throws NotFoundException when organization with specified id doesn't have distributed
* resources
* @throws ServerException when any other error occurs
*/
OrganizationDistributedResourcesImpl get(String organizationId)
throws NotFoundException, ServerException;
/**
* Returns distributed resources for suborganizations of given parent organization.
*
* @param organizationId organization id
* @return distributed resources for suborganizations of given parent organization
* @throws NullPointerException when either {@code organizationId} is null
* @throws ServerException when any other error occurs
*/
Page<OrganizationDistributedResourcesImpl> getByParent(
String organizationId, int maxItems, long skipCount) throws ServerException;
/**
* Remove distributed organization resources.
*
* @param organizationId organization id
* @throws NullPointerException when either {@code organizationId} is null
* @throws ServerException when any other error occurs
*/
void remove(String organizationId) throws ServerException;
}

View File

@ -1,123 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.spi.impl;
import java.util.List;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain;
import org.eclipse.che.multiuser.organization.shared.model.Member;
/**
* Data object for {@link Member}.
*
* @author Sergii Leschenko
*/
@Entity(name = "Member")
@NamedQueries({
@NamedQuery(
name = "Member.getMember",
query =
"SELECT m "
+ "FROM Member m "
+ "WHERE m.userId = :userId AND m.organizationId = :organizationId"),
@NamedQuery(
name = "Member.getByOrganization",
query = "SELECT m " + "FROM Member m " + "WHERE m.organizationId = :organizationId"),
@NamedQuery(
name = "Member.getCountByOrganizationId",
query = "SELECT COUNT(m) " + "FROM Member m " + "WHERE m.organizationId = :organizationId"),
@NamedQuery(
name = "Member.getByUser",
query = "SELECT m " + "FROM Member m " + "WHERE m.userId = :userId"),
@NamedQuery(
name = "Member.getOrganizations",
query = "SELECT org " + "FROM Member m, m.organization org " + "WHERE m.userId = :userId"),
@NamedQuery(
name = "Member.getOrganizationsCount",
query = "SELECT COUNT(m) " + "FROM Member m " + "WHERE m.userId = :userId ")
})
@Table(name = "che_member")
public class MemberImpl extends AbstractPermissions implements Member {
@Column(name = "organization_id")
private String organizationId;
@ElementCollection(fetch = FetchType.EAGER)
@Column(name = "actions")
@CollectionTable(name = "che_member_actions", joinColumns = @JoinColumn(name = "member_id"))
protected List<String> actions;
@ManyToOne
@JoinColumn(
name = "organization_id",
referencedColumnName = "id",
insertable = false,
updatable = false)
private OrganizationImpl organization;
public MemberImpl() {}
public MemberImpl(String userId, String organizationId, List<String> actions) {
super(userId);
this.organizationId = organizationId;
if (actions != null) {
this.actions = actions;
}
}
public MemberImpl(Member member) {
this(member.getUserId(), member.getOrganizationId(), member.getActions());
}
@Override
public String getInstanceId() {
return organizationId;
}
@Override
public String getDomainId() {
return OrganizationDomain.DOMAIN_ID;
}
@Override
public List<String> getActions() {
return actions;
}
@Override
public String getOrganizationId() {
return organizationId;
}
@Override
public String toString() {
return "MemberImpl{"
+ "userId='"
+ userId
+ '\''
+ ", organizationId='"
+ organizationId
+ '\''
+ ", actions="
+ actions
+ '}';
}
}

View File

@ -1,139 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.spi.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.eclipse.che.multiuser.organization.shared.model.OrganizationDistributedResources;
import org.eclipse.che.multiuser.resource.model.Resource;
import org.eclipse.che.multiuser.resource.spi.impl.ResourceImpl;
/**
* Data object for {@link OrganizationDistributedResources}.
*
* @author Sergii Leschenko
*/
@Entity(name = "OrganizationDistributedResources")
@NamedQueries({
@NamedQuery(
name = "OrganizationDistributedResources.get",
query =
"SELECT r "
+ "FROM OrganizationDistributedResources r "
+ "WHERE r.organizationId = :organizationId"),
@NamedQuery(
name = "OrganizationDistributedResources.getByParent",
query =
"SELECT r "
+ "FROM OrganizationDistributedResources r "
+ "WHERE r.organization.parent = :parent"),
@NamedQuery(
name = "OrganizationDistributedResources.getCountByParent",
query =
"SELECT COUNT(r) "
+ "FROM OrganizationDistributedResources r "
+ "WHERE r.organization.parent = :parent")
})
@Table(name = "che_organization_distributed_resources")
public class OrganizationDistributedResourcesImpl implements OrganizationDistributedResources {
@Id
@Column(name = "organization_id")
private String organizationId;
@PrimaryKeyJoinColumn private OrganizationImpl organization;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(
name = "che_organization_distributed_resources_resource",
joinColumns = @JoinColumn(name = "organization_distributed_resources_id"),
inverseJoinColumns = @JoinColumn(name = "resource_id"))
private List<ResourceImpl> resourcesCap;
public OrganizationDistributedResourcesImpl() {}
public OrganizationDistributedResourcesImpl(
OrganizationDistributedResources organizationDistributedResource) {
this(
organizationDistributedResource.getOrganizationId(),
organizationDistributedResource.getResourcesCap());
}
public OrganizationDistributedResourcesImpl(
String organizationId, List<? extends Resource> resourcesCap) {
this.organizationId = organizationId;
if (resourcesCap != null) {
this.resourcesCap = resourcesCap.stream().map(ResourceImpl::new).collect(Collectors.toList());
}
}
@Override
public String getOrganizationId() {
return organizationId;
}
@Override
public List<ResourceImpl> getResourcesCap() {
if (resourcesCap == null) {
resourcesCap = new ArrayList<>();
}
return resourcesCap;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof OrganizationDistributedResourcesImpl)) {
return false;
}
final OrganizationDistributedResourcesImpl that = (OrganizationDistributedResourcesImpl) obj;
return Objects.equals(organizationId, that.organizationId)
&& Objects.equals(organization, that.organization)
&& getResourcesCap().equals(that.getResourcesCap());
}
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + Objects.hashCode(organizationId);
hash = 31 * hash + Objects.hashCode(organization);
hash = 31 * hash + getResourcesCap().hashCode();
return hash;
}
@Override
public String toString() {
return "OrganizationDistributedResourcesImpl{"
+ "organizationId='"
+ organizationId
+ '\''
+ ", organization="
+ organization
+ ", resourcesCaps="
+ getResourcesCap()
+ '}';
}
}

View File

@ -1,178 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.spi.impl;
import java.util.Objects;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
/**
* Data object for {@link Organization}.
*
* @author Sergii Leschenko
*/
@Entity(name = "Organization")
@NamedQueries({
@NamedQuery(
name = "Organization.getByName",
query = "SELECT o " + "FROM Organization o " + "WHERE o.account.name = :name"),
@NamedQuery(
name = "Organization.getByParent",
query = "SELECT o " + "FROM Organization o " + "WHERE o.parent = :parent "),
@NamedQuery(
name = "Organization.getByParentCount",
query = "SELECT COUNT(o) " + "FROM Organization o " + "WHERE o.parent = :parent "),
@NamedQuery(
name = "Organization.getSuborganizations",
query = "SELECT o " + "FROM Organization o " + "WHERE o.account.name LIKE :qualifiedName "),
@NamedQuery(
name = "Organization.getSuborganizationsCount",
query =
"SELECT COUNT(o) " + "FROM Organization o " + "WHERE o.account.name LIKE :qualifiedName ")
})
@Table(name = "che_organization")
public class OrganizationImpl implements Organization {
public static final String ORGANIZATIONAL_ACCOUNT = "organizational";
@Id
@Column(name = "id")
private String id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "account_id", nullable = false)
private AccountImpl account;
@Column(name = "parent")
private String parent;
// Mapping exists for explicit constraints which allows
// jpa backend to perform operations in correct order
@ManyToOne
@JoinColumn(name = "parent", insertable = false, updatable = false)
private OrganizationImpl parentObj;
public OrganizationImpl() {}
public OrganizationImpl(Organization organization) {
this(organization.getId(), organization.getQualifiedName(), organization.getParent());
}
public OrganizationImpl(String id, String qualifiedName, String parent) {
this.id = id;
this.account = new AccountImpl(id, qualifiedName, ORGANIZATIONAL_ACCOUNT);
this.parent = parent;
}
@Override
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String getName() {
String qualifiedName = getQualifiedName();
if (qualifiedName == null) {
return null;
}
int lastSlashIndex = qualifiedName.lastIndexOf("/");
if (lastSlashIndex == -1) {
return qualifiedName;
}
return qualifiedName.substring(lastSlashIndex + 1);
}
@Override
public String getQualifiedName() {
if (account != null) {
return account.getName();
}
return null;
}
public void setQualifiedName(String qualifiedName) {
if (account != null) {
account.setName(qualifiedName);
}
}
@Override
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public AccountImpl getAccount() {
return account;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof OrganizationImpl)) {
return false;
}
OrganizationImpl that = (OrganizationImpl) o;
return Objects.equals(id, that.id)
&& Objects.equals(getName(), that.getName())
&& Objects.equals(parent, that.parent);
}
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + Objects.hashCode(id);
hash = 31 * hash + Objects.hashCode(getName());
hash = 31 * hash + Objects.hashCode(getQualifiedName());
hash = 31 * hash + Objects.hashCode(parent);
return hash;
}
@Override
public String toString() {
return "OrganizationImpl{"
+ "id='"
+ id
+ '\''
+ ", name='"
+ getName()
+ '\''
+ ", qualifiedName='"
+ getQualifiedName()
+ '\''
+ ", parent='"
+ parent
+ '\''
+ '}';
}
}

View File

@ -1,23 +0,0 @@
\<html>
\<head>
\<style>@import url(https://fonts.googleapis.com/css?family=Roboto);\</style>
\</head>
\<body style="width: 700px; margin: 0 auto; font-size: 16px; font-family: Roboto;">
\<div style="background-color: #292c2f; color: white; padding: 25px 10px;">
\<span style="font-size: 26px; color: #cccccc;">Eclipse Che\</span>
\<div style="height: 50px">\</div>
\<span style="font-size: 24px; color: white;">Organization has been deleted\</span>
\</div>
\<div style="padding: 10px;">
\<p>Hi,\</p>
\<p>\<b><organizationName>\</b> organization has been deleted.\</p>
\</div>
\<div style="background-color: #292c2f; color: white; padding: 15px 10px 47px 10px;">
\<a href="http://www.eclipse.org/che/">
\<img src="https://www.eclipse.org/che/images/logo-eclipseche.svg" height="32" align="left" alt=" " />
\</a>
\</div>
\</body>
\</html>

View File

@ -1,23 +0,0 @@
\<html>
\<head>
\<style>@import url(https://fonts.googleapis.com/css?family=Roboto);\</style>
\</head>
\<body style="width: 700px; margin: 0 auto; font-size: 16px; font-family: Roboto;">
\<div style="background-color: #292c2f; color: white; padding: 25px 10px;">
\<span style="font-size: 26px; color: #cccccc;">Eclipse Che\</span>
\<div style="height: 50px">\</div>
\<span style="font-size: 24px; color: white;">Organization has been renamed\</span>
\</div>
\<div style="padding: 10px;">
\<p>Hi,\</p>
\<p>\<b><orgOldName>\</b> organization has been renamed to \<b><orgNewName>\</b>\</p>
\</div>
\<div style="background-color: #292c2f; color: white; padding: 15px 10px 47px 10px;">
\<a href="http://www.eclipse.org/che/">
\<img src="https://www.eclipse.org/che/images/logo-eclipseche.svg" height="32" align="left" alt=" " />
\</a>
\</div>
\</body>
\</html>

View File

@ -1,24 +0,0 @@
\<html>
\<head>
\<style>@import url(https://fonts.googleapis.com/css?family=Roboto);\</style>
\</head>
\<body style="width: 700px; margin: 0 auto; font-size: 16px; font-family: Roboto;">
\<div style="background-color: #292c2f; color: white; padding: 25px 10px;">
\<span style="font-size: 26px; color: #cccccc;">Eclipse Che\</span>
\<div style="height: 50px">\</div>
\<span style="font-size: 24px; color: white;">User added to organization\</span>
\</div>
\<div style="padding: 10px;">
\<p>Hi,\</p>
\<p>\<b><initiator>\</b> added you to a Che organization called \<b><organizationName>\</b>.\</p>
\<p>Access the organization here \<a href="<dashboardEndpoint>/#/organization/<orgQualifiedName>">link\</a>.\</p>
\</div>
\<div style="background-color: #292c2f; color: white; padding: 15px 10px 47px 10px;">
\<a href="http://www.eclipse.org/che/">
\<img src="https://www.eclipse.org/che/images/logo-eclipseche.svg" height="32" align="left" alt=" " />
\</a>
\</div>
\</body>
\</html>

View File

@ -1,24 +0,0 @@
\<html>
\<head>
\<style>@import url(https://fonts.googleapis.com/css?family=Roboto);\</style>
\</head>
\<body style="width: 700px; margin: 0 auto; font-size: 16px; font-family: Roboto;">
\<div style="background-color: #292c2f; color: white; padding: 25px 10px;">
\<span style="font-size: 26px; color: #cccccc;">Eclipse Che\</span>
\<div style="height: 50px">\</div>
\<span style="font-size: 24px; color: white;">User removed from organization\</span>
\</div>
\<div style="padding: 10px;">
\<p>Hi,\</p>
\<p>\<b><initiator>\</b> removed you from a Che organization called \<b><organizationName>\</b>.\</p>
\</div>
\<div style="background-color: #292c2f; color: white; padding: 15px 10px 47px 10px;">
\<a href="http://www.eclipse.org/che/">
\<img src="https://www.eclipse.org/che/images/logo-eclipseche.svg" height="32" align="left" alt=" " />
\</a>
\</div>
\</body>
\</html>

View File

@ -1,61 +0,0 @@
/*
* 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.organization.api;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import jakarta.ws.rs.core.UriBuilder;
import org.eclipse.che.api.core.rest.ServiceContext;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.organization.shared.Constants;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
import org.everrest.core.impl.uri.UriBuilderImpl;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Tests for {@link org.eclipse.che.multiuser.organization.api.OrganizationLinksInjector}
*
* @author Sergii Leschenko
*/
@Listeners(MockitoTestNGListener.class)
public class OrganizationLinksInjectorTest {
private static final String URI_BASE = "http://localhost:8080";
@Mock ServiceContext context;
OrganizationLinksInjector organizationLinksInjector = new OrganizationLinksInjector();
@BeforeMethod
public void setUp() {
final UriBuilder uriBuilder = new UriBuilderImpl();
uriBuilder.uri(URI_BASE);
when(context.getBaseUriBuilder()).thenReturn(uriBuilder);
}
@Test
public void shouldInjectLinks() {
final OrganizationDto organization = DtoFactory.newDto(OrganizationDto.class).withId("org123");
final OrganizationDto withLinks = organizationLinksInjector.injectLinks(organization, context);
assertEquals(withLinks.getLinks().size(), 2);
assertNotNull(withLinks.getLink(Constants.LINK_REL_SELF));
assertNotNull(withLinks.getLink(Constants.LINK_REL_SUBORGANIZATIONS));
}
}

View File

@ -1,359 +0,0 @@
/*
* Copyright (c) 2012-2024 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.organization.api;
import static java.util.Collections.singletonList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
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 static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertNotNull;
import java.util.Collections;
import java.util.List;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
import org.eclipse.che.multiuser.organization.shared.model.Member;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.organization.spi.MemberDao;
import org.eclipse.che.multiuser.organization.spi.OrganizationDao;
import org.eclipse.che.multiuser.organization.spi.impl.MemberImpl;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Tests for {@link org.eclipse.che.multiuser.organization.api.OrganizationManager}
*
* @author Sergii Leschenko
*/
@Listeners(MockitoTestNGListener.class)
public class OrganizationManagerTest {
@Captor private ArgumentCaptor<OrganizationImpl> organizationCaptor;
private static final String USER_NAME = "user-name";
private static final String USER_ID = "user-id";
@Mock private OrganizationDao organizationDao;
@Mock private MemberDao memberDao;
@Mock private EventService eventService;
private OrganizationManager manager;
@BeforeMethod
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
manager =
spy(
new OrganizationManager(
eventService, organizationDao, memberDao, new String[] {"reserved"}));
lenient()
.when(eventService.publish(any()))
.thenAnswer(invocation -> invocation.getArguments()[0]);
EnvironmentContext.getCurrent()
.setSubject(new SubjectImpl(USER_NAME, USER_ID, "userToken", false));
}
@AfterMethod
public void tearDown() throws Exception {
EnvironmentContext.reset();
}
@Test
public void shouldCreateOrganization() throws Exception {
final Organization toCreate = DtoFactory.newDto(OrganizationDto.class).withName("newOrg");
manager.create(toCreate);
verify(organizationDao).create(organizationCaptor.capture());
final OrganizationImpl createdOrganization = organizationCaptor.getValue();
assertEquals(createdOrganization.getName(), toCreate.getName());
assertEquals(createdOrganization.getQualifiedName(), toCreate.getName());
assertEquals(createdOrganization.getParent(), toCreate.getParent());
verify(memberDao)
.store(
new MemberImpl(USER_ID, createdOrganization.getId(), OrganizationDomain.getActions()));
}
@Test
public void shouldCreateSuborganization() throws Exception {
final OrganizationImpl parentOrganization = new OrganizationImpl("org123", "parentOrg", null);
when(organizationDao.getById(anyString())).thenReturn(parentOrganization);
final Organization toCreate = new OrganizationImpl(null, "orgName", parentOrganization.getId());
manager.create(toCreate);
verify(organizationDao).create(organizationCaptor.capture());
final OrganizationImpl createdOrganization = organizationCaptor.getValue();
assertEquals(createdOrganization.getName(), toCreate.getName());
assertEquals(
createdOrganization.getQualifiedName(),
parentOrganization.getQualifiedName() + "/" + toCreate.getName());
assertEquals(createdOrganization.getParent(), toCreate.getParent());
verify(memberDao)
.store(
new MemberImpl(USER_ID, createdOrganization.getId(), OrganizationDomain.getActions()));
}
@Test
public void shouldGenerateIdentifierWhenCreatingOrganization() throws Exception {
final Organization organization =
DtoFactory.newDto(OrganizationDto.class).withName("newOrg").withId("identifier");
manager.create(organization);
verify(organizationDao).create(organizationCaptor.capture());
final String id = organizationCaptor.getValue().getId();
assertNotNull(id);
assertNotEquals(id, "identifier");
}
@Test(expectedExceptions = ConflictException.class)
public void shouldThrowConflictExceptionOnCreationIfOrganizationNameIsReserved()
throws Exception {
final Organization organization =
DtoFactory.newDto(OrganizationDto.class).withName("reserved").withParent(null);
manager.create(organization);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenCreatingNullableOrganization() throws Exception {
manager.create(null);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenUpdatingOrganizationWithNullEntity() throws Exception {
manager.update("organizationId", null);
}
@Test
public void shouldUpdateOrganizationAndIgnoreNewIdAndParentFields() throws Exception {
final OrganizationImpl existing = new OrganizationImpl("org123", "oldName", "parent123");
final OrganizationImpl expectedExistingToUpdate = new OrganizationImpl(existing);
expectedExistingToUpdate.setQualifiedName("newName");
final OrganizationImpl suborganization =
new OrganizationImpl("org321", "oldName/suborgName", "org123");
final OrganizationImpl expectedSuborganizationToUpdate = new OrganizationImpl(suborganization);
expectedSuborganizationToUpdate.setQualifiedName(
expectedExistingToUpdate.getQualifiedName() + "/" + suborganization.getName());
when(organizationDao.getById(any())).thenReturn(existing);
doReturn(new Page<>(singletonList(suborganization), 0, 1, 1))
.when(organizationDao)
.getSuborganizations(anyString(), anyInt(), anyLong());
final OrganizationImpl update = new OrganizationImpl("newId", "newName", "newParentId");
final Organization updated = manager.update("organizationId", update);
verify(organizationDao).getById("organizationId");
verify(organizationDao, times(2)).update(organizationCaptor.capture());
List<OrganizationImpl> updatedOrganizations = organizationCaptor.getAllValues();
assertEquals(updatedOrganizations.get(0), expectedExistingToUpdate);
assertEquals(updatedOrganizations.get(1), expectedSuborganizationToUpdate);
verify(organizationDao).getSuborganizations(eq("oldName"), anyInt(), anyLong());
assertEquals(updated, expectedExistingToUpdate);
}
@Test(expectedExceptions = ConflictException.class)
public void shouldThrowConflictExceptionOnUpdatingIfOrganizationNameIsReserved()
throws Exception {
when(organizationDao.getById("id")).thenReturn(new OrganizationImpl("id", "oldName", null));
manager.update("id", new OrganizationImpl("id", "reserved", null));
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenUpdatingOrganizationByNullId() throws Exception {
manager.update(null, new OrganizationImpl());
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenRemovingOrganizationByNullId() throws Exception {
manager.remove(null);
}
@Test
public void shouldRemoveOrganization() throws Exception {
doNothing().when(manager).removeSuborganizations(anyString());
final List<Member> members = Collections.singletonList(mock(Member.class));
doReturn(members).when(manager).removeMembers(anyString());
OrganizationImpl toRemove = new OrganizationImpl("org123", "toRemove", null);
when(organizationDao.getById(anyString())).thenReturn(toRemove);
manager.remove(toRemove.getId());
verify(organizationDao).remove(toRemove.getId());
verify(manager).removeMembers(eq(toRemove.getId()));
verify(manager).removeSuborganizations(eq(toRemove.getId()));
}
@Test
public void shouldRemoveMembersByOrganizationId() throws Exception {
MemberImpl member1 = new MemberImpl("user1", "org1", singletonList("read"));
MemberImpl member2 = new MemberImpl("user2", "org1", singletonList("read"));
doReturn(new Page<>(singletonList(member1), 0, 1, 2))
.doReturn(new Page<>(singletonList(member2), 1, 1, 2))
.when(memberDao)
.getMembers(anyString(), anyInt(), anyLong());
manager.removeMembers("org1");
verify(memberDao, times(2)).getMembers("org1", 100, 0);
verify(memberDao).remove("user1", "org1");
verify(memberDao).remove("user2", "org1");
}
@Test
public void shouldRemoveSuborganizationsByParentOrganizationId() throws Exception {
doNothing().when(manager).remove(any());
OrganizationImpl subOrg1 = new OrganizationImpl("subOrg1", "subOrg1", "org1");
OrganizationImpl subOrg2 = new OrganizationImpl("subOrg2", "subOrg2", "org1");
doReturn(new Page<>(singletonList(subOrg1), 0, 1, 2))
.doReturn(new Page<>(singletonList(subOrg2), 1, 1, 2))
.when(organizationDao)
.getByParent(anyString(), anyInt(), anyLong());
manager.removeSuborganizations("org1");
verify(organizationDao, times(2)).getByParent("org1", 100, 0);
verify(manager).remove("subOrg1");
verify(manager).remove("subOrg2");
}
@Test
public void shouldNotTryToRemoveOrganizationWhenItIsNotExistRemoveOrganization()
throws Exception {
when(organizationDao.getById(anyString())).thenThrow(new NotFoundException("not found"));
manager.remove("id");
verify(organizationDao, never()).remove(anyString());
verify(eventService, never()).publish(any());
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenGettingOrganizationByNullName() throws Exception {
manager.getById(null);
}
@Test
public void shouldGetOrganizationByName() throws Exception {
final OrganizationImpl toFetch = new OrganizationImpl("org123", "toFetchOrg", "org321");
when(organizationDao.getByName(eq("org123"))).thenReturn(toFetch);
final Organization fetched = manager.getByName("org123");
assertEquals(fetched, toFetch);
verify(organizationDao).getByName("org123");
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenGettingOrganizationByNullId() throws Exception {
manager.getById(null);
}
@Test
public void shouldGetOrganizationById() throws Exception {
final OrganizationImpl toFetch = new OrganizationImpl("org123", "toFetchOrg", "org321");
when(organizationDao.getById(eq("org123"))).thenReturn(toFetch);
final Organization fetched = manager.getById("org123");
assertEquals(fetched, toFetch);
verify(organizationDao).getById("org123");
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenGettingSuborganizationsByNullParent() throws Exception {
manager.getByParent(null, 30, 0);
}
@Test
public void shouldGetOrganizationsByParent() throws Exception {
final OrganizationImpl toFetch = new OrganizationImpl("org321", "toFetchOrg", "org123");
when(organizationDao.getByParent(eq("org123"), anyInt(), anyLong()))
.thenReturn(new Page<>(singletonList(toFetch), 0, 1, 1));
final Page<? extends Organization> organizations = manager.getByParent("org123", 30, 0);
assertEquals(organizations.getItemsCount(), 1);
assertEquals(organizations.getItems().get(0), toFetch);
verify(organizationDao).getByParent("org123", 30, 0);
}
@Test
public void shouldGetSuborganizations() throws Exception {
final OrganizationImpl toFetch = new OrganizationImpl("org321", "parent/toFetchOrg", "org123");
when(organizationDao.getSuborganizations(eq("parent"), anyInt(), anyLong()))
.thenReturn(new Page<>(singletonList(toFetch), 0, 1, 1));
final Page<? extends Organization> organizations = manager.getSuborganizations("parent", 30, 0);
assertEquals(organizations.getItemsCount(), 1);
assertEquals(organizations.getItems().get(0), toFetch);
verify(organizationDao).getSuborganizations("parent", 30, 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeOnGettingSuborganizationsByNullParentQualifiedName() throws Exception {
manager.getSuborganizations(null, 30, 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void shouldThrowNpeWhenGettingOrganizationsByNullUserId() throws Exception {
manager.getByMember(null, 30, 0);
}
@Test
public void shouldGetOrganizationsByMember() throws Exception {
final OrganizationImpl toFetch = new OrganizationImpl("org123", "toFetchOrg", "org321");
when(memberDao.getOrganizations(eq("org123"), anyInt(), anyLong()))
.thenReturn(new Page<>(singletonList(toFetch), 0, 1, 1));
final Page<? extends Organization> organizations = manager.getByMember("org123", 30, 0);
assertEquals(organizations.getItemsCount(), 1);
assertEquals(organizations.getItems().get(0), toFetch);
verify(memberDao).getOrganizations("org123", 30, 0);
}
}

View File

@ -1,345 +0,0 @@
/*
* 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.organization.api;
import static io.restassured.RestAssured.given;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
import static org.everrest.assured.JettyHttpServer.SECURE_PATH;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import io.restassured.response.Response;
import java.util.HashSet;
import java.util.List;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
import org.eclipse.che.multiuser.organization.shared.model.Organization;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Tests for {@link org.eclipse.che.multiuser.organization.api.OrganizationService}.
*
* @author Sergii Leschenko
*/
@Listeners({EverrestJetty.class, MockitoTestNGListener.class})
public class OrganizationServiceTest {
private static final String CURRENT_USER_ID = "user123";
@SuppressWarnings("unused") // is declared for deploying by everrest-assured
private ApiExceptionMapper mapper;
@SuppressWarnings("unused") // is declared for deploying by everrest-assured
private EnvironmentFilter filter;
@SuppressWarnings("unused") // is declared for deploying by everrest-assured
private CheJsonProvider jsonProvider = new CheJsonProvider(new HashSet<>());
@Mock private OrganizationManager orgManager;
@Mock private OrganizationLinksInjector linksInjector;
@Mock private OrganizationValidator validator;
@InjectMocks private OrganizationService service;
@BeforeMethod
public void setUp() throws Exception {
lenient()
.when(linksInjector.injectLinks(any(), any()))
.thenAnswer(invocation -> invocation.getArguments()[0]);
}
@Test
public void shouldCreateOrganization() throws Exception {
when(orgManager.create(any()))
.thenAnswer(
invocationOnMock ->
new OrganizationImpl((Organization) invocationOnMock.getArguments()[0]));
final OrganizationDto toCreate = createOrganization();
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(toCreate)
.when()
.post(SECURE_PATH + "/organization");
assertEquals(response.statusCode(), 201);
final OrganizationDto createdOrganization = unwrapDto(response, OrganizationDto.class);
assertEquals(createdOrganization, toCreate);
verify(linksInjector).injectLinks(any(), any());
verify(orgManager).create(eq(toCreate));
}
@Test
public void shouldThrowBadRequestWhenCreatingNonValidOrganization() throws Exception {
doThrow(new BadRequestException("non valid organization"))
.when(validator)
.checkOrganization(any());
final OrganizationDto toCreate = createOrganization();
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(toCreate)
.when()
.post(SECURE_PATH + "/organization");
assertEquals(response.statusCode(), 400);
final ServiceError error = unwrapDto(response, ServiceError.class);
assertEquals(error.getMessage(), "non valid organization");
verify(validator).checkOrganization(toCreate);
}
@Test
public void shouldUpdateOrganization() throws Exception {
when(orgManager.update(anyString(), any()))
.thenAnswer(
invocationOnMock ->
new OrganizationImpl((Organization) invocationOnMock.getArguments()[1]));
final OrganizationDto toUpdate = createOrganization();
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(toUpdate)
.when()
.post(SECURE_PATH + "/organization/organization123");
assertEquals(response.statusCode(), 200);
final OrganizationDto createdOrganization = unwrapDto(response, OrganizationDto.class);
assertEquals(createdOrganization, toUpdate);
verify(linksInjector).injectLinks(any(), any());
verify(orgManager).update(eq("organization123"), eq(toUpdate));
}
@Test
public void shouldThrowBadRequestWhenUpdatingNonValidOrganization() throws Exception {
doThrow(new BadRequestException("non valid organization"))
.when(validator)
.checkOrganization(any());
final OrganizationDto toUpdate = createOrganization();
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(toUpdate)
.when()
.post(SECURE_PATH + "/organization/organization123");
assertEquals(response.statusCode(), 400);
final ServiceError error = unwrapDto(response, ServiceError.class);
assertEquals(error.getMessage(), "non valid organization");
verify(validator).checkOrganization(toUpdate);
}
@Test
public void shouldRemoveOrganization() throws Exception {
Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.delete(SECURE_PATH + "/organization/organization123");
assertEquals(response.statusCode(), 204);
verify(orgManager).remove(eq("organization123"));
}
@Test
public void shouldGetOrganizationById() throws Exception {
final OrganizationDto toFetch = createOrganization();
when(orgManager.getById(eq("organization123"))).thenReturn(toFetch);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/organization/organization123");
assertEquals(response.statusCode(), 200);
final OrganizationDto fetchedOrganization = unwrapDto(response, OrganizationDto.class);
assertEquals(fetchedOrganization, toFetch);
verify(orgManager).getById(eq("organization123"));
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldFindOrganizationByName() throws Exception {
final OrganizationDto toFetch = createOrganization();
when(orgManager.getByName(eq("subOrg"))).thenReturn(toFetch);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.get(SECURE_PATH + "/organization/find?name=subOrg");
assertEquals(response.statusCode(), 200);
final OrganizationDto fetchedOrganization = unwrapDto(response, OrganizationDto.class);
assertEquals(fetchedOrganization, toFetch);
verify(orgManager).getByName(eq("subOrg"));
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldThrowBadRequestExceptionWhenFindingOrganizationWithoutName() throws Exception {
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/organization/find")
.then()
.assertThat()
.statusCode(400);
}
@Test
public void shouldGetChildOrganizations() throws Exception {
final OrganizationDto toFetch = createOrganization();
doReturn(new Page<>(singletonList(toFetch), 0, 1, 1))
.when(orgManager)
.getByParent(anyString(), anyInt(), anyLong());
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/organization/parentOrg123/organizations?skipCount=0&maxItems=1");
assertEquals(response.statusCode(), 200);
final List<OrganizationDto> organizationDtos = unwrapDtoList(response, OrganizationDto.class);
assertEquals(organizationDtos.size(), 1);
assertEquals(organizationDtos.get(0), toFetch);
verify(orgManager).getByParent("parentOrg123", 1, 0);
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldGetOrganizationsByCurrentUserIfParameterIsNotSpecified() throws Exception {
final OrganizationDto toFetch = createOrganization();
doReturn(new Page<>(singletonList(toFetch), 0, 1, 1))
.when(orgManager)
.getByMember(anyString(), anyInt(), anyInt());
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/organization?skipCount=0&maxItems=1");
assertEquals(response.statusCode(), 200);
final List<OrganizationDto> organizationDtos = unwrapDtoList(response, OrganizationDto.class);
assertEquals(organizationDtos.size(), 1);
assertEquals(organizationDtos.get(0), toFetch);
verify(orgManager).getByMember(CURRENT_USER_ID, 1, 0);
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldGetOrganizationsBySpecifiedUser() throws Exception {
final OrganizationDto toFetch = createOrganization();
doReturn(new Page<>(singletonList(toFetch), 0, 1, 1))
.when(orgManager)
.getByMember(anyString(), anyInt(), anyInt());
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/organization?user=user789&skipCount=0&maxItems=1");
assertEquals(response.statusCode(), 200);
final List<OrganizationDto> organizationDtos = unwrapDtoList(response, OrganizationDto.class);
assertEquals(organizationDtos.size(), 1);
assertEquals(organizationDtos.get(0), toFetch);
verify(orgManager).getByMember("user789", 1, 0);
verify(linksInjector).injectLinks(any(), any());
}
private static <T> T unwrapDto(Response response, Class<T> dtoClass) {
return DtoFactory.getInstance().createDtoFromJson(response.body().print(), dtoClass);
}
private static <T> List<T> unwrapDtoList(Response response, Class<T> dtoClass) {
return DtoFactory.getInstance()
.createListDtoFromJson(response.body().print(), dtoClass)
.stream()
.collect(toList());
}
private OrganizationDto createOrganization() {
return DtoFactory.newDto(OrganizationDto.class)
.withId("organization123")
.withName("subOrg")
.withQualifiedName("parentOrg/subOrg")
.withParent("parentOrg123");
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
@Override
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent()
.setSubject(new SubjectImpl("userName", CURRENT_USER_ID, "token", false));
}
}
}

View File

@ -1,499 +0,0 @@
/*
* 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.organization.api.permissions;
import static io.restassured.RestAssured.given;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.DELETE;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.DOMAIN_ID;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.MANAGE_SUBORGANIZATIONS;
import static org.eclipse.che.multiuser.organization.api.permissions.OrganizationDomain.UPDATE;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
import static org.everrest.assured.JettyHttpServer.SECURE_PATH;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
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.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.multiuser.api.permission.server.SuperPrivilegesChecker;
import org.eclipse.che.multiuser.organization.api.OrganizationManager;
import org.eclipse.che.multiuser.organization.api.OrganizationService;
import org.eclipse.che.multiuser.organization.shared.dto.OrganizationDto;
import org.eclipse.che.multiuser.organization.spi.impl.OrganizationImpl;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.everrest.core.resource.GenericResourceMethod;
import org.mockito.InjectMocks;
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;
/**
* Tests for {@link
* org.eclipse.che.multiuser.organization.api.permissions.OrganizationPermissionsFilter}
*
* @author Sergii Leschenko
*/
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class OrganizationPermissionsFilterTest {
@SuppressWarnings("unused")
private static final ApiExceptionMapper MAPPER = new ApiExceptionMapper();
@SuppressWarnings("unused")
private static final EnvironmentFilter FILTER = new EnvironmentFilter();
private static final String USER_ID = "user123";
@Mock private static Subject subject;
@Mock private OrganizationService service;
@Mock private OrganizationManager manager;
@Mock private SuperPrivilegesChecker superPrivilegesChecker;
@InjectMocks private OrganizationPermissionsFilter permissionsFilter;
@BeforeMethod
public void setUp() throws Exception {
lenient().when(subject.getUserId()).thenReturn(USER_ID);
lenient()
.when(manager.getById(anyString()))
.thenReturn(new OrganizationImpl("organization123", "test", null));
}
@Test
public void shouldTestThatAllPublicMethodsAreCoveredByPermissionsFilter() throws Exception {
// given
final List<String> collect =
Stream.of(OrganizationService.class.getDeclaredMethods())
.filter(method -> Modifier.isPublic(method.getModifiers()))
.map(Method::getName)
.collect(Collectors.toList());
// then
assertEquals(collect.size(), 7);
assertTrue(collect.contains(OrganizationPermissionsFilter.CREATE_METHOD));
assertTrue(collect.contains(OrganizationPermissionsFilter.UPDATE_METHOD));
assertTrue(collect.contains(OrganizationPermissionsFilter.REMOVE_METHOD));
assertTrue(collect.contains(OrganizationPermissionsFilter.GET_BY_PARENT_METHOD));
assertTrue(collect.contains(OrganizationPermissionsFilter.GET_ORGANIZATIONS_METHOD));
assertTrue(collect.contains(OrganizationPermissionsFilter.GET_BY_ID_METHOD));
assertTrue(collect.contains(OrganizationPermissionsFilter.FIND_METHOD));
}
@Test
public void shouldNotCheckPermissionsOnGettingOrganizationById() throws Exception {
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/organization/organization123");
verify(service).getById("organization123");
verifyNoMoreInteractions(subject);
}
@Test
public void shouldNotCheckPermissionsOnGettingOrganizationByName() throws Exception {
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/organization/find?name=test");
verify(service).find("test");
verifyNoMoreInteractions(subject);
}
@Test
public void shouldNotCheckPermissionsOnOrganizationsFetchingIfUserIdIsNotSpecified()
throws Exception {
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.expect()
.statusCode(204)
.when()
.get(SECURE_PATH + "/organization");
verify(service).getOrganizations(eq(null), anyInt(), anyInt());
verify(subject, never()).hasPermission(anyString(), anyString(), anyString());
}
@Test
public void shouldNotCheckPermissionsOnOrganizationsFetchingIfUserSpecifiesHisOwnId()
throws Exception {
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.expect()
.statusCode(204)
.when()
.get(SECURE_PATH + "/organization?user=" + USER_ID);
verify(service).getOrganizations(eq(USER_ID), anyInt(), anyInt());
verify(subject, never()).hasPermission(anyString(), anyString(), anyString());
}
@Test
public void shouldCheckSuperPrivilegesOnOrganizationsFetchingIfUserSpecifiesForeignId()
throws Exception {
when(superPrivilegesChecker.hasSuperPrivileges()).thenReturn(true);
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.expect()
.statusCode(204)
.when()
.get(SECURE_PATH + "/organization?user=user321");
verify(service).getOrganizations(eq("user321"), anyInt(), anyInt());
verify(superPrivilegesChecker).hasSuperPrivileges();
}
@Test
public void
shouldThrowForbiddenExceptionOnOrganizationsFetchingIfUserSpecifiesForeignIdAndDoesNotHaveSuperPrivileges()
throws Exception {
when(superPrivilegesChecker.hasSuperPrivileges()).thenReturn(false);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.expect()
.statusCode(403)
.when()
.get(SECURE_PATH + "/organization?user=user321");
assertEquals(unwrapError(response), "The user is able to specify only his own id");
verify(superPrivilegesChecker).hasSuperPrivileges();
verifyNoMoreInteractions(service);
}
@Test
public void shouldCheckPermissionsOnOrganizationUpdating() throws Exception {
when(subject.hasPermission(DOMAIN_ID, "organization123", UPDATE)).thenReturn(true);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.post(SECURE_PATH + "/organization/organization123");
assertEquals(response.getStatusCode(), 204);
verify(service).update(eq("organization123"), any());
verify(subject).hasPermission(DOMAIN_ID, "organization123", UPDATE);
verify(superPrivilegesChecker, never()).hasSuperPrivileges();
verifyNoMoreInteractions(subject);
}
@Test
public void shouldCheckPermissionsOnParentOrgLevelOnChildOrganizationUpdating() throws Exception {
when(manager.getById(anyString()))
.thenReturn(new OrganizationImpl("organization123", "test", "parent123"));
when(subject.hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS)).thenReturn(true);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.post(SECURE_PATH + "/organization/organization123");
assertEquals(response.getStatusCode(), 204);
verify(service).update(eq("organization123"), any());
verify(subject).hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS);
verify(superPrivilegesChecker, never()).hasSuperPrivileges();
verifyNoMoreInteractions(subject);
}
@Test
public void
shouldCheckPermissionsOnChildOrganizationUpdatingWhenUserDoesNotHavePermissionsOnParentOrgLevel()
throws Exception {
when(manager.getById(anyString()))
.thenReturn(new OrganizationImpl("organization123", "test", "parent123"));
doReturn(false).when(subject).hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS);
doReturn(true).when(subject).hasPermission(DOMAIN_ID, "organization123", UPDATE);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.post(SECURE_PATH + "/organization/organization123");
assertEquals(response.getStatusCode(), 204);
verify(service).update(eq("organization123"), any());
verify(subject).hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS);
verify(subject).hasPermission(DOMAIN_ID, "organization123", UPDATE);
}
@Test
public void shouldCheckPermissionsOnOrganizationRemoving() throws Exception {
when(subject.hasPermission(DOMAIN_ID, "organization123", DELETE)).thenReturn(true);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.delete(SECURE_PATH + "/organization/organization123");
assertEquals(response.getStatusCode(), 204);
verify(service).remove(eq("organization123"));
verify(subject).hasPermission(DOMAIN_ID, "organization123", DELETE);
verify(superPrivilegesChecker, never()).hasSuperPrivileges();
verifyNoMoreInteractions(subject);
}
@Test
public void shouldCheckPermissionsOnParentOrgLevelOnChildOrganizationRemoving() throws Exception {
when(manager.getById(anyString()))
.thenReturn(new OrganizationImpl("organization123", "test", "parent123"));
when(subject.hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS)).thenReturn(true);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.delete(SECURE_PATH + "/organization/organization123");
assertEquals(response.getStatusCode(), 204);
verify(service).remove(eq("organization123"));
verify(subject).hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS);
verify(superPrivilegesChecker, never()).hasSuperPrivileges();
verifyNoMoreInteractions(subject);
}
@Test
public void
shouldCheckPermissionsOnChildOrganizationRemovingWhenUserDoesNotHavePermissionsOnParentOrgLevel()
throws Exception {
when(manager.getById(anyString()))
.thenReturn(new OrganizationImpl("organization123", "test", "parent123"));
doReturn(false).when(subject).hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS);
doReturn(true).when(subject).hasPermission(DOMAIN_ID, "organization123", DELETE);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.delete(SECURE_PATH + "/organization/organization123");
assertEquals(response.getStatusCode(), 204);
verify(service).remove(eq("organization123"));
verify(subject).hasPermission(DOMAIN_ID, "parent123", MANAGE_SUBORGANIZATIONS);
verify(subject).hasPermission(DOMAIN_ID, "organization123", DELETE);
verify(superPrivilegesChecker, never()).hasSuperPrivileges();
verifyNoMoreInteractions(subject);
}
@Test
public void shouldNotCheckPermissionsOnRootOrganizationCreation() throws Exception {
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.body(DtoFactory.newDto(OrganizationDto.class).withParent(null))
.post(SECURE_PATH + "/organization");
assertEquals(response.getStatusCode(), 204);
verify(service).create(any());
verifyNoMoreInteractions(subject);
}
@Test
public void shouldCheckPermissionsOnChildOrganizationCreation() throws Exception {
when(subject.hasPermission(DOMAIN_ID, "parent-org", MANAGE_SUBORGANIZATIONS)).thenReturn(true);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.body(DtoFactory.newDto(OrganizationDto.class).withParent("parent-org"))
.post(SECURE_PATH + "/organization");
assertEquals(response.getStatusCode(), 204);
verify(service).create(any());
verify(subject).hasPermission(DOMAIN_ID, "parent-org", MANAGE_SUBORGANIZATIONS);
}
@Test
public void
shouldThrowForbiddenExceptionOnChildOrganizationCreationIfUserDoesNotHaveCorrespondingPermission()
throws Exception {
when(subject.hasPermission(DOMAIN_ID, "parent-org", MANAGE_SUBORGANIZATIONS)).thenReturn(false);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.body(DtoFactory.newDto(OrganizationDto.class).withParent("parent-org"))
.post(SECURE_PATH + "/organization");
assertEquals(response.getStatusCode(), 403);
verifyNoMoreInteractions(service);
verify(subject).hasPermission(DOMAIN_ID, "parent-org", MANAGE_SUBORGANIZATIONS);
}
@Test(
expectedExceptions = ForbiddenException.class,
expectedExceptionsMessageRegExp =
"The user does not have permission to perform this operation")
public void shouldThrowForbiddenExceptionWhenRequestedUnknownMethod() throws Exception {
final GenericResourceMethod mock = mock(GenericResourceMethod.class);
Method injectLinks = OrganizationService.class.getMethod("getServiceDescriptor");
when(mock.getMethod()).thenReturn(injectLinks);
permissionsFilter.filter(mock, new Object[] {});
}
@Test(dataProvider = "coveredPaths")
public void shouldThrowForbiddenExceptionWhenUserDoesNotHavePermissionsForPerformOperation(
String path, String method, String action) throws Exception {
when(subject.hasPermission(anyString(), anyString(), anyString())).thenReturn(false);
Response response =
request(
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when(),
SECURE_PATH + path,
method);
assertEquals(response.getStatusCode(), 403);
assertEquals(
unwrapError(response),
"The user does not have permission to "
+ action
+ " organization with id 'organization123'");
verifyNoMoreInteractions(service);
}
@Test(dataProvider = "coveredPaths")
public void shouldThrowNotFoundWhenUserRequestsNonExistedOrganization(
String path, String method, String ignored) throws Exception {
when(manager.getById(anyString()))
.thenThrow(new NotFoundException("Organization was not found"));
Response response =
request(
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when(),
SECURE_PATH + path,
method);
assertEquals(response.getStatusCode(), 404);
assertEquals(unwrapError(response), "Organization was not found");
verifyNoMoreInteractions(service);
}
@DataProvider(name = "coveredPaths")
public Object[][] pathsProvider() {
return new Object[][] {
{"/organization/organization123", "post", UPDATE},
{"/organization/organization123", "delete", DELETE},
{"/organization/organization123/organizations", "get", MANAGE_SUBORGANIZATIONS}
};
}
private Response request(RequestSpecification request, String path, String method) {
switch (method) {
case "post":
return request.post(path);
case "get":
return request.get(path);
case "delete":
return request.delete(path);
case "put":
return request.put(path);
}
throw new RuntimeException("Unsupported method");
}
private static String unwrapError(Response response) {
return unwrapDto(response, ServiceError.class).getMessage();
}
private static <T> T unwrapDto(Response response, Class<T> dtoClass) {
return DtoFactory.getInstance().createDtoFromJson(response.body().print(), dtoClass);
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
@Override
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(subject);
}
}
}

View File

@ -1,156 +0,0 @@
/*
* Copyright (c) 2012-2018 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.organization.api.permissions;
import static org.eclipse.che.multiuser.organization.api.listener.OrganizationEventsWebsocketBroadcaster.ORGANIZATION_CHANGED_METHOD_NAME;
import static org.eclipse.che.multiuser.organization.api.listener.OrganizationEventsWebsocketBroadcaster.ORGANIZATION_MEMBERSHIP_METHOD_NAME;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
import java.util.Collections;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.multiuser.api.permission.server.PermissionsManager;
import org.eclipse.che.multiuser.api.permission.server.jsonrpc.RemoteSubscriptionPermissionManager;
import org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationRemoteSubscriptionPermissionsChecks.MembershipsChangedSubscriptionCheck;
import org.eclipse.che.multiuser.organization.api.permissions.OrganizationRemoteSubscriptionPermissionsChecks.OrganizationChangedSubscriptionCheck;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* Tests {@link OrganizationRemoteSubscriptionPermissionsChecks}.
*
* @author Sergii Leshchenko
*/
@Listeners(MockitoTestNGListener.class)
public class OrganizationRemoteSubscriptionPermissionsChecksTest {
@Mock private Subject subject;
@Mock private PermissionsManager permissionsManager;
@Mock private RemoteSubscriptionPermissionManager permissionManager;
@InjectMocks private OrganizationRemoteSubscriptionPermissionsChecks permissionsChecks;
@BeforeMethod
public void setUp() {
EnvironmentContext.getCurrent().setSubject(subject);
}
@AfterMethod
public void tearDown() {
EnvironmentContext.reset();
}
@Test
public void shouldRegisterChecks() {
// when
permissionsChecks.register(permissionManager);
// then
verify(permissionManager)
.registerCheck(
any(OrganizationChangedSubscriptionCheck.class), eq(ORGANIZATION_CHANGED_METHOD_NAME));
verify(permissionManager)
.registerCheck(
any(MembershipsChangedSubscriptionCheck.class),
eq(ORGANIZATION_MEMBERSHIP_METHOD_NAME));
}
@Test(
expectedExceptions = ForbiddenException.class,
expectedExceptionsMessageRegExp = "User id must be specified in scope")
public void shouldThrowExceptionIfUserIdIsMissing() throws Exception {
// given
MembershipsChangedSubscriptionCheck check = new MembershipsChangedSubscriptionCheck();
when(subject.getUserId()).thenReturn("user2");
// when
check.check(ORGANIZATION_MEMBERSHIP_METHOD_NAME, Collections.emptyMap());
}
@Test(
expectedExceptions = ForbiddenException.class,
expectedExceptionsMessageRegExp = "It is only allowed to listen to own memberships changes")
public void shouldThrowExceptionIfUserTryToListenToForeignMemberships() throws Exception {
// given
MembershipsChangedSubscriptionCheck check = new MembershipsChangedSubscriptionCheck();
when(subject.getUserId()).thenReturn("user2");
// when
check.check(ORGANIZATION_MEMBERSHIP_METHOD_NAME, ImmutableMap.of("userId", "user1"));
}
@Test
public void shouldDoNothingIfUserTryToListenToOwnMemberships() throws Exception {
// given
MembershipsChangedSubscriptionCheck check = new MembershipsChangedSubscriptionCheck();
when(subject.getUserId()).thenReturn("user1");
// when
check.check(ORGANIZATION_MEMBERSHIP_METHOD_NAME, ImmutableMap.of("userId", "user1"));
}
@Test(
expectedExceptions = ForbiddenException.class,
expectedExceptionsMessageRegExp = "Organization id must be specified in scope")
public void shouldThrowExceptionIfOrganizationIdIsMissing() throws Exception {
// given
OrganizationChangedSubscriptionCheck check =
new OrganizationChangedSubscriptionCheck(permissionsManager);
// when
check.check(ORGANIZATION_MEMBERSHIP_METHOD_NAME, Collections.emptyMap());
}
@Test(
expectedExceptions = ForbiddenException.class,
expectedExceptionsMessageRegExp =
"User doesn't have any permissions for the specified organization")
public void shouldThrowExceptionIfUserDoesNotHaveAnyPermissionsToRequestedOrganization()
throws Exception {
// given
OrganizationChangedSubscriptionCheck check =
new OrganizationChangedSubscriptionCheck(permissionsManager);
when(subject.getUserId()).thenReturn("user1");
when(permissionsManager.get("user1", OrganizationDomain.DOMAIN_ID, "org123"))
.thenThrow(new NotFoundException(""));
// when
check.check(ORGANIZATION_MEMBERSHIP_METHOD_NAME, ImmutableMap.of("organizationId", "org123"));
}
@Test
public void shouldDoNothingIfUserTryToListenEventsOfOrganizationWhereHeHasPermissions()
throws Exception {
// given
OrganizationChangedSubscriptionCheck check =
new OrganizationChangedSubscriptionCheck(permissionsManager);
when(subject.getUserId()).thenReturn("user1");
when(permissionsManager.get("user1", OrganizationDomain.DOMAIN_ID, "org123"))
.thenReturn(mock(AbstractPermissions.class));
// when
check.check(ORGANIZATION_MEMBERSHIP_METHOD_NAME, ImmutableMap.of("organizationId", "org123"));
}
}

Some files were not shown because too many files have changed in this diff Show More