Respect authorisation request opt-out on workspace start

pull/576/head
Igor 2023-10-05 15:09:47 +03:00
parent 5d645e1d3c
commit 1d18eb871b
34 changed files with 892 additions and 214 deletions

View File

@ -51,7 +51,6 @@ import org.eclipse.che.api.factory.server.github.GithubFactoryParametersResolver
import org.eclipse.che.api.factory.server.github.GithubScmFileResolver;
import org.eclipse.che.api.factory.server.gitlab.GitlabFactoryParametersResolver;
import org.eclipse.che.api.factory.server.gitlab.GitlabScmFileResolver;
import org.eclipse.che.api.factory.server.scm.OAuthTokenFetcher;
import org.eclipse.che.api.metrics.WsMasterMetricsModule;
import org.eclipse.che.api.system.server.ServiceTermination;
import org.eclipse.che.api.system.server.SystemModule;
@ -413,7 +412,6 @@ public class WsMasterModule extends AbstractModule {
bind(TokenValidator.class).to(NotImplementedTokenValidator.class);
bind(ProfileDao.class).to(JpaProfileDao.class);
bind(OAuthAPI.class).to(EmbeddedOAuthAPI.class).asEagerSingleton();
bind(OAuthTokenFetcher.class).to(EmbeddedOAuthAPI.class).asEagerSingleton();
}
bind(AdminPermissionInitializer.class).asEagerSingleton();

View File

@ -23,6 +23,10 @@
<packaging>jar</packaging>
<name>Infrastructure :: Factory components</name>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@ -43,6 +47,14 @@
<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-auth</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -12,13 +12,16 @@
package org.eclipse.che.api.factory.server.scm;
import com.google.inject.AbstractModule;
import org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesAuthorisationRequestManager;
import org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager;
import org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesPersonalAccessTokenManager;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
public class KubernetesScmModule extends AbstractModule {
@Override
protected void configure() {
bind(GitCredentialManager.class).to(KubernetesGitCredentialManager.class);
bind(PersonalAccessTokenManager.class).to(KubernetesPersonalAccessTokenManager.class);
bind(AuthorisationRequestManager.class).to(KubernetesAuthorisationRequestManager.class);
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.api.factory.server.scm.kubernetes;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.commons.lang.UrlUtils.getParameter;
import static org.eclipse.che.commons.lang.UrlUtils.getQueryParametersFromState;
import static org.eclipse.che.commons.lang.UrlUtils.getRequestUrl;
import static org.eclipse.che.commons.lang.UrlUtils.getState;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
import com.google.gson.Gson;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClient;
import jakarta.ws.rs.core.UriInfo;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException;
import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
/** Store and retrieve rejected authorisation requests in the Kubernetes ConfigMap. */
@Singleton
public class KubernetesAuthorisationRequestManager implements AuthorisationRequestManager {
private final KubernetesNamespaceFactory namespaceFactory;
private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private static final String SKIP_AUTHORISATION_MAP_KEY = "skip-authorisation";
@Inject
public KubernetesAuthorisationRequestManager(
KubernetesNamespaceFactory namespaceFactory,
CheServerKubernetesClientFactory cheServerKubernetesClientFactory) {
this.namespaceFactory = namespaceFactory;
this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory;
}
@Override
public void store(String scmProviderName) {
if (isStored(scmProviderName)) {
return;
}
ConfigMap configMap = getConfigMap();
HashSet<String> fromJson = getSkipAuthorisationValues();
fromJson.add(scmProviderName);
configMap.setData(Map.of(SKIP_AUTHORISATION_MAP_KEY, fromJson.toString()));
patchConfigMap(configMap);
}
@Override
public void remove(String scmProviderName) {
if (!isStored(scmProviderName)) {
return;
}
ConfigMap configMap = getConfigMap();
HashSet<String> fromJson = getSkipAuthorisationValues();
fromJson.remove(scmProviderName);
configMap.setData(Map.of(SKIP_AUTHORISATION_MAP_KEY, fromJson.toString()));
patchConfigMap(configMap);
}
@Override
public boolean isStored(String scmProviderName) {
return getSkipAuthorisationValues().contains(scmProviderName);
}
@Override
public void callback(UriInfo uriInfo, List<String> errorValues) {
URL requestUrl = getRequestUrl(uriInfo);
Map<String, List<String>> params = getQueryParametersFromState(getState(requestUrl));
errorValues = errorValues == null ? uriInfo.getQueryParameters().get("error") : errorValues;
if (errorValues != null && errorValues.contains("access_denied")) {
store(getParameter(params, "oauth_provider"));
}
}
private ConfigMap getConfigMap() {
try (KubernetesClient kubernetesClient = cheServerKubernetesClientFactory.create()) {
String namespace = getFirstNamespace();
return kubernetesClient
.configMaps()
.inNamespace(namespace)
.withName(PREFERENCES_CONFIGMAP_NAME)
.get();
} catch (UnsatisfiedScmPreconditionException
| ScmConfigurationPersistenceException
| InfrastructureException e) {
throw new RuntimeException(e);
}
}
private void patchConfigMap(ConfigMap configMap) {
try (KubernetesClient kubernetesClient = cheServerKubernetesClientFactory.create()) {
kubernetesClient
.configMaps()
.inNamespace(getFirstNamespace())
.withName(PREFERENCES_CONFIGMAP_NAME)
.patch(configMap);
} catch (UnsatisfiedScmPreconditionException
| ScmConfigurationPersistenceException
| InfrastructureException e) {
throw new RuntimeException(e);
}
}
private HashSet<String> getSkipAuthorisationValues() {
String data = getConfigMap().getData().get(SKIP_AUTHORISATION_MAP_KEY);
return new Gson().fromJson(isNullOrEmpty(data) ? "[]" : data, HashSet.class);
}
private String getFirstNamespace()
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException {
try {
return namespaceFactory.list().stream()
.map(KubernetesNamespaceMeta::getName)
.findFirst()
.orElseThrow(
() ->
new UnsatisfiedScmPreconditionException(
"No user namespace found. Cannot read SCM credentials."));
} catch (InfrastructureException e) {
throw new ScmConfigurationPersistenceException(e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,197 @@
/*
* 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.api.factory.server.scm.kubernetes;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.AbstractWorkspaceServiceAccount.PREFERENCES_CONFIGMAP_NAME;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapList;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import java.util.Collections;
import java.util.Map;
import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class KubernetesAuthorisationRequestManagerTest {
@Mock private KubernetesNamespaceFactory namespaceFactory;
@Mock
private MixedOperation<ConfigMap, ConfigMapList, Resource<ConfigMap>> configMapsMixedOperation;
@Mock NonNamespaceOperation<ConfigMap, ConfigMapList, Resource<ConfigMap>> nonNamespaceOperation;
@Mock private Resource<ConfigMap> configMapResource;
@Mock private ConfigMap configMap;
@Mock private KubernetesClient kubeClient;
@Mock private CheServerKubernetesClientFactory cheServerKubernetesClientFactory;
private KubernetesAuthorisationRequestManager authorisationRequestManager;
@BeforeMethod
protected void init() throws Exception {
KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");
when(namespaceFactory.list()).thenReturn(Collections.singletonList(meta));
when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient);
when(kubeClient.configMaps()).thenReturn(configMapsMixedOperation);
when(configMapsMixedOperation.inNamespace(eq(meta.getName())))
.thenReturn(nonNamespaceOperation);
when(nonNamespaceOperation.withName(eq(PREFERENCES_CONFIGMAP_NAME)))
.thenReturn(configMapResource);
when(configMapResource.get()).thenReturn(configMap);
authorisationRequestManager =
new KubernetesAuthorisationRequestManager(
namespaceFactory, cheServerKubernetesClientFactory);
}
@Test
public void shouldStoreAuthorisationRequestToEmptyConfigMap() {
// given
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.store("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[test-provider]");
}
@Test
public void shouldStoreAuthorisationRequestToNonEmptyConfigMap() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[existing-provider]"));
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.store("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[test-provider, existing-provider]");
}
@Test
public void shouldNotStoreAuthorisationRequestIfAlreadyStored() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[test-provider]"));
// when
authorisationRequestManager.store("test-provider");
// then
verify(configMap, never()).setData(anyMap());
}
@Test
public void shouldRemoveAuthorisationRequestFromNonEmptyConfigMap() {
// given
when(configMap.getData())
.thenReturn(Map.of("skip-authorisation", "[test-provider, existing-provider]"));
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.remove("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[existing-provider]");
}
@Test
public void shouldRemoveAuthorisationRequestFromSingleValueConfigMap() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[test-provider]"));
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
// when
authorisationRequestManager.remove("test-provider");
// then
verify(configMap).setData(captor.capture());
Map<String, String> value = captor.getValue();
assertEquals(value.get("skip-authorisation"), "[]");
}
@Test
public void shouldNotRemoveAuthorisationRequestIfAlreadyRemoved() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[]"));
// when
authorisationRequestManager.remove("test-provider");
// then
verify(configMap, never()).setData(anyMap());
}
@Test
public void shouldReturnFalseAuthorisationRequestStateFromEmptyConfigMap() {
// when
boolean stored = authorisationRequestManager.isStored("test-provider");
// then
assertFalse(stored);
}
@Test
public void shouldReturnFalseAuthorisationRequestStateFromNonEmptyConfigMap() {
// given
when(configMap.getData()).thenReturn(Map.of("skip-authorisation", "[existing-provider]"));
// when
boolean stored = authorisationRequestManager.isStored("test-provider");
// then
assertFalse(stored);
}
@Test
public void shouldReturnTrueAuthorisationRequestStateFromNonEmptyConfigMap() {
// given
when(configMap.getData())
.thenReturn(Map.of("skip-authorisation", "[existing-provider, test-provider]"));
// when
boolean stored = authorisationRequestManager.isStored("test-provider");
// then
assertTrue(stored);
}
}

View File

@ -59,10 +59,6 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>

View File

@ -0,0 +1,47 @@
/*
* 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.security.oauth;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
/**
* Manager for storing and retrieving rejected authorisation requests. This is used to prevent from
* asking the user to grant access to the SCM provider after the user has already rejected the
* request.
*/
public interface AuthorisationRequestManager {
/**
* Store the reject flag for the given SCM provider name.
*
* @param scmProviderName the SCM provider name
*/
void store(String scmProviderName);
/**
* Remove the reject flag for the given SCM provider name.
*
* @param scmProviderName the SCM provider name
*/
void remove(String scmProviderName);
/**
* Check if the reject flag is stored for the given SCM provider name.
*
* @param scmProviderName the SCM provider name to check
* @return {@code true} if the reject flag is stored, {@code false} otherwise
*/
boolean isStored(String scmProviderName);
/** This method must be called on the Oauth callback from the SCM provider. */
void callback(UriInfo uriInfo, List<String> errorValues);
}

View File

@ -40,7 +40,6 @@ import org.eclipse.che.api.core.UnauthorizedException;
import org.eclipse.che.api.core.rest.shared.dto.Link;
import org.eclipse.che.api.core.rest.shared.dto.LinkParameter;
import org.eclipse.che.api.core.util.LinksHelper;
import org.eclipse.che.api.factory.server.scm.OAuthTokenFetcher;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.security.oauth.shared.dto.OAuthAuthenticatorDescriptor;
@ -54,7 +53,7 @@ import org.slf4j.LoggerFactory;
* @author Mykhailo Kuznietsov
*/
@Singleton
public class EmbeddedOAuthAPI implements OAuthAPI, OAuthTokenFetcher {
public class EmbeddedOAuthAPI implements OAuthAPI {
private static final Logger LOG = LoggerFactory.getLogger(EmbeddedOAuthAPI.class);
@Inject

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2023 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -38,6 +38,7 @@ public class OAuthAuthenticationService extends Service {
@Context protected SecurityContext security;
@Inject private OAuthAPI oAuthAPI;
@Inject private AuthorisationRequestManager authorisationRequestManager;
/**
* Redirect request to OAuth provider site for authentication|authorization. Client must provide
@ -66,6 +67,7 @@ public class OAuthAuthenticationService extends Service {
/** Process OAuth callback */
public Response callback(@QueryParam("errorValues") List<String> errorValues)
throws OAuthAuthenticationException, NotFoundException, ForbiddenException {
authorisationRequestManager.callback(uriInfo, errorValues);
return oAuthAPI.callback(uriInfo, errorValues);
}

View File

@ -11,10 +11,8 @@
*/
package org.eclipse.che.api.factory.server.azure.devops;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.security.oauth1.OAuthAuthenticationService.ERROR_QUERY_NAME;
import com.google.common.base.Strings;
import jakarta.validation.constraints.NotNull;
@ -23,6 +21,7 @@ import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
@ -38,6 +37,7 @@ import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/**
* Provides Factory Parameters resolver for Azure DevOps repositories.
@ -45,7 +45,10 @@ import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
* @author Anatolii Bazko
*/
@Singleton
public class AzureDevOpsFactoryParametersResolver implements FactoryParametersResolver {
public class AzureDevOpsFactoryParametersResolver extends BaseFactoryParameterResolver
implements FactoryParametersResolver {
private static final String PROVIDER_NAME = "azure-devops";
/** Parser which will allow to check validity of URLs and create objects. */
private final AzureDevOpsURLParser azureDevOpsURLParser;
@ -61,7 +64,9 @@ public class AzureDevOpsFactoryParametersResolver implements FactoryParametersRe
AzureDevOpsURLParser azureDevOpsURLParser,
URLFetcher urlFetcher,
URLFactoryBuilder urlFactoryBuilder,
PersonalAccessTokenManager personalAccessTokenManager) {
PersonalAccessTokenManager personalAccessTokenManager,
AuthorisationRequestManager authorisationRequestManager) {
super(authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
this.azureDevOpsURLParser = azureDevOpsURLParser;
this.urlFetcher = urlFetcher;
this.urlFactoryBuilder = urlFactoryBuilder;
@ -75,27 +80,25 @@ public class AzureDevOpsFactoryParametersResolver implements FactoryParametersRe
&& azureDevOpsURLParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
}
@Override
public String getProviderName() {
return PROVIDER_NAME;
}
@Override
public FactoryMetaDto createFactory(@NotNull final Map<String, String> factoryParameters)
throws ApiException {
boolean skipAuthentication =
factoryParameters.get(ERROR_QUERY_NAME) != null
&& factoryParameters.get(ERROR_QUERY_NAME).equals("access_denied");
// no need to check null value of url parameter as accept() method has performed the check
final AzureDevOpsUrl azureDevOpsUrl =
azureDevOpsURLParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
azureDevOpsUrl,
new AzureDevOpsAuthorizingFileContentProvider(
azureDevOpsUrl, urlFetcher, personalAccessTokenManager),
extractOverrideParams(factoryParameters),
skipAuthentication)
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(new AzureDevOpsFactoryVisitor(azureDevOpsUrl));
return createFactory(
factoryParameters,
azureDevOpsUrl,
new AzureDevOpsFactoryVisitor(azureDevOpsUrl),
new AzureDevOpsAuthorizingFileContentProvider(
azureDevOpsUrl, urlFetcher, personalAccessTokenManager));
}
/**

View File

@ -18,12 +18,12 @@ import javax.inject.Named;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.api.factory.server.scm.AbstractGitUserDataFetcher;
import org.eclipse.che.api.factory.server.scm.GitUserData;
import org.eclipse.che.api.factory.server.scm.OAuthTokenFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.scm.exception.ScmBadRequestException;
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException;
import org.eclipse.che.security.oauth.OAuthAPI;
/**
* Azure DevOps user data fetcher.
@ -37,7 +37,7 @@ public class AzureDevOpsUserDataFetcher extends AbstractGitUserDataFetcher {
@Inject
public AzureDevOpsUserDataFetcher(
OAuthTokenFetcher oAuthTokenFetcher,
OAuthAPI oAuthTokenFetcher,
PersonalAccessTokenManager personalAccessTokenManager,
AzureDevOpsApiClient azureDevOpsApiClient,
@Named("che.api") String cheApiEndpoint,

View File

@ -11,10 +11,8 @@
*/
package org.eclipse.che.api.factory.server.bitbucket;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.security.oauth1.OAuthAuthenticationService.ERROR_QUERY_NAME;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
@ -22,6 +20,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
@ -31,10 +30,10 @@ import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto;
import org.eclipse.che.api.factory.shared.dto.FactoryVisitor;
import org.eclipse.che.api.factory.shared.dto.ScmInfoDto;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/**
* Provides Factory Parameters resolver for both public and private bitbucket repositories.
@ -43,7 +42,9 @@ import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
*/
@Singleton
public class BitbucketServerAuthorizingFactoryParametersResolver
implements FactoryParametersResolver {
extends BaseFactoryParameterResolver implements FactoryParametersResolver {
private static final String PROVIDER_NAME = "bitbucket-server";
private final URLFactoryBuilder urlFactoryBuilder;
private final URLFetcher urlFetcher;
@ -57,7 +58,9 @@ public class BitbucketServerAuthorizingFactoryParametersResolver
URLFactoryBuilder urlFactoryBuilder,
URLFetcher urlFetcher,
BitbucketServerURLParser bitbucketURLParser,
PersonalAccessTokenManager personalAccessTokenManager) {
PersonalAccessTokenManager personalAccessTokenManager,
AuthorisationRequestManager authorisationRequestManager) {
super(authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
this.urlFactoryBuilder = urlFactoryBuilder;
this.urlFetcher = urlFetcher;
this.bitbucketURLParser = bitbucketURLParser;
@ -77,6 +80,11 @@ public class BitbucketServerAuthorizingFactoryParametersResolver
&& bitbucketURLParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
}
@Override
public String getProviderName() {
return PROVIDER_NAME;
}
/**
* Create factory object based on provided parameters
*
@ -90,23 +98,13 @@ public class BitbucketServerAuthorizingFactoryParametersResolver
// no need to check null value of url parameter as accept() method has performed the check
final BitbucketServerUrl bitbucketServerUrl =
bitbucketURLParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
final FileContentProvider fileContentProvider =
new BitbucketServerAuthorizingFileContentProvider(
bitbucketServerUrl, urlFetcher, personalAccessTokenManager);
boolean skipAuthentication =
factoryParameters.get(ERROR_QUERY_NAME) != null
&& factoryParameters.get(ERROR_QUERY_NAME).equals("access_denied");
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
bitbucketServerUrl,
fileContentProvider,
extractOverrideParams(factoryParameters),
skipAuthentication)
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(new BitbucketFactoryVisitor(bitbucketServerUrl));
return createFactory(
factoryParameters,
bitbucketServerUrl,
new BitbucketFactoryVisitor(bitbucketServerUrl),
new BitbucketServerAuthorizingFileContentProvider(
bitbucketServerUrl, urlFetcher, personalAccessTokenManager));
}
/**

View File

@ -45,6 +45,7 @@ import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
import org.eclipse.che.security.oauth.OAuthAPI;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
@ -62,6 +63,8 @@ public class BitbucketServerAuthorizingFactoryParametersResolverTest {
@Mock private DevfileFilenamesProvider devfileFilenamesProvider;
@Mock private AuthorisationRequestManager authorisationRequestManager;
BitbucketServerURLParser bitbucketURLParser;
@Mock private PersonalAccessTokenManager personalAccessTokenManager;
@ -79,7 +82,11 @@ public class BitbucketServerAuthorizingFactoryParametersResolverTest {
assertNotNull(this.bitbucketURLParser);
bitbucketServerFactoryParametersResolver =
new BitbucketServerAuthorizingFactoryParametersResolver(
urlFactoryBuilder, urlFetcher, bitbucketURLParser, personalAccessTokenManager);
urlFactoryBuilder,
urlFetcher,
bitbucketURLParser,
personalAccessTokenManager,
authorisationRequestManager);
assertNotNull(this.bitbucketServerFactoryParametersResolver);
}

View File

@ -11,10 +11,8 @@
*/
package org.eclipse.che.api.factory.server.bitbucket;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.security.oauth1.OAuthAuthenticationService.ERROR_QUERY_NAME;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
@ -22,6 +20,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
@ -35,10 +34,14 @@ import org.eclipse.che.api.factory.shared.dto.ScmInfoDto;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/** Provides Factory Parameters resolver for bitbucket repositories. */
@Singleton
public class BitbucketFactoryParametersResolver implements FactoryParametersResolver {
public class BitbucketFactoryParametersResolver extends BaseFactoryParameterResolver
implements FactoryParametersResolver {
private static final String PROVIDER_NAME = "bitbucket";
/** Parser which will allow to check validity of URLs and create objects. */
private final BitbucketURLParser bitbucketURLParser;
@ -64,7 +67,9 @@ public class BitbucketFactoryParametersResolver implements FactoryParametersReso
URLFactoryBuilder urlFactoryBuilder,
ProjectConfigDtoMerger projectConfigDtoMerger,
PersonalAccessTokenManager personalAccessTokenManager,
BitbucketApiClient bitbucketApiClient) {
BitbucketApiClient bitbucketApiClient,
AuthorisationRequestManager authorisationRequestManager) {
super(authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
this.bitbucketURLParser = bitbucketURLParser;
this.urlFetcher = urlFetcher;
this.bitbucketSourceStorageBuilder = bitbucketSourceStorageBuilder;
@ -88,6 +93,11 @@ public class BitbucketFactoryParametersResolver implements FactoryParametersReso
&& bitbucketURLParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
}
@Override
public String getProviderName() {
return PROVIDER_NAME;
}
/**
* Create factory object based on provided parameters
*
@ -100,19 +110,13 @@ public class BitbucketFactoryParametersResolver implements FactoryParametersReso
// no need to check null value of url parameter as accept() method has performed the check
final BitbucketUrl bitbucketUrl =
bitbucketURLParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
boolean skipAuthentication =
factoryParameters.get(ERROR_QUERY_NAME) != null
&& factoryParameters.get(ERROR_QUERY_NAME).equals("access_denied");
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
bitbucketUrl,
new BitbucketAuthorizingFileContentProvider(
bitbucketUrl, urlFetcher, personalAccessTokenManager, bitbucketApiClient),
extractOverrideParams(factoryParameters),
skipAuthentication)
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(new BitbucketFactoryVisitor(bitbucketUrl));
return createFactory(
factoryParameters,
bitbucketUrl,
new BitbucketFactoryVisitor(bitbucketUrl),
new BitbucketAuthorizingFileContentProvider(
bitbucketUrl, urlFetcher, personalAccessTokenManager, bitbucketApiClient));
}
/**

View File

@ -49,6 +49,7 @@ import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
@ -66,6 +67,8 @@ public class BitbucketFactoryParametersResolverTest {
@Mock private DevfileFilenamesProvider devfileFilenamesProvider;
@Mock private AuthorisationRequestManager authorisationRequestManager;
/** Parser which will allow to check validity of URLs and create objects. */
private BitbucketURLParser bitbucketURLParser;
@ -104,7 +107,8 @@ public class BitbucketFactoryParametersResolverTest {
urlFactoryBuilder,
projectConfigDtoMerger,
personalAccessTokenManager,
bitbucketApiClient);
bitbucketApiClient,
authorisationRequestManager);
assertNotNull(this.bitbucketFactoryParametersResolver);
}

View File

@ -34,6 +34,10 @@
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>

View File

@ -12,7 +12,6 @@
package org.eclipse.che.api.factory.server.git.ssh;
import static org.eclipse.che.api.factory.server.FactoryResolverPriority.LOWEST;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
@ -21,6 +20,7 @@ import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.FactoryResolverPriority;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
@ -34,6 +34,7 @@ import org.eclipse.che.api.factory.shared.dto.ScmInfoDto;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/**
* Provides Factory Parameters resolver for Git Ssh repositories.
@ -41,7 +42,10 @@ import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
* @author Anatolii Bazko
*/
@Singleton
public class GitSshFactoryParametersResolver implements FactoryParametersResolver {
public class GitSshFactoryParametersResolver extends BaseFactoryParameterResolver
implements FactoryParametersResolver {
private static final String PROVIDER_NAME = "git-ssh";
private final GitSshURLParser gitSshURLParser;
@ -54,7 +58,9 @@ public class GitSshFactoryParametersResolver implements FactoryParametersResolve
GitSshURLParser gitSshURLParser,
URLFetcher urlFetcher,
URLFactoryBuilder urlFactoryBuilder,
PersonalAccessTokenManager personalAccessTokenManager) {
PersonalAccessTokenManager personalAccessTokenManager,
AuthorisationRequestManager authorisationRequestManager) {
super(authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
this.gitSshURLParser = gitSshURLParser;
this.urlFetcher = urlFetcher;
this.urlFactoryBuilder = urlFactoryBuilder;
@ -67,22 +73,23 @@ public class GitSshFactoryParametersResolver implements FactoryParametersResolve
&& gitSshURLParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
}
@Override
public String getProviderName() {
return PROVIDER_NAME;
}
@Override
public FactoryMetaDto createFactory(@NotNull final Map<String, String> factoryParameters)
throws ApiException {
// no need to check null value of url parameter as accept() method has performed the check
final GitSshUrl gitSshUrl = gitSshURLParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
gitSshUrl,
new GitSshAuthorizingFileContentProvider(
gitSshUrl, urlFetcher, personalAccessTokenManager),
extractOverrideParams(factoryParameters),
true)
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(new GitSshFactoryVisitor(gitSshUrl));
return super.createFactory(
factoryParameters,
gitSshUrl,
new GitSshFactoryVisitor(gitSshUrl),
new GitSshAuthorizingFileContentProvider(
gitSshUrl, urlFetcher, personalAccessTokenManager));
}
/**

View File

@ -11,10 +11,9 @@
*/
package org.eclipse.che.api.factory.server.github;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static java.util.Collections.emptyMap;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.security.oauth1.OAuthAuthenticationService.ERROR_QUERY_NAME;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
@ -22,6 +21,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
@ -35,6 +35,7 @@ import org.eclipse.che.api.factory.shared.dto.ScmInfoDto;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/**
* Provides Factory Parameters resolver for github repositories.
@ -42,7 +43,10 @@ import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
* @author Florent Benoit
*/
@Singleton
public class GithubFactoryParametersResolver implements FactoryParametersResolver {
public class GithubFactoryParametersResolver extends BaseFactoryParameterResolver
implements FactoryParametersResolver {
private static final String PROVIDER_NAME = "github";
/** Parser which will allow to check validity of URLs and create objects. */
private final GithubURLParser githubUrlParser;
@ -64,9 +68,11 @@ public class GithubFactoryParametersResolver implements FactoryParametersResolve
GithubURLParser githubUrlParser,
URLFetcher urlFetcher,
GithubSourceStorageBuilder githubSourceStorageBuilder,
AuthorisationRequestManager authorisationRequestManager,
URLFactoryBuilder urlFactoryBuilder,
ProjectConfigDtoMerger projectConfigDtoMerger,
PersonalAccessTokenManager personalAccessTokenManager) {
super(authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
this.githubUrlParser = githubUrlParser;
this.urlFetcher = urlFetcher;
this.githubSourceStorageBuilder = githubSourceStorageBuilder;
@ -89,6 +95,11 @@ public class GithubFactoryParametersResolver implements FactoryParametersResolve
&& githubUrlParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
}
@Override
public String getProviderName() {
return PROVIDER_NAME;
}
/**
* Create factory object based on provided parameters
*
@ -98,28 +109,21 @@ public class GithubFactoryParametersResolver implements FactoryParametersResolve
@Override
public FactoryMetaDto createFactory(@NotNull final Map<String, String> factoryParameters)
throws ApiException {
boolean skipAuthentication =
factoryParameters.get(ERROR_QUERY_NAME) != null
&& factoryParameters.get(ERROR_QUERY_NAME).equals("access_denied");
// no need to check null value of url parameter as accept() method has performed the check
final GithubUrl githubUrl;
if (skipAuthentication) {
if (getSkipAuthorisation(factoryParameters)) {
githubUrl =
githubUrlParser.parseWithoutAuthentication(factoryParameters.get(URL_PARAMETER_NAME));
} else {
githubUrl = githubUrlParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
}
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
githubUrl,
new GithubAuthorizingFileContentProvider(
githubUrl, urlFetcher, personalAccessTokenManager),
extractOverrideParams(factoryParameters),
skipAuthentication)
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(new GithubFactoryVisitor(githubUrl));
return createFactory(
factoryParameters,
githubUrl,
new GithubFactoryVisitor(githubUrl),
new GithubAuthorizingFileContentProvider(
githubUrl, urlFetcher, personalAccessTokenManager));
}
/**
@ -182,6 +186,10 @@ public class GithubFactoryParametersResolver implements FactoryParametersResolve
@Override
public RemoteFactoryUrl parseFactoryUrl(String factoryUrl) throws ApiException {
return githubUrlParser.parse(factoryUrl);
if (getSkipAuthorisation(emptyMap())) {
return githubUrlParser.parseWithoutAuthentication(factoryUrl);
} else {
return githubUrlParser.parse(factoryUrl);
}
}
}

View File

@ -19,13 +19,13 @@ import javax.inject.Named;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.api.factory.server.scm.AbstractGitUserDataFetcher;
import org.eclipse.che.api.factory.server.scm.GitUserData;
import org.eclipse.che.api.factory.server.scm.OAuthTokenFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.scm.exception.ScmBadRequestException;
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.security.oauth.OAuthAPI;
/** GitHub user data retriever. */
public class GithubUserDataFetcher extends AbstractGitUserDataFetcher {
@ -44,7 +44,7 @@ public class GithubUserDataFetcher extends AbstractGitUserDataFetcher {
public GithubUserDataFetcher(
@Named("che.api") String apiEndpoint,
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint,
OAuthTokenFetcher oAuthTokenFetcher,
OAuthAPI oAuthTokenFetcher,
PersonalAccessTokenManager personalAccessTokenManager) {
this(
apiEndpoint,
@ -56,7 +56,7 @@ public class GithubUserDataFetcher extends AbstractGitUserDataFetcher {
/** Constructor used for testing only. */
public GithubUserDataFetcher(
String apiEndpoint,
OAuthTokenFetcher oAuthTokenFetcher,
OAuthAPI oAuthTokenFetcher,
PersonalAccessTokenManager personalAccessTokenManager,
GithubApiClient githubApiClient) {
super(OAUTH_PROVIDER_NAME, personalAccessTokenManager, oAuthTokenFetcher);

View File

@ -51,6 +51,7 @@ import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
@ -88,6 +89,7 @@ public class GithubFactoryParametersResolverTest {
private URLFactoryBuilder urlFactoryBuilder;
@Mock private PersonalAccessTokenManager personalAccessTokenManager;
@Mock private AuthorisationRequestManager authorisationRequestManager;
/**
* Capturing the location parameter when calling {@link
@ -113,6 +115,7 @@ public class GithubFactoryParametersResolverTest {
githubUrlParser,
urlFetcher,
githubSourceStorageBuilder,
authorisationRequestManager,
urlFactoryBuilder,
projectConfigDtoMerger,
personalAccessTokenManager);
@ -351,6 +354,50 @@ public class GithubFactoryParametersResolverTest {
eq(true));
}
@Test
public void shouldParseFactoryUrlWithAuthentication() throws Exception {
// given
githubUrlParser = mock(GithubURLParser.class);
githubFactoryParametersResolver =
new GithubFactoryParametersResolver(
githubUrlParser,
urlFetcher,
githubSourceStorageBuilder,
authorisationRequestManager,
urlFactoryBuilder,
projectConfigDtoMerger,
personalAccessTokenManager);
when(authorisationRequestManager.isStored(eq("github"))).thenReturn(true);
// when
githubFactoryParametersResolver.parseFactoryUrl("url");
// then
verify(githubUrlParser).parseWithoutAuthentication("url");
verify(githubUrlParser, never()).parse("url");
}
@Test
public void shouldParseFactoryUrlWithOutAuthentication() throws Exception {
// given
githubUrlParser = mock(GithubURLParser.class);
githubFactoryParametersResolver =
new GithubFactoryParametersResolver(
githubUrlParser,
urlFetcher,
githubSourceStorageBuilder,
authorisationRequestManager,
urlFactoryBuilder,
projectConfigDtoMerger,
personalAccessTokenManager);
when(authorisationRequestManager.isStored(eq("github"))).thenReturn(false);
// when
githubFactoryParametersResolver.parseFactoryUrl("url");
// then
verify(githubUrlParser).parse("url");
verify(githubUrlParser, never()).parseWithoutAuthentication("url");
}
private FactoryDto generateDevfileFactory() {
return newDto(FactoryDto.class)
.withV(CURRENT_VERSION)

View File

@ -28,8 +28,8 @@ import com.github.tomakehurst.wiremock.common.Slf4jNotifier;
import com.google.common.net.HttpHeaders;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.api.factory.server.scm.GitUserData;
import org.eclipse.che.api.factory.server.scm.OAuthTokenFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.security.oauth.OAuthAPI;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.AfterMethod;
@ -40,7 +40,7 @@ import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class GithubGitUserDataFetcherTest {
@Mock OAuthTokenFetcher oAuthTokenFetcher;
@Mock OAuthAPI oAuthTokenFetcher;
@Mock PersonalAccessTokenManager personalAccessTokenManager;
GithubUserDataFetcher githubGUDFetcher;

View File

@ -11,10 +11,8 @@
*/
package org.eclipse.che.api.factory.server.gitlab;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.security.oauth1.OAuthAuthenticationService.ERROR_QUERY_NAME;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
@ -22,6 +20,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
@ -34,6 +33,7 @@ import org.eclipse.che.api.factory.shared.dto.ScmInfoDto;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/**
* Provides Factory Parameters resolver for Gitlab repositories.
@ -41,7 +41,10 @@ import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
* @author Max Shaposhnyk
*/
@Singleton
public class GitlabFactoryParametersResolver implements FactoryParametersResolver {
public class GitlabFactoryParametersResolver extends BaseFactoryParameterResolver
implements FactoryParametersResolver {
private static final String PROVIDER_NAME = "gitlab";
private final URLFactoryBuilder urlFactoryBuilder;
private final URLFetcher urlFetcher;
@ -53,7 +56,9 @@ public class GitlabFactoryParametersResolver implements FactoryParametersResolve
URLFactoryBuilder urlFactoryBuilder,
URLFetcher urlFetcher,
GitlabUrlParser gitlabURLParser,
PersonalAccessTokenManager personalAccessTokenManager) {
PersonalAccessTokenManager personalAccessTokenManager,
AuthorisationRequestManager authorisationRequestManager) {
super(authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
this.urlFactoryBuilder = urlFactoryBuilder;
this.urlFetcher = urlFetcher;
this.gitlabURLParser = gitlabURLParser;
@ -73,6 +78,11 @@ public class GitlabFactoryParametersResolver implements FactoryParametersResolve
&& gitlabURLParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
}
@Override
public String getProviderName() {
return PROVIDER_NAME;
}
/**
* Create factory object based on provided parameters
*
@ -84,19 +94,13 @@ public class GitlabFactoryParametersResolver implements FactoryParametersResolve
throws ApiException {
// no need to check null value of url parameter as accept() method has performed the check
final GitlabUrl gitlabUrl = gitlabURLParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
boolean skipAuthentication =
factoryParameters.get(ERROR_QUERY_NAME) != null
&& factoryParameters.get(ERROR_QUERY_NAME).equals("access_denied");
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
gitlabUrl,
new GitlabAuthorizingFileContentProvider(
gitlabUrl, urlFetcher, personalAccessTokenManager),
extractOverrideParams(factoryParameters),
skipAuthentication)
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(new GitlabFactoryVisitor(gitlabUrl));
return createFactory(
factoryParameters,
gitlabUrl,
new GitlabFactoryVisitor(gitlabUrl),
new GitlabAuthorizingFileContentProvider(
gitlabUrl, urlFetcher, personalAccessTokenManager));
}
/**

View File

@ -29,6 +29,7 @@ import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.lang.StringUtils;
import org.eclipse.che.inject.ConfigurationException;
import org.eclipse.che.security.oauth.OAuthAPI;
/** Gitlab OAuth token retriever. */
public class GitlabUserDataFetcher extends AbstractGitUserDataFetcher {
@ -48,7 +49,7 @@ public class GitlabUserDataFetcher extends AbstractGitUserDataFetcher {
@Nullable @Named("che.integration.gitlab.oauth_endpoint") String oauthEndpoint,
@Named("che.api") String apiEndpoint,
PersonalAccessTokenManager personalAccessTokenManager,
OAuthTokenFetcher oAuthTokenFetcher) {
OAuthAPI oAuthTokenFetcher) {
super(OAUTH_PROVIDER_NAME, personalAccessTokenManager, oAuthTokenFetcher);
this.apiEndpoint = apiEndpoint;
if (gitlabEndpoints != null) {

View File

@ -45,6 +45,7 @@ import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
@ -68,6 +69,8 @@ public class GitlabFactoryParametersResolverTest {
GitlabUrlParser gitlabUrlParser;
@Mock private PersonalAccessTokenManager personalAccessTokenManager;
@Mock private AuthorisationRequestManager authorisationRequestManager;
private GitlabFactoryParametersResolver gitlabFactoryParametersResolver;
@BeforeMethod
@ -80,7 +83,11 @@ public class GitlabFactoryParametersResolverTest {
assertNotNull(this.gitlabUrlParser);
gitlabFactoryParametersResolver =
new GitlabFactoryParametersResolver(
urlFactoryBuilder, urlFetcher, gitlabUrlParser, personalAccessTokenManager);
urlFactoryBuilder,
urlFetcher,
gitlabUrlParser,
personalAccessTokenManager,
authorisationRequestManager);
assertNotNull(this.gitlabFactoryParametersResolver);
}

View File

@ -28,8 +28,8 @@ import com.github.tomakehurst.wiremock.common.Slf4jNotifier;
import com.google.common.net.HttpHeaders;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.api.factory.server.scm.GitUserData;
import org.eclipse.che.api.factory.server.scm.OAuthTokenFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.security.oauth.OAuthAPI;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.AfterMethod;
@ -40,7 +40,7 @@ import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class GitlabUserDataFetcherTest {
@Mock OAuthTokenFetcher oAuthTokenFetcher;
@Mock OAuthAPI oAuthTokenFetcher;
@Mock PersonalAccessTokenManager personalAccessTokenManager;
GitlabUserDataFetcher gitlabUserDataFetcher;

View File

@ -62,6 +62,10 @@
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth-shared</artifactId>

View File

@ -0,0 +1,121 @@
/*
* 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.api.factory.server;
import static java.util.stream.Collectors.toMap;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto;
import org.eclipse.che.api.factory.shared.dto.FactoryVisitor;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
public class BaseFactoryParameterResolver {
private final AuthorisationRequestManager authorisationRequestManager;
private final URLFactoryBuilder urlFactoryBuilder;
private final String providerName;
public BaseFactoryParameterResolver(
AuthorisationRequestManager authorisationRequestManager,
URLFactoryBuilder urlFactoryBuilder,
String providerName) {
this.authorisationRequestManager = authorisationRequestManager;
this.urlFactoryBuilder = urlFactoryBuilder;
this.providerName = providerName;
}
protected FactoryMetaDto createFactory(
Map<String, String> factoryParameters,
RemoteFactoryUrl factoryUrl,
FactoryVisitor factoryVisitor,
FileContentProvider contentProvider)
throws ApiException {
// create factory from the following location if location exists, else create default factory
return urlFactoryBuilder
.createFactoryFromDevfile(
factoryUrl,
contentProvider,
extractOverrideParams(factoryParameters),
getSkipAuthorisation(factoryParameters))
.orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo"))
.acceptVisitor(factoryVisitor);
}
protected boolean getSkipAuthorisation(Map<String, String> factoryParameters) {
String errorCode = "error_code";
boolean stored = authorisationRequestManager.isStored(providerName);
boolean skipAuthentication =
factoryParameters.get(errorCode) != null
&& factoryParameters.get(errorCode).equals("access_denied")
|| stored;
if (skipAuthentication && !stored) {
authorisationRequestManager.store(providerName);
}
return skipAuthentication;
}
/**
* Returns priority of the resolver. Resolvers with higher priority will be used among matched
* resolvers.
*/
public FactoryResolverPriority priority() {
return FactoryResolverPriority.DEFAULT;
}
/**
* Finds and returns devfile override parameters in general factory parameters map.
*
* @param factoryParameters map containing factory data parameters provided through URL
* @return filtered devfile values override map
*/
protected Map<String, String> extractOverrideParams(Map<String, String> factoryParameters) {
String overridePrefix = "override.";
return factoryParameters.entrySet().stream()
.filter(e -> e.getKey().startsWith(overridePrefix))
.collect(toMap(e -> e.getKey().substring(overridePrefix.length()), Map.Entry::getValue));
}
/**
* If devfile has no projects, put there one provided by given `projectSupplier`. Otherwise update
* all projects with given `projectModifier`.
*
* @param devfile of the projects to update
* @param projectSupplier provides default project
* @param projectModifier updates existing projects
*/
protected void updateProjects(
DevfileDto devfile,
Supplier<ProjectDto> projectSupplier,
Consumer<ProjectDto> projectModifier) {
List<ProjectDto> projects = devfile.getProjects();
if (projects.isEmpty()) {
devfile.setProjects(Collections.singletonList(projectSupplier.get()));
} else {
// update existing project with same repository, set current branch if needed
projects.forEach(projectModifier);
}
}
}

View File

@ -11,20 +11,12 @@
*/
package org.eclipse.che.api.factory.server;
import static java.util.stream.Collectors.toMap;
import jakarta.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
/**
* Defines a resolver that will produce factories for some parameters
@ -41,6 +33,9 @@ public interface FactoryParametersResolver {
*/
boolean accept(@NotNull Map<String, String> factoryParameters);
/** Returns the name of the provider */
String getProviderName();
/**
* Create factory object based on provided parameters
*
@ -62,41 +57,5 @@ public interface FactoryParametersResolver {
* Returns priority of the resolver. Resolvers with higher priority will be used among matched
* resolvers.
*/
default FactoryResolverPriority priority() {
return FactoryResolverPriority.DEFAULT;
}
/**
* Finds and returns devfile override parameters in general factory parameters map.
*
* @param factoryParameters map containing factory data parameters provided through URL
* @return filtered devfile values override map
*/
default Map<String, String> extractOverrideParams(Map<String, String> factoryParameters) {
String overridePrefix = "override.";
return factoryParameters.entrySet().stream()
.filter(e -> e.getKey().startsWith(overridePrefix))
.collect(toMap(e -> e.getKey().substring(overridePrefix.length()), Map.Entry::getValue));
}
/**
* If devfile has no projects, put there one provided by given `projectSupplier`. Otherwise update
* all projects with given `projectModifier`.
*
* @param devfile of the projects to update
* @param projectSupplier provides default project
* @param projectModifier updates existing projects
*/
default void updateProjects(
DevfileDto devfile,
Supplier<ProjectDto> projectSupplier,
Consumer<ProjectDto> projectModifier) {
List<ProjectDto> projects = devfile.getProjects();
if (projects.isEmpty()) {
devfile.setProjects(Collections.singletonList(projectSupplier.get()));
} else {
// update existing project with same repository, set current branch if needed
projects.forEach(projectModifier);
}
}
FactoryResolverPriority priority();
}

View File

@ -42,6 +42,7 @@ import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException
import org.eclipse.che.api.factory.server.scm.exception.UnknownScmProviderException;
import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException;
import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/**
* Defines Factory REST API.
@ -64,17 +65,20 @@ public class FactoryService extends Service {
private final FactoryParametersResolverHolder factoryParametersResolverHolder;
private final AdditionalFilenamesProvider additionalFilenamesProvider;
private final PersonalAccessTokenManager personalAccessTokenManager;
private final AuthorisationRequestManager authorisationRequestManager;
@Inject
public FactoryService(
FactoryAcceptValidator acceptValidator,
FactoryParametersResolverHolder factoryParametersResolverHolder,
AdditionalFilenamesProvider additionalFilenamesProvider,
PersonalAccessTokenManager personalAccessTokenManager) {
PersonalAccessTokenManager personalAccessTokenManager,
AuthorisationRequestManager authorisationRequestManager) {
this.acceptValidator = acceptValidator;
this.factoryParametersResolverHolder = factoryParametersResolverHolder;
this.additionalFilenamesProvider = additionalFilenamesProvider;
this.personalAccessTokenManager = personalAccessTokenManager;
this.authorisationRequestManager = authorisationRequestManager;
}
@POST
@ -147,8 +151,10 @@ public class FactoryService extends Service {
FactoryParametersResolver factoryParametersResolver =
factoryParametersResolverHolder.getFactoryParametersResolver(
singletonMap(URL_PARAMETER_NAME, url));
personalAccessTokenManager.getAndStore(
factoryParametersResolver.parseFactoryUrl(url).getHostName());
if (!authorisationRequestManager.isStored(factoryParametersResolver.getProviderName())) {
personalAccessTokenManager.getAndStore(
factoryParametersResolver.parseFactoryUrl(url).getHostName());
}
} catch (ScmCommunicationException
| ScmConfigurationPersistenceException
| UnknownScmProviderException

View File

@ -36,7 +36,10 @@ import org.eclipse.che.api.workspace.server.devfile.URLFileContentProvider;
* {@link FactoryParametersResolver} implementation to resolve factory based on url parameter as a
* direct URL to a devfile content. Extracts and applies devfile values override parameters.
*/
public class RawDevfileUrlFactoryParameterResolver implements FactoryParametersResolver {
public class RawDevfileUrlFactoryParameterResolver extends BaseFactoryParameterResolver
implements FactoryParametersResolver {
private static final String PROVIDER_NAME = "raw-devfile-url";
protected final URLFactoryBuilder urlFactoryBuilder;
protected final URLFetcher urlFetcher;
@ -44,6 +47,7 @@ public class RawDevfileUrlFactoryParameterResolver implements FactoryParametersR
@Inject
public RawDevfileUrlFactoryParameterResolver(
URLFactoryBuilder urlFactoryBuilder, URLFetcher urlFetcher) {
super(null, urlFactoryBuilder, PROVIDER_NAME);
this.urlFactoryBuilder = urlFactoryBuilder;
this.urlFetcher = urlFetcher;
}
@ -61,6 +65,11 @@ public class RawDevfileUrlFactoryParameterResolver implements FactoryParametersR
return !isNullOrEmpty(url) && (url.endsWith(".yaml") || url.endsWith(".yml"));
}
@Override
public String getProviderName() {
return PROVIDER_NAME;
}
/**
* Creates factory based on provided parameters. Presumes url parameter as direct URL to a devfile
* content.

View File

@ -17,6 +17,7 @@ import org.eclipse.che.api.core.*;
import org.eclipse.che.api.factory.server.scm.exception.*;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.security.oauth.OAuthAPI;
/**
* Abstraction to fetch git user data from the specific git provider using OAuth 2.0 or personal
@ -27,12 +28,12 @@ import org.eclipse.che.commons.subject.Subject;
public abstract class AbstractGitUserDataFetcher implements GitUserDataFetcher {
protected final String oAuthProviderName;
protected final PersonalAccessTokenManager personalAccessTokenManager;
protected final OAuthTokenFetcher oAuthTokenFetcher;
protected final OAuthAPI oAuthTokenFetcher;
public AbstractGitUserDataFetcher(
String oAuthProviderName,
PersonalAccessTokenManager personalAccessTokenManager,
OAuthTokenFetcher oAuthTokenFetcher) {
OAuthAPI oAuthTokenFetcher) {
this.oAuthProviderName = oAuthProviderName;
this.personalAccessTokenManager = personalAccessTokenManager;
this.oAuthTokenFetcher = oAuthTokenFetcher;

View File

@ -1,34 +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.api.factory.server.scm;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.api.core.*;
/**
* OAuth 2.0 token fetcher is designed to use a dependency in che-core-api-factory module. It is
* needed to avoid circular dependency between che-core-api-factory and che-core-api-auth modules.
*
* @author Anatolii Bazko
*/
public interface OAuthTokenFetcher {
/**
* Fetches OAuth token for the given OAuth provider name.
*
* @param oAuthProviderName OAuth provider name (e.g. github, bitbucket)
* @return OAuth token
*/
OAuthToken getToken(String oAuthProviderName)
throws NotFoundException, UnauthorizedException, ServerException, ForbiddenException,
BadRequestException, ConflictException;
}

View File

@ -0,0 +1,76 @@
/*
* 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.api.factory.server;
import static java.util.Collections.emptyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(value = {MockitoTestNGListener.class})
public class BaseFactoryParameterResolverTest {
@Mock private AuthorisationRequestManager authorisationRequestManager;
@Mock private URLFactoryBuilder urlFactoryBuilder;
private static final String PROVIDER_NAME = "test";
private BaseFactoryParameterResolver baseFactoryParameterResolver;
@BeforeMethod
protected void init() throws Exception {
baseFactoryParameterResolver =
new BaseFactoryParameterResolver(
authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
}
@Test
public void shouldReturnFalseOnGetSkipAuthorisation() {
// given
when(authorisationRequestManager.isStored(eq(PROVIDER_NAME))).thenReturn(false);
// when
boolean result = baseFactoryParameterResolver.getSkipAuthorisation(emptyMap());
// then
assertFalse(result);
}
@Test
public void shouldReturnTrueOnGetSkipAuthorisation() {
// given
when(authorisationRequestManager.isStored(eq(PROVIDER_NAME))).thenReturn(true);
// when
boolean result = baseFactoryParameterResolver.getSkipAuthorisation(emptyMap());
// then
assertTrue(result);
}
@Test
public void shouldReturnTrueOnGetSkipAuthorisationFromFactoryParams() {
// given
when(authorisationRequestManager.isStored(eq(PROVIDER_NAME))).thenReturn(false);
// when
boolean result =
baseFactoryParameterResolver.getSkipAuthorisation(Map.of("error_code", "access_denied"));
// then
assertTrue(result);
}
}

View File

@ -32,6 +32,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
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.verify;
import static org.mockito.Mockito.when;
@ -62,6 +63,7 @@ import org.eclipse.che.api.user.server.model.impl.UserImpl;
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.security.oauth.AuthorisationRequestManager;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
@ -99,6 +101,7 @@ public class FactoryServiceTest {
@Mock private AdditionalFilenamesProvider additionalFilenamesProvider;
@Mock private RawDevfileUrlFactoryParameterResolver rawDevfileUrlFactoryParameterResolver;
@Mock private PersonalAccessTokenManager personalAccessTokenManager;
@Mock private AuthorisationRequestManager authorisationRequestManager;
@InjectMocks private FactoryParametersResolverHolder factoryParametersResolverHolder;
private Set<FactoryParametersResolver> specificFactoryParametersResolvers;
@ -136,7 +139,8 @@ public class FactoryServiceTest {
acceptValidator,
factoryParametersResolverHolder,
additionalFilenamesProvider,
personalAccessTokenManager);
personalAccessTokenManager,
authorisationRequestManager);
}
@Filter
@ -157,7 +161,11 @@ public class FactoryServiceTest {
// service instance with dummy holder
service =
new FactoryService(
acceptValidator, dummyHolder, additionalFilenamesProvider, personalAccessTokenManager);
acceptValidator,
dummyHolder,
additionalFilenamesProvider,
personalAccessTokenManager,
authorisationRequestManager);
// when
final Map<String, String> map = new HashMap<>();
@ -184,7 +192,11 @@ public class FactoryServiceTest {
// service instance with dummy holder
service =
new FactoryService(
acceptValidator, dummyHolder, additionalFilenamesProvider, personalAccessTokenManager);
acceptValidator,
dummyHolder,
additionalFilenamesProvider,
personalAccessTokenManager,
authorisationRequestManager);
// invalid factory
final String invalidFactoryMessage = "invalid factory";
@ -232,7 +244,11 @@ public class FactoryServiceTest {
doReturn(factoryParametersResolver).when(dummyHolder).getFactoryParametersResolver(anyMap());
service =
new FactoryService(
acceptValidator, dummyHolder, additionalFilenamesProvider, personalAccessTokenManager);
acceptValidator,
dummyHolder,
additionalFilenamesProvider,
personalAccessTokenManager,
authorisationRequestManager);
// when
given()
@ -245,6 +261,32 @@ public class FactoryServiceTest {
verify(personalAccessTokenManager).getAndStore(eq("hostName"));
}
@Test
public void shouldNotRefreshTokenIfAuthorisationRejected() throws Exception {
// given
final FactoryParametersResolverHolder dummyHolder = spy(factoryParametersResolverHolder);
FactoryParametersResolver factoryParametersResolver = mock(FactoryParametersResolver.class);
doReturn(factoryParametersResolver).when(dummyHolder).getFactoryParametersResolver(anyMap());
when(authorisationRequestManager.isStored(any())).thenReturn(true);
service =
new FactoryService(
acceptValidator,
dummyHolder,
additionalFilenamesProvider,
personalAccessTokenManager,
authorisationRequestManager);
// when
given()
.contentType(ContentType.JSON)
.when()
.queryParam("url", "someUrl")
.post(SERVICE_PATH + "/token/refresh");
// then
verify(personalAccessTokenManager, never()).getAndStore(eq("hostName"));
}
@Test
public void shouldThrowBadRequestWhenRefreshTokenWithoutUrl() throws Exception {
service =
@ -252,7 +294,8 @@ public class FactoryServiceTest {
acceptValidator,
factoryParametersResolverHolder,
additionalFilenamesProvider,
personalAccessTokenManager);
personalAccessTokenManager,
authorisationRequestManager);
// when
final Response response =