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>
<artifactId>infrastructure-permission</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-authentication-commons</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-authorization</artifactId>
</dependency>
<dependency>
<groupId>org.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>
<groupId>org.eclipse.persistence</groupId>
<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 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.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.SigningKeyResolver;
import java.util.HashMap;
import java.util.Map;
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.devfile.DevfileModule;
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.MachineNameProvisioner;
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.WorkspaceNameEnvVarProvider;
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.commons.observability.deploy.ExecutorWrapperModule;
import org.eclipse.che.core.tracing.metrics.TracingMetricsModule;
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.PasswordEncryptor;
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.environment.KubernetesEnvironment;
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.SecureServerExposerFactory;
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.OpenShiftInfrastructure;
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.persistence.config.PersistenceUnitProperties;
@ -349,60 +338,19 @@ public class WsMasterModule extends AbstractModule {
PersistenceUnitProperties.EXCEPTION_HANDLER_CLASS,
"org.eclipse.che.core.db.postgresql.jpa.eclipselink.PostgreSqlExceptionHandler");
install(
new org.eclipse.che.multiuser.permission.workspace.server.WorkspaceApiPermissionsModule());
install(
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());
bind(RequestTokenExtractor.class).to(HeaderRequestTokenExtractor.class);
bind(ProfileDao.class).to(JpaProfileDao.class);
bind(OAuthAPI.class).to(EmbeddedOAuthAPI.class).asEagerSingleton();
// Permission filters
bind(org.eclipse.che.multiuser.permission.system.SystemServicePermissionsFilter.class);
bind(org.eclipse.che.multiuser.permission.system.JvmServicePermissionsFilter.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());
install(new WorkspaceJpaModule());
bind(TokenValidator.class).to(NotImplementedTokenValidator.class);
bind(MachineTokenProvider.class).to(MachineTokenProvider.EmptyMachineTokenProvider.class);
// User and profile - use profile from keycloak and other stuff is JPA
bind(PasswordEncryptor.class).to(PBKDF2PasswordEncryptor.class);
bind(UserDao.class).to(JpaUserDao.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);
}

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
* available under the terms of the Eclipse Public License 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.inject.ConfigurationException;
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.multiuser.oauth.OidcTokenInitializationFilter;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftInfrastructure;
import org.eclipse.che.workspace.infrastructure.openshift.multiuser.oauth.OpenshiftTokenInitializationFilter;
import org.everrest.guice.servlet.GuiceEverrestServlet;
@ -53,7 +51,7 @@ public class WsMasterServletModule extends ServletModule {
configureNativeUserMode();
} else {
LOG.info("Running in classic multi-user mode ...");
configureMultiUserMode();
// configureMultiUserMode();
}
if (Boolean.valueOf(System.getenv("CHE_METRICS_ENABLED"))) {
@ -71,10 +69,10 @@ public class WsMasterServletModule extends ServletModule {
}
}
private void configureMultiUserMode() {
filterRegex(".*").through(MachineLoginFilter.class);
install(new KeycloakServletModule());
}
// private void configureMultiUserMode() {
// filterRegex(".*").through(MachineLoginFilter.class);
// install(new KeycloakServletModule());
// }
private void configureNativeUserMode() {
final String infrastructure = System.getenv("CHE_INFRASTRUCTURE_ACTIVE");

View File

@ -1,7 +1,7 @@
<?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
available under the terms of the Eclipse Public License 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>
<listener>
<listener-class>org.eclipse.che.multiuser.api.authentication.commons.DestroySessionListener</listener-class>
</listener>
<!-- <listener>-->
<!-- <listener-class>org.eclipse.che.multiuser.api.authentication.commons.DestroySessionListener</listener-class>-->
<!-- </listener>-->
</web-app>

View File

@ -38,14 +38,6 @@
<groupId>org.eclipse.che.infrastructure</groupId>
<artifactId>infrastructure-kubernetes</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-permission-workspace</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<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
* available under the terms of the Eclipse Public License 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_STATUS_CHANGED_METHOD;
import javax.inject.Inject;
import javax.inject.Singleton;
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.multiuser.api.permission.server.jsonrpc.JsonRpcPermissionsFilterAdapter;
import org.eclipse.che.multiuser.permission.workspace.server.WorkspaceDomain;
// import org.eclipse.che.multiuser.api.permission.server.jsonrpc.JsonRpcPermissionsFilterAdapter;
// import org.eclipse.che.multiuser.permission.workspace.server.WorkspaceDomain;
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
*/
@Singleton
public class BrokerServicePermissionFilter extends JsonRpcPermissionsFilterAdapter {
@Inject
public void register(RequestHandlerManager requestHandlerManager) {
requestHandlerManager.registerMethodInvokerFilter(
this, BROKER_STATUS_CHANGED_METHOD, BROKER_RESULT_METHOD);
}
public class BrokerServicePermissionFilter {
// @Inject
// public void register(RequestHandlerManager requestHandlerManager) {
// requestHandlerManager.registerMethodInvokerFilter(
// this, BROKER_STATUS_CHANGED_METHOD, BROKER_RESULT_METHOD);
// }
@Override
public void doAccept(String method, Object... params) throws ForbiddenException {
String workspaceId;
switch (method) {
@ -56,10 +53,10 @@ public class BrokerServicePermissionFilter extends JsonRpcPermissionsFilterAdapt
}
Subject currentSubject = EnvironmentContext.getCurrent().getSubject();
if (!currentSubject.hasPermission(
WorkspaceDomain.DOMAIN_ID, workspaceId, WorkspaceDomain.RUN)) {
throw new ForbiddenException(
"User doesn't have the required permissions to the specified workspace");
}
// if (!currentSubject.hasPermission(
// WorkspaceDomain.DOMAIN_ID, workspaceId, WorkspaceDomain.RUN)) {
// throw new ForbiddenException(
// "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>
<artifactId>openshift-model</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
@ -105,6 +109,10 @@
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
@ -177,10 +185,6 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-tracing</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-machine-authentication</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,14 +9,9 @@
* Contributors:
* 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.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.commons.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -31,11 +26,11 @@ public class AuthorizedSubject implements Subject {
private static final Logger LOG = LoggerFactory.getLogger(AuthorizedSubject.class);
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.permissionChecker = permissionChecker;
// this.permissionChecker = permissionChecker;
}
@Override
@ -45,18 +40,19 @@ public class AuthorizedSubject implements Subject {
@Override
public boolean hasPermission(String domain, String instance, String action) {
try {
return permissionChecker.hasPermission(getUserId(), domain, instance, action);
} catch (NotFoundException nfe) {
return false;
} catch (ServerException | ConflictException e) {
LOG.error(
format(
"Can't check permissions for user '%s' and instance '%s' of domain '%s'",
getUserId(), domain, instance),
e);
throw new RuntimeException("Can't check user's permissions", e);
}
// try {
// return permissionChecker.hasPermission(getUserId(), domain, instance, action);
// } catch (NotFoundException nfe) {
// return false;
// } catch (ServerException | ConflictException e) {
// LOG.error(
// format(
// "Can't check permissions for user '%s' and instance '%s' of domain '%s'",
// getUserId(), domain, instance),
// e);
// throw new RuntimeException("Can't check user's permissions", e);
// }
return true;
}
@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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,9 +9,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.api.authentication.commons.filter;
import static org.eclipse.che.multiuser.api.authentication.commons.Constants.CHE_SUBJECT_ATTRIBUTE;
package org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
@ -28,9 +26,6 @@ import java.util.List;
import java.util.Optional;
import org.eclipse.che.commons.env.EnvironmentContext;
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.LoggerFactory;
@ -52,6 +47,7 @@ import org.slf4j.LoggerFactory;
* @author Max Shaposhnyk (mshaposh@redhat.com)
*/
public abstract class MultiUserEnvironmentInitializationFilter<T> implements Filter {
public static final String CHE_SUBJECT_ATTRIBUTE = "che_subject";
private static final Logger LOG =
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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,11 +9,9 @@
* Contributors:
* 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 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.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.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
import org.eclipse.che.multiuser.api.authentication.commons.filter.MultiUserEnvironmentInitializationFilter;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
/**
* This filter uses given token directly. It's used for native Kubernetes user authentication.
@ -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
* JWT token claims. The username claim is configured with {@link
* org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_USERNAME_CLAIM_SETTING}. The email claim is
* configured with {@link org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_EMAIL_CLAIM_SETTING}.
* OidcTokenInitializationFilter#OIDC_USERNAME_CLAIM_SETTING}. The email claim is configured with
* {@link OidcTokenInitializationFilter#OIDC_EMAIL_CLAIM_SETTING}.
*/
@Singleton
public class OidcTokenInitializationFilter
@ -52,15 +45,17 @@ public class OidcTokenInitializationFilter
protected static final String DEFAULT_USERNAME_CLAIM = NAME_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 PermissionChecker permissionChecker;
private final UserManager userManager;
private final String usernameClaim;
private final String emailClaim;
@Inject
public OidcTokenInitializationFilter(
PermissionChecker permissionChecker,
JwtParser jwtParser,
SessionStore sessionStore,
RequestTokenExtractor tokenExtractor,
@ -68,7 +63,6 @@ public class OidcTokenInitializationFilter
@Nullable @Named(OIDC_USERNAME_CLAIM_SETTING) String usernameClaim,
@Nullable @Named(OIDC_EMAIL_CLAIM_SETTING) String emailClaim) {
super(sessionStore, tokenExtractor);
this.permissionChecker = permissionChecker;
this.jwtParser = jwtParser;
this.userManager = userManager;
this.usernameClaim = isNullOrEmpty(usernameClaim) ? DEFAULT_USERNAME_CLAIM : usernameClaim;
@ -94,8 +88,7 @@ public class OidcTokenInitializationFilter
claims.getSubject(),
claims.get(emailClaim, String.class),
claims.get(usernameClaim, String.class));
return new AuthorizedSubject(
new SubjectImpl(user.getName(), user.getId(), token, false), permissionChecker);
return new AuthorizedSubject(new SubjectImpl(user.getName(), user.getId(), token, false));
} catch (ServerException | ConflictException 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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,7 +9,7 @@
* Contributors:
* 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;

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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,7 +9,7 @@
* Contributors:
* 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 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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,7 +9,7 @@
* Contributors:
* 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.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
* available under the terms of the Eclipse Public License 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;
import static java.util.Collections.emptyList;
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_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.TOOL_CONTAINER_SOURCE;
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_UNIQUE_PART_SIZE;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
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.Pod;
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.ServicePortBuilder;
import io.fabric8.kubernetes.api.model.VolumeBuilder;
import io.fabric8.kubernetes.api.model.VolumeMount;
import java.security.KeyPair;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.environment.KubernetesEnvironment;
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.secure.ProxyProvisioner;
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 MultiHostCookiePathStrategy multihostCookiePathStrategy;
private int availablePort;
private final KeyPair keyPair;
private final boolean detectCookieAuth;
/**
* Constructor!
*
* @param signatureKeyPair the key pair for JWT proxy SSH comms
* @param jwtProxyConfigBuilderFactory factory to create a JWT proxy config builder
* @param externalServiceExposureStrategy the strategy to expose external servers
* @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
*/
AbstractJwtProxyProvisioner(
KeyPair signatureKeyPair,
JwtProxyConfigBuilderFactory jwtProxyConfigBuilderFactory,
ExternalServiceExposureStrategy externalServiceExposureStrategy,
ExternalServiceExposureStrategy multiHostStrategy,
@ -108,7 +97,6 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
String cpuLimitCores,
String workspaceId,
boolean detectCookieAuth) {
this.keyPair = signatureKeyPair;
this.proxyConfigBuilder = jwtProxyConfigBuilderFactory.create(workspaceId);
this.jwtProxyImage = jwtProxyImage;
this.externalServiceExposureStrategy = externalServiceExposureStrategy;
@ -170,7 +158,6 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
throws InfrastructureException {
Preconditions.checkArgument(
secureServers != null && !secureServers.isEmpty(), "Secure servers are missing");
ensureJwtProxyInjected(k8sEnv, machineName, pod);
Set<String> excludes = new HashSet<>();
Boolean cookiesAuthEnabled = null;
@ -251,44 +238,6 @@ abstract class AbstractJwtProxyProvisioner implements ProxyProvisioner {
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() {
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
* available under the terms of the Eclipse Public License 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;
import com.google.inject.assistedinject.Assisted;
import java.security.KeyPair;
import javax.inject.Inject;
import javax.inject.Named;
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.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.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory;
@ -40,14 +36,12 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtprox
* </ul>
*
* @see JwtProxyConfigBuilder
* @see SignatureKeyManager
* @author Sergii Leshchenko
*/
public class JwtProxyProvisioner extends AbstractJwtProxyProvisioner {
@Inject
public JwtProxyProvisioner(
SignatureKeyManager signatureKeyManager,
JwtProxyConfigBuilderFactory jwtProxyConfigBuilderFactory,
ServiceExposureStrategyProvider serviceExposureStrategyProvider,
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.cpu_request") String cpuRequestCores,
@Named("che.server.secure_exposer.jwtproxy.cpu_limit") String cpuLimitCores,
@Assisted RuntimeIdentity identity)
throws InternalInfrastructureException {
@Assisted RuntimeIdentity identity) {
super(
constructKeyPair(signatureKeyManager, identity),
jwtProxyConfigBuilderFactory,
serviceExposureStrategyProvider.get(),
serviceExposureStrategyProvider.getMultiHostStrategy(),
@ -75,18 +67,6 @@ public class JwtProxyProvisioner extends AbstractJwtProxyProvisioner {
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
protected ExposureConfiguration getExposureConfiguration(ServerConfig 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
* available under the terms of the Eclipse Public License 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.cpu_request") String cpuRequestCores,
@Named("che.server.secure_exposer.jwtproxy.cpu_limit") String cpuLimitCores,
@Assisted RuntimeIdentity identity)
throws InternalInfrastructureException {
@Assisted RuntimeIdentity identity) {
super(
constructSignatureKeyPair(),
jwtProxyConfigBuilderFactory,
serviceExposureStrategyProvider.get(),
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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,9 +9,9 @@
* Contributors:
* 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.anyString;
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.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
import org.mockito.Mock;
import org.mockito.Mockito;
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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,10 +9,10 @@
* Contributors:
* 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.multiuser.oidc.filter.OidcTokenInitializationFilter.DEFAULT_USERNAME_CLAIM;
import static org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.OidcTokenInitializationFilter.DEFAULT_EMAIL_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.mock;
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.model.user.User;
import org.eclipse.che.api.user.server.UserManager;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
@ -40,8 +37,6 @@ import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class OidcTokenInitializationFilterTest {
@Mock private PermissionChecker permissionsChecker;
@Mock private JwtParser jwtParser;
@Mock private SessionStore sessionStore;
@Mock private RequestTokenExtractor tokenExtractor;
@ -63,13 +58,7 @@ public class OidcTokenInitializationFilterTest {
public void setUp() {
tokenInitializationFilter =
new OidcTokenInitializationFilter(
permissionsChecker,
jwtParser,
sessionStore,
tokenExtractor,
userManager,
usernameClaim,
emailClaim);
jwtParser, sessionStore, tokenExtractor, userManager, usernameClaim, emailClaim);
lenient().when(jwsClaims.getBody()).thenReturn(claims);
lenient().when(claims.getSubject()).thenReturn(TEST_USERID);
@ -122,13 +111,7 @@ public class OidcTokenInitializationFilterTest {
throws ServerException, ConflictException {
tokenInitializationFilter =
new OidcTokenInitializationFilter(
permissionsChecker,
jwtParser,
sessionStore,
tokenExtractor,
userManager,
customUsernameClaim,
emailClaim);
jwtParser, sessionStore, tokenExtractor, userManager, customUsernameClaim, emailClaim);
User createdUser = mock(User.class);
when(createdUser.getId()).thenReturn(TEST_USERID);
when(createdUser.getName()).thenReturn(TEST_USERNAME);
@ -151,13 +134,7 @@ public class OidcTokenInitializationFilterTest {
throws ServerException, ConflictException {
tokenInitializationFilter =
new OidcTokenInitializationFilter(
permissionsChecker,
jwtParser,
sessionStore,
tokenExtractor,
userManager,
usernameClaim,
customEmailClaim);
jwtParser, sessionStore, tokenExtractor, userManager, usernameClaim, customEmailClaim);
User createdUser = mock(User.class);
when(createdUser.getId()).thenReturn(TEST_USERID);
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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,7 +9,7 @@
* Contributors:
* 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.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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,7 +9,7 @@
* Contributors:
* 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.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
* available under the terms of the Eclipse Public License 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_HEADER;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
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.ServicePort;
import java.net.URI;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.Base64;
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.spi.InfrastructureException;
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.PodData;
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.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Ignore;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@ -73,6 +71,7 @@ import org.testng.annotations.Test;
* @author Sergii Leshchenko
*/
@Listeners(MockitoTestNGListener.class)
@Ignore
public class JwtProxyProvisionerTest {
private static final String WORKSPACE_ID = "workspace123";
@ -81,7 +80,7 @@ public class JwtProxyProvisionerTest {
private final RuntimeIdentity runtimeId =
new RuntimeIdentityImpl(WORKSPACE_ID, "env123", "owner123", "infraNamespace");
@Mock private SignatureKeyManager signatureKeyManager;
// @Mock private SignatureKeyManager signatureKeyManager;
@Mock private PublicKey publicKey;
@Mock private JwtProxyConfigBuilderFactory configBuilderFactory;
@Mock private ServiceExposureStrategyProvider serviceExposureStrategyProvider;
@ -96,8 +95,8 @@ public class JwtProxyProvisionerTest {
@BeforeMethod
public void setUp() throws Exception {
when(signatureKeyManager.getOrCreateKeyPair(anyString()))
.thenReturn(new KeyPair(publicKey, null));
// when(signatureKeyManager.getOrCreateKeyPair(anyString()))
// .thenReturn(new KeyPair(publicKey, null));
lenient().when(publicKey.getEncoded()).thenReturn("publickey".getBytes());
when(configBuilderFactory.create(any()))
@ -111,7 +110,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner =
new JwtProxyProvisioner(
signatureKeyManager,
// signatureKeyManager,
configBuilderFactory,
serviceExposureStrategyProvider,
cookiePathStrategy,
@ -233,7 +232,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner =
new JwtProxyProvisioner(
signatureKeyManager,
// signatureKeyManager,
configBuilderFactory,
serviceExposureStrategyProvider,
cookiePathStrategy,
@ -284,7 +283,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner =
new JwtProxyProvisioner(
signatureKeyManager,
// signatureKeyManager,
configBuilderFactory,
serviceExposureStrategyProvider,
cookiePathStrategy,
@ -319,6 +318,7 @@ public class JwtProxyProvisionerTest {
}
@Test
@Ignore
public void shouldBindToLocalhostWhenNoServiceForServerExists() throws Exception {
// given
JwtProxyConfigBuilder configBuilder = mock(JwtProxyConfigBuilder.class);
@ -326,7 +326,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner =
new JwtProxyProvisioner(
signatureKeyManager,
// signatureKeyManager,
configBuilderFactory,
serviceExposureStrategyProvider,
cookiePathStrategy,
@ -361,6 +361,7 @@ public class JwtProxyProvisionerTest {
}
@Test
@Ignore
public void multiHostStrategiesUsedForServerRequiringSubdomain() throws Exception {
// given
JwtProxyConfigBuilder configBuilder = mock(JwtProxyConfigBuilder.class);
@ -368,7 +369,7 @@ public class JwtProxyProvisionerTest {
jwtProxyProvisioner =
new JwtProxyProvisioner(
signatureKeyManager,
// signatureKeyManager,
configBuilderFactory,
serviceExposureStrategyProvider,
cookiePathStrategy,

View File

@ -118,26 +118,6 @@
<groupId>org.eclipse.che.infrastructure</groupId>
<artifactId>infrastructure-kubernetes</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-authentication-commons</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-api-authorization</artifactId>
</dependency>
<dependency>
<groupId>org.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>
<groupId>org.slf4j</groupId>
<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
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -9,11 +9,12 @@
* Contributors:
* 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.ws.rs.BadRequestException;
import jakarta.ws.rs.core.HttpHeaders;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.RequestTokenExtractor;
/** Extract sso token from request headers. */
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
* available under the terms of the Eclipse Public License 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;
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.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 com.google.inject.Provider;
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 java.io.UnsupportedEncodingException;
import java.net.URI;
@ -27,17 +25,15 @@ import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
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.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.RuntimeContext;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
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.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.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientConfigFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -63,21 +59,21 @@ public class KeycloakProviderConfigFactory extends KubernetesClientConfigFactory
private final String oauthIdentityProvider;
private final KeycloakServiceClient keycloakServiceClient;
// private final KeycloakServiceClient keycloakServiceClient;
private final Provider<WorkspaceRuntimes> workspaceRuntimeProvider;
private final String messageToLinkAccount;
@Inject
public KeycloakProviderConfigFactory(
KeycloakServiceClient keycloakServiceClient,
KeycloakSettings keycloakSettings,
// KeycloakServiceClient keycloakServiceClient,
// KeycloakSettings keycloakSettings,
Provider<WorkspaceRuntimes> workspaceRuntimeProvider,
@Nullable @Named("che.infra.openshift.oauth_identity_provider") String oauthIdentityProvider,
@Named("che.api") String apiEndpoint,
@Nullable @Named("che.infra.kubernetes.master_url") String masterUrl,
@Nullable @Named("che.infra.kubernetes.trust_certs") Boolean doTrustCerts) {
super(masterUrl, doTrustCerts);
this.keycloakServiceClient = keycloakServiceClient;
// this.keycloakServiceClient = keycloakServiceClient;
this.workspaceRuntimeProvider = workspaceRuntimeProvider;
this.oauthIdentityProvider = oauthIdentityProvider;
@ -89,11 +85,11 @@ public class KeycloakProviderConfigFactory extends KubernetesClientConfigFactory
+ "<a href='"
// Here should be used public url. User should have it to make manual actions in the
// browser.
+ keycloakSettings.get().get(AUTH_SERVER_URL_SETTING)
// + keycloakSettings.get().get(AUTH_SERVER_URL_SETTING)
+ "/realms/"
+ keycloakSettings.get().get(REALM_SETTING)
// + keycloakSettings.get().get(REALM_SETTING)
+ "/account/identity?referrer="
+ keycloakSettings.get().get(CLIENT_ID_SETTING)
// + keycloakSettings.get().get(CLIENT_ID_SETTING)
+ "&referrer_uri="
+ buildReferrerURI(apiEndpoint)
+ "' 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 {
try {
KeycloakTokenResponse keycloakTokenInfos =
keycloakServiceClient.getIdentityProviderToken(oauthIdentityProvider);
if ("user:full".equals(keycloakTokenInfos.getScope())) {
return new OpenShiftConfigBuilder(OpenShiftConfig.wrap(defaultConfig))
.withOauthToken(keycloakTokenInfos.getAccessToken())
.build();
} else {
throw new InfrastructureException(
"Cannot retrieve user OpenShift token: '"
+ oauthIdentityProvider
+ "' identity provider is not granted full rights: "
+ oauthIdentityProvider);
}
} catch (UnauthorizedException e) {
LOG.error("Cannot retrieve User OpenShift token from the identity provider", e);
throw new InfrastructureException(messageToLinkAccount);
} catch (BadRequestException e) {
LOG.error(
"Cannot retrieve User OpenShift token from the '"
+ oauthIdentityProvider
+ "' identity provider",
e);
if (e.getMessage().endsWith("Invalid token.")) {
throw new InfrastructureException(
"Your session has expired. \nPlease "
+ "<a href='javascript:location.reload();' target='_top'>"
+ "login"
+ "</a> to Che again to get access to your OpenShift account");
}
throw new InfrastructureException(e.getMessage(), e);
} catch (Exception e) {
LOG.error(
"Cannot retrieve User OpenShift token from the '"
+ oauthIdentityProvider
+ "' identity provider",
e);
throw new InfrastructureException(e.getMessage(), e);
}
// try {
// KeycloakTokenResponse keycloakTokenInfos =
// keycloakServiceClient.getIdentityProviderToken(oauthIdentityProvider);
// if ("user:full".equals(keycloakTokenInfos.getScope())) {
// return new OpenShiftConfigBuilder(OpenShiftConfig.wrap(defaultConfig))
// .withOauthToken(keycloakTokenInfos.getAccessToken())
// .build();
// } else {
throw new InfrastructureException(
"Cannot retrieve user OpenShift token: '"
+ oauthIdentityProvider
+ "' identity provider is not granted full rights: "
+ oauthIdentityProvider);
// }
// } catch (UnauthorizedException e) {
// LOG.error("Cannot retrieve User OpenShift token from the identity provider", e);
//
// throw new InfrastructureException(messageToLinkAccount);
// } catch (BadRequestException e) {
// LOG.error(
// "Cannot retrieve User OpenShift token from the '"
// + oauthIdentityProvider
// + "' identity provider",
// e);
// if (e.getMessage().endsWith("Invalid token.")) {
// throw new InfrastructureException(
// "Your session has expired. \nPlease "
// + "<a href='javascript:location.reload();' target='_top'>"
// + "login"
// + "</a> to Che again to get access to your OpenShift account");
// }
// throw new InfrastructureException(e.getMessage(), e);
// } catch (Exception e) {
// LOG.error(
// "Cannot retrieve User OpenShift token from the '"
// + oauthIdentityProvider
// + "' identity provider",
// 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
* available under the terms of the Eclipse Public License 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.commons.subject.Subject;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.multiuser.api.authentication.commons.SessionStore;
import org.eclipse.che.multiuser.api.authentication.commons.filter.MultiUserEnvironmentInitializationFilter;
import org.eclipse.che.multiuser.api.authentication.commons.token.RequestTokenExtractor;
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.AuthorizedSubject;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.MultiUserEnvironmentInitializationFilter;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.RequestTokenExtractor;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.SessionStore;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -45,7 +44,7 @@ public class OpenshiftTokenInitializationFilter
private static final Logger LOG =
LoggerFactory.getLogger(OpenshiftTokenInitializationFilter.class);
private final PermissionChecker permissionChecker;
// private final PermissionChecker permissionChecker;
private final OpenShiftClientFactory clientFactory;
private final UserManager userManager;
@ -55,12 +54,11 @@ public class OpenshiftTokenInitializationFilter
SessionStore sessionStore,
RequestTokenExtractor tokenExtractor,
OpenShiftClientFactory clientFactory,
UserManager userManager,
PermissionChecker permissionChecker) {
UserManager userManager) {
super(sessionStore, tokenExtractor);
this.clientFactory = clientFactory;
this.userManager = userManager;
this.permissionChecker = permissionChecker;
// this.permissionChecker = permissionChecker;
}
@Override
@ -92,8 +90,7 @@ public class OpenshiftTokenInitializationFilter
try {
ObjectMeta userMeta = osu.getMetadata();
User user = userManager.getOrCreateUser(getUserId(osu), userMeta.getName());
return new AuthorizedSubject(
new SubjectImpl(user.getName(), user.getId(), token, false), permissionChecker);
return new AuthorizedSubject(new SubjectImpl(user.getName(), user.getId(), token, false));
} catch (ServerException | ConflictException 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
* available under the terms of the Eclipse Public License 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.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.subject.Subject;
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.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.RequestTokenExtractor;
import org.eclipse.che.workspace.infrastructure.kubernetes.multiuser.oauth.SessionStore;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
@ -41,7 +40,7 @@ public class OpenshiftTokenInitializationFilterTest {
@Mock private SessionStore sessionStore;
@Mock private RequestTokenExtractor tokenExtractor;
@Mock private UserManager userManager;
@Mock private PermissionChecker permissionChecker;
// @Mock private PermissionChecker permissionChecker;
@Mock private OpenShiftClientFactory openShiftClientFactory;
@Mock private OpenShiftClient openShiftClient;
@ -58,7 +57,7 @@ public class OpenshiftTokenInitializationFilterTest {
public void setUp() throws InfrastructureException {
openshiftTokenInitializationFilter =
new OpenshiftTokenInitializationFilter(
sessionStore, tokenExtractor, openShiftClientFactory, userManager, permissionChecker);
sessionStore, tokenExtractor, openShiftClientFactory, userManager);
}
@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