From 46114a4d8f69f55bd2a840c015fdf025b4d6021f Mon Sep 17 00:00:00 2001 From: ivinokur Date: Tue, 27 Aug 2024 15:27:58 +0300 Subject: [PATCH] Intercept ScmComunicationException on workspace start --- .../KubernetesPersonalAccessTokenManager.java | 9 ++-- .../CredentialsSecretConfigurator.java | 6 ++- .../OAuthTokenSecretsConfigurator.java | 9 +++- .../che/security/oauth/EmbeddedOAuthAPI.java | 3 +- ...AzureDevOpsPersonalAccessTokenFetcher.java | 5 ++- ...ucketServerPersonalAccessTokenFetcher.java | 5 ++- .../bitbucket/BitbucketServerURLParser.java | 2 +- .../server/bitbucket/BitbucketApiClient.java | 21 +++++++--- .../BitbucketPersonalAccessTokenFetcher.java | 10 +++-- ...tbucketPersonalAccessTokenFetcherTest.java | 6 +-- ...tractGithubPersonalAccessTokenFetcher.java | 10 +++-- .../github/AbstractGithubURLParser.java | 23 +++++----- .../github/AbstractGithubUserDataFetcher.java | 7 +++- .../server/github/GithubApiClient.java | 18 +++++--- .../GithubPersonalAccessTokenFetcherTest.java | 6 +-- .../server/gitlab/GitlabApiClient.java | 16 ++++--- .../gitlab/GitlabOAuthTokenFetcher.java | 12 ++++-- .../server/gitlab/GitlabUrlParser.java | 10 ++--- .../server/gitlab/GitlabUserDataFetcher.java | 7 +++- .../factory/server/ApiExceptionMapper.java | 42 +++++++++++++++---- .../api/factory/server/FactoryService.java | 9 ++-- .../scm/AbstractGitUserDataFetcher.java | 6 ++- .../scm/PersonalAccessTokenFetcher.java | 4 +- .../scm/PersonalAccessTokenManager.java | 6 ++- .../scm/ScmPersonalAccessTokenFetcher.java | 2 +- 25 files changed, 171 insertions(+), 83 deletions(-) diff --git a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesPersonalAccessTokenManager.java b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesPersonalAccessTokenManager.java index 0b501873364..e9ad48dc315 100644 --- a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesPersonalAccessTokenManager.java +++ b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesPersonalAccessTokenManager.java @@ -152,7 +152,7 @@ public PersonalAccessToken fetchAndSave(Subject cheUser, String scmServerUrl) @Override public Optional get(Subject cheUser, String scmServerUrl) - throws ScmConfigurationPersistenceException { + throws ScmConfigurationPersistenceException, ScmCommunicationException { return doGetPersonalAccessTokens(cheUser, null, scmServerUrl).stream().findFirst(); } @@ -174,13 +174,13 @@ public PersonalAccessToken get(String scmServerUrl) @Override public Optional get( Subject cheUser, String oAuthProviderName, @Nullable String scmServerUrl) - throws ScmConfigurationPersistenceException { + throws ScmConfigurationPersistenceException, ScmCommunicationException { return doGetPersonalAccessTokens(cheUser, oAuthProviderName, scmServerUrl).stream().findFirst(); } private List doGetPersonalAccessTokens( Subject cheUser, @Nullable String oAuthProviderName, @Nullable String scmServerUrl) - throws ScmConfigurationPersistenceException { + throws ScmConfigurationPersistenceException, ScmCommunicationException { List result = new ArrayList<>(); try { LOG.debug( @@ -358,7 +358,8 @@ public void forceRefreshPersonalAccessToken(String scmServerUrl) } private void removePreviousTokensIfPresent(Subject subject, String scmServerUrl) - throws ScmConfigurationPersistenceException, UnsatisfiedScmPreconditionException { + throws ScmConfigurationPersistenceException, UnsatisfiedScmPreconditionException, + ScmCommunicationException { List personalAccessTokens = doGetPersonalAccessTokens(subject, null, scmServerUrl); for (int i = 1; i < personalAccessTokens.size(); i++) { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/CredentialsSecretConfigurator.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/CredentialsSecretConfigurator.java index cf52218b624..a33edb2a921 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/CredentialsSecretConfigurator.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/CredentialsSecretConfigurator.java @@ -25,6 +25,8 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This {@link NamespaceConfigurator} ensures that Secret {@link @@ -45,6 +47,8 @@ public class CredentialsSecretConfigurator implements NamespaceConfigurator { private static final String MERGED_GIT_CREDENTIALS_SECRET_NAME = "devworkspace-merged-git-credentials"; + private static final Logger LOG = LoggerFactory.getLogger(CredentialsSecretConfigurator.class); + @Inject public CredentialsSecretConfigurator( CheServerKubernetesClientFactory cheServerKubernetesClientFactory, @@ -79,7 +83,7 @@ public void configure(NamespaceResolutionContext namespaceResolutionContext, Str | ScmConfigurationPersistenceException | UnsatisfiedScmPreconditionException | ScmUnauthorizedException e) { - throw new RuntimeException(e); + LOG.error(e.getMessage(), e); } }); } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/OAuthTokenSecretsConfigurator.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/OAuthTokenSecretsConfigurator.java index 0ab31f71223..5739b26c38e 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/OAuthTokenSecretsConfigurator.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/OAuthTokenSecretsConfigurator.java @@ -17,12 +17,15 @@ import javax.inject.Singleton; import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher; import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager; +import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException; import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.subject.Subject; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Ensures that OAuth token that are represented by Kubernetes Secrets are valid. @@ -44,6 +47,8 @@ public class OAuthTokenSecretsConfigurator implements NamespaceConfigurator { "app.kubernetes.io/part-of", "che.eclipse.org", "app.kubernetes.io/component", "scm-personal-access-token"); + private static final Logger LOG = LoggerFactory.getLogger(OAuthTokenSecretsConfigurator.class); + @Inject public OAuthTokenSecretsConfigurator( CheServerKubernetesClientFactory cheServerKubernetesClientFactory, @@ -74,8 +79,8 @@ public void configure(NamespaceResolutionContext namespaceResolutionContext, Str Subject cheSubject = EnvironmentContext.getCurrent().getSubject(); personalAccessTokenManager.get( cheSubject, s.getMetadata().getAnnotations().get(ANNOTATION_SCM_URL)); - } catch (ScmConfigurationPersistenceException e) { - throw new RuntimeException(e); + } catch (ScmConfigurationPersistenceException | ScmCommunicationException e) { + LOG.error(e.getMessage(), e); } }); } diff --git a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java index c31ca3f06df..0a7f008b623 100644 --- a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java +++ b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java @@ -43,6 +43,7 @@ import org.eclipse.che.api.core.util.LinksHelper; 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.ScmCommunicationException; import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException; import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException; import org.eclipse.che.commons.annotation.Nullable; @@ -237,7 +238,7 @@ public OAuthToken getOrRefreshToken(String oauthProvider) if (tokenOptional.isPresent()) { return newDto(OAuthToken.class).withToken(tokenOptional.get().getToken()); } - } catch (ScmConfigurationPersistenceException e) { + } catch (ScmConfigurationPersistenceException | ScmCommunicationException e) { throw new RuntimeException(e); } } diff --git a/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsPersonalAccessTokenFetcher.java index 5721dbf3af6..2c671c33ac7 100644 --- a/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsPersonalAccessTokenFetcher.java +++ b/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsPersonalAccessTokenFetcher.java @@ -169,7 +169,8 @@ public Optional isValid(PersonalAccessToken personalAccessToken) { } @Override - public Optional> isValid(PersonalAccessTokenParams params) { + public Optional> isValid(PersonalAccessTokenParams params) + throws ScmCommunicationException { if (!isValidScmServerUrl(params.getScmProviderUrl())) { LOG.debug("not a valid url {} for current fetcher ", params.getScmProviderUrl()); return Optional.empty(); @@ -183,7 +184,7 @@ public Optional> isValid(PersonalAccessTokenParams params) user = azureDevOpsApiClient.getUserWithPAT(params.getToken(), params.getOrganization()); } return Optional.of(Pair.of(Boolean.TRUE, user.getEmailAddress())); - } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) { + } catch (ScmItemNotFoundException | ScmBadRequestException e) { return Optional.empty(); } } diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java index 11484956988..5229fd58774 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java @@ -158,7 +158,8 @@ public Optional isValid(PersonalAccessToken accessToken) } @Override - public Optional> isValid(PersonalAccessTokenParams params) { + public Optional> isValid(PersonalAccessTokenParams params) + throws ScmCommunicationException { if (!bitbucketServerApiClient.isConnected(params.getScmProviderUrl())) { // If BitBucket oAuth is not configured check the manually added user namespace token. HttpBitbucketServerApiClient apiClient = @@ -180,7 +181,7 @@ public Optional> isValid(PersonalAccessTokenParams params) try { BitbucketUser user = bitbucketServerApiClient.getUser(params.getToken()); return Optional.of(Pair.of(Boolean.TRUE, user.getName())); - } catch (ScmItemNotFoundException | ScmUnauthorizedException | ScmCommunicationException e) { + } catch (ScmItemNotFoundException | ScmUnauthorizedException e) { return Optional.empty(); } } diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java index f551cdce423..93e84f6c794 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java @@ -96,7 +96,7 @@ private boolean isUserTokenPresent(String repositoryUrl) { Optional token = personalAccessTokenManager.get(EnvironmentContext.getCurrent().getSubject(), serverUrl); return token.isPresent() && token.get().getScmTokenName().equals(OAUTH_PROVIDER_NAME); - } catch (ScmConfigurationPersistenceException exception) { + } catch (ScmConfigurationPersistenceException | ScmCommunicationException exception) { return false; } } diff --git a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketApiClient.java b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketApiClient.java index 1b0d72c2a0f..b598efb7cb0 100644 --- a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketApiClient.java +++ b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketApiClient.java @@ -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/ @@ -15,6 +15,7 @@ import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import static java.net.HttpURLConnection.HTTP_NO_CONTENT; import static java.net.HttpURLConnection.HTTP_OK; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static java.time.Duration.ofSeconds; import com.fasterxml.jackson.databind.ObjectMapper; @@ -37,6 +38,7 @@ 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.api.factory.server.scm.exception.ScmUnauthorizedException; import org.eclipse.che.commons.lang.Pair; import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; import org.slf4j.Logger; @@ -100,7 +102,8 @@ public BitbucketApiClient() { * @throws ScmBadRequestException */ public BitbucketUser getUser(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = apiServerUrl.resolve("user"); HttpRequest request = buildBitbucketApiRequest(uri, authenticationToken); LOG.trace("executeRequest={}", request); @@ -120,7 +123,8 @@ public BitbucketUser getUser(String authenticationToken) public String getFileContent( String workspace, String repository, String source, String path, String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = apiServerUrl.resolve( String.format("repositories/%s/%s/src/%s/%s", workspace, repository, source, path)); @@ -148,7 +152,8 @@ public String getFileContent( * @throws ScmBadRequestException */ public BitbucketUserEmail getEmail(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = apiServerUrl.resolve("user/emails"); HttpRequest request = buildBitbucketApiRequest(uri, authenticationToken); LOG.trace("executeRequest={}", request); @@ -174,7 +179,8 @@ public BitbucketUserEmail getEmail(String authenticationToken) * scopes. */ public Pair getTokenScopes(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = apiServerUrl.resolve("user"); HttpRequest request = buildBitbucketApiRequest(uri, authenticationToken); LOG.trace("executeRequest={}", request); @@ -212,7 +218,8 @@ private T executeRequest( HttpClient httpClient, HttpRequest request, Function, T> responseConverter) - throws ScmBadRequestException, ScmItemNotFoundException, ScmCommunicationException { + throws ScmBadRequestException, ScmItemNotFoundException, ScmCommunicationException, + ScmUnauthorizedException { try { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); @@ -228,6 +235,8 @@ private T executeRequest( throw new ScmBadRequestException(body); case HTTP_NOT_FOUND: throw new ScmItemNotFoundException(body); + case HTTP_UNAUTHORIZED: + throw new ScmUnauthorizedException(body, "github", "v1", ""); default: throw new ScmCommunicationException( "Unexpected status code " + response.statusCode() + " " + response.toString()); diff --git a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcher.java index 5ad3380f31f..460fc95949b 100644 --- a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcher.java +++ b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcher.java @@ -166,13 +166,17 @@ public Optional isValid(PersonalAccessToken personalAccessToken) { try { String[] scopes = bitbucketApiClient.getTokenScopes(personalAccessToken.getToken()).second; return Optional.of(isValidScope(Sets.newHashSet(scopes))); - } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) { + } catch (ScmItemNotFoundException + | ScmCommunicationException + | ScmBadRequestException + | ScmUnauthorizedException e) { return Optional.of(Boolean.FALSE); } } @Override - public Optional> isValid(PersonalAccessTokenParams params) { + public Optional> isValid(PersonalAccessTokenParams params) + throws ScmCommunicationException { if (!bitbucketApiClient.isConnected(params.getScmProviderUrl())) { LOG.debug("not a valid url {} for current fetcher ", params.getScmProviderUrl()); return Optional.empty(); @@ -184,7 +188,7 @@ public Optional> isValid(PersonalAccessTokenParams params) Pair.of( isValidScope(Sets.newHashSet(pair.second)) ? Boolean.TRUE : Boolean.FALSE, pair.first)); - } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) { + } catch (ScmItemNotFoundException | ScmBadRequestException | ScmUnauthorizedException e) { return Optional.empty(); } } diff --git a/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcherTest.java b/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcherTest.java index 04ffb68bbf4..6bd77b2d3b9 100644 --- a/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcherTest.java +++ b/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketPersonalAccessTokenFetcherTest.java @@ -17,7 +17,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static java.net.HttpURLConnection.HTTP_FORBIDDEN; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX; import static org.eclipse.che.dto.server.DtoFactory.newDto; import static org.mockito.ArgumentMatchers.anyString; @@ -211,7 +211,7 @@ public void shouldValidateOauthToken() throws Exception { @Test public void shouldNotValidateExpiredOauthToken() throws Exception { - stubFor(get(urlEqualTo("/user")).willReturn(aResponse().withStatus(HTTP_FORBIDDEN))); + stubFor(get(urlEqualTo("/user")).willReturn(aResponse().withStatus(HTTP_UNAUTHORIZED))); PersonalAccessTokenParams params = new PersonalAccessTokenParams( @@ -235,7 +235,7 @@ public void shouldThrowUnauthorizedExceptionIfTokenIsNotValid() throws Exception stubFor( get(urlEqualTo("/user")) .withHeader(HttpHeaders.AUTHORIZATION, equalTo("token " + bitbucketOauthToken)) - .willReturn(aResponse().withStatus(HTTP_FORBIDDEN))); + .willReturn(aResponse().withStatus(HTTP_UNAUTHORIZED))); bitbucketPersonalAccessTokenFetcher.fetchPersonalAccessToken( subject, BitbucketApiClient.BITBUCKET_SERVER); diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubPersonalAccessTokenFetcher.java index df0f70c64ff..36495295170 100644 --- a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubPersonalAccessTokenFetcher.java +++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubPersonalAccessTokenFetcher.java @@ -215,13 +215,17 @@ public Optional isValid(PersonalAccessToken personalAccessToken) { return Optional.of(Boolean.FALSE); } } - } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) { + } catch (ScmItemNotFoundException + | ScmCommunicationException + | ScmBadRequestException + | ScmUnauthorizedException e) { return Optional.of(Boolean.FALSE); } } @Override - public Optional> isValid(PersonalAccessTokenParams params) { + public Optional> isValid(PersonalAccessTokenParams params) + throws ScmCommunicationException { GithubApiClient apiClient; if (githubApiClient.isConnected(params.getScmProviderUrl())) { // The url from the token has the same url as the api client, no need to create a new one. @@ -247,7 +251,7 @@ public Optional> isValid(PersonalAccessTokenParams params) GithubUser user = apiClient.getUser(params.getToken()); return Optional.of(Pair.of(Boolean.TRUE, user.getLogin())); } - } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) { + } catch (ScmItemNotFoundException | ScmBadRequestException | ScmUnauthorizedException e) { return Optional.empty(); } } diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubURLParser.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubURLParser.java index b1747d5663d..46e65e0c648 100644 --- a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubURLParser.java +++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubURLParser.java @@ -14,7 +14,6 @@ import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; -import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static java.util.regex.Pattern.compile; import static org.eclipse.che.api.factory.server.ApiExceptionMapper.toApiException; import static org.eclipse.che.api.factory.server.github.GithubApiClient.GITHUB_SAAS_ENDPOINT; @@ -121,7 +120,7 @@ private boolean isUserTokenPresent(String repositoryUrl) { PersonalAccessToken accessToken = token.get(); return accessToken.getScmTokenName().equals(providerName); } - } catch (ScmConfigurationPersistenceException exception) { + } catch (ScmConfigurationPersistenceException | ScmCommunicationException exception) { return false; } } @@ -137,14 +136,16 @@ private boolean isApiRequestRelevant(String repositoryUrl) { // If the user request catches the unauthorised error, it means that the provided url // belongs to GitHub. githubApiClient.getUser(""); - } catch (ScmCommunicationException e) { - return e.getStatusCode() == HTTP_UNAUTHORIZED - // Check the error message as well, because other providers might also return 401 - // for such requests. - && e.getMessage().contains("Requires authentication") + } catch (ScmUnauthorizedException e) { + // Check the error message as well, because other providers might also return 401 + // for such requests. + return e.getMessage().contains("Requires authentication") || // for older GitHub Enterprise versions e.getMessage().contains("Must authenticate to access this API."); - } catch (ScmItemNotFoundException | ScmBadRequestException | IllegalArgumentException e) { + } catch (ScmItemNotFoundException + | ScmBadRequestException + | IllegalArgumentException + | ScmCommunicationException e) { return false; } } @@ -289,7 +290,8 @@ private GithubPullRequest getPullRequest( return apiClient.getPullRequest(pullRequestId, repoUser, repoName, null); } catch (ScmItemNotFoundException | ScmCommunicationException - | ScmBadRequestException exception) { + | ScmBadRequestException + | ScmUnauthorizedException exception) { LOG.error("Failed to authenticate to GitHub", e); } @@ -340,7 +342,8 @@ private GithubCommit getLatestCommit( } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException - | URISyntaxException exception) { + | URISyntaxException + | ScmUnauthorizedException exception) { LOG.error("Failed to authenticate to GitHub", e); } } catch (ScmCommunicationException diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubUserDataFetcher.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubUserDataFetcher.java index 1ce559486dd..54c8ddb45c6 100644 --- a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubUserDataFetcher.java +++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubUserDataFetcher.java @@ -23,6 +23,7 @@ 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.api.factory.server.scm.exception.ScmUnauthorizedException; /** GitHub user data retriever. */ public abstract class AbstractGithubUserDataFetcher extends AbstractGitUserDataFetcher { @@ -53,7 +54,8 @@ public AbstractGithubUserDataFetcher( @Override protected GitUserData fetchGitUserDataWithOAuthToken(String token) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { GithubUser user = githubApiClient.getUser(token); if (isNullOrEmpty(user.getName()) || isNullOrEmpty(user.getEmail())) { throw new ScmItemNotFoundException(NO_USERNAME_AND_EMAIL_ERROR_MESSAGE); @@ -65,7 +67,8 @@ protected GitUserData fetchGitUserDataWithOAuthToken(String token) @Override protected GitUserData fetchGitUserDataWithPersonalAccessToken( PersonalAccessToken personalAccessToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { GithubApiClient apiClient = githubApiClient.isConnected(personalAccessToken.getScmProviderUrl()) ? githubApiClient diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java index 57907c1275b..9a45d973f54 100644 --- a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java +++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java @@ -16,6 +16,7 @@ import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import static java.net.HttpURLConnection.HTTP_NO_CONTENT; import static java.net.HttpURLConnection.HTTP_OK; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static java.time.Duration.ofSeconds; import static org.eclipse.che.commons.lang.StringUtils.trimEnd; @@ -40,6 +41,7 @@ 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.api.factory.server.scm.exception.ScmUnauthorizedException; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.lang.Pair; import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; @@ -103,7 +105,8 @@ public GithubApiClient(@Nullable String serverUrl) { * @throws ScmBadRequestException */ public GithubUser getUser(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = apiServerUrl.resolve("./user"); HttpRequest request = buildGithubApiRequest(uri, authenticationToken); LOG.trace("executeRequest={}", request); @@ -133,7 +136,8 @@ public GithubUser getUser(String authenticationToken) */ public GithubPullRequest getPullRequest( String id, String username, String repoName, String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = apiServerUrl.resolve(String.format("./repos/%s/%s/pulls/%s", username, repoName, id)); HttpRequest request = buildGithubApiRequest(uri, authenticationToken); @@ -167,7 +171,7 @@ public GithubPullRequest getPullRequest( public GithubCommit getLatestCommit( String user, String repository, String branch, @Nullable String authenticationToken) throws ScmBadRequestException, ScmItemNotFoundException, ScmCommunicationException, - URISyntaxException { + URISyntaxException, ScmUnauthorizedException { final URI uri = apiServerUrl.resolve(String.format("./repos/%s/%s/commits", user, repository)); @@ -208,7 +212,8 @@ public GithubCommit getLatestCommit( * @throws ScmBadRequestException */ public Pair getTokenScopes(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = apiServerUrl.resolve("./user"); HttpRequest request = buildGithubApiRequest(uri, authenticationToken); LOG.trace("executeRequest={}", request); @@ -270,7 +275,8 @@ private T executeRequest( HttpClient httpClient, HttpRequest request, Function, T> responseConverter) - throws ScmBadRequestException, ScmItemNotFoundException, ScmCommunicationException { + throws ScmBadRequestException, ScmItemNotFoundException, ScmCommunicationException, + ScmUnauthorizedException { try { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); @@ -290,6 +296,8 @@ private T executeRequest( throw new ScmBadRequestException(body); case HTTP_NOT_FOUND: throw new ScmItemNotFoundException(body); + case HTTP_UNAUTHORIZED: + throw new ScmUnauthorizedException(body, "github", "v1", ""); default: throw new ScmCommunicationException( "Unexpected status code " + statusCode + " " + body, statusCode); diff --git a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcherTest.java b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcherTest.java index 2193a349ec9..2bf561c630a 100644 --- a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcherTest.java +++ b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcherTest.java @@ -17,7 +17,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static java.net.HttpURLConnection.HTTP_FORBIDDEN; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static org.eclipse.che.api.factory.server.github.GithubPersonalAccessTokenFetcher.DEFAULT_TOKEN_SCOPES; import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX; import static org.eclipse.che.dto.server.DtoFactory.newDto; @@ -176,7 +176,7 @@ public void shouldThrowUnauthorizedExceptionIfTokenIsNotValid() throws Exception stubFor( get(urlEqualTo("/api/v3/user")) .withHeader(HttpHeaders.AUTHORIZATION, equalTo("token " + githubOauthToken)) - .willReturn(aResponse().withStatus(HTTP_FORBIDDEN))); + .willReturn(aResponse().withStatus(HTTP_UNAUTHORIZED))); githubPATFetcher.fetchPersonalAccessToken(subject, wireMockServer.url("/")); } @@ -254,7 +254,7 @@ public void shouldValidateOauthToken() throws Exception { @Test public void shouldNotValidateExpiredOauthToken() throws Exception { - stubFor(get(urlEqualTo("/api/v3/user")).willReturn(aResponse().withStatus(HTTP_FORBIDDEN))); + stubFor(get(urlEqualTo("/api/v3/user")).willReturn(aResponse().withStatus(HTTP_UNAUTHORIZED))); PersonalAccessTokenParams params = new PersonalAccessTokenParams( diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabApiClient.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabApiClient.java index 6f2a902e9bf..e7e36049848 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabApiClient.java +++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabApiClient.java @@ -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/ @@ -13,6 +13,7 @@ import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static java.time.Duration.ofSeconds; import com.fasterxml.jackson.databind.ObjectMapper; @@ -33,6 +34,7 @@ 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.api.factory.server.scm.exception.ScmUnauthorizedException; import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +67,8 @@ public GitlabApiClient(String serverUrl) { } public GitlabUser getUser(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { final URI uri = serverUrl.resolve("/api/v4/user"); HttpRequest request = HttpRequest.newBuilder(uri) @@ -88,7 +91,7 @@ public GitlabUser getUser(String authenticationToken) } public GitlabPersonalAccessTokenInfo getPersonalAccessTokenInfo(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmUnauthorizedException { final URI uri = serverUrl.resolve("/api/v4/personal_access_tokens/self"); HttpRequest request = HttpRequest.newBuilder(uri) @@ -115,7 +118,7 @@ public GitlabPersonalAccessTokenInfo getPersonalAccessTokenInfo(String authentic } public GitlabOauthTokenInfo getOAuthTokenInfo(String authenticationToken) - throws ScmItemNotFoundException, ScmCommunicationException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmUnauthorizedException { final URI uri = serverUrl.resolve("/oauth/token/info"); HttpRequest request = HttpRequest.newBuilder(uri) @@ -143,7 +146,8 @@ public GitlabOauthTokenInfo getOAuthTokenInfo(String authenticationToken) private T executeRequest( HttpClient httpClient, HttpRequest request, Function bodyConverter) - throws ScmBadRequestException, ScmItemNotFoundException, ScmCommunicationException { + throws ScmBadRequestException, ScmItemNotFoundException, ScmCommunicationException, + ScmUnauthorizedException { try { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); @@ -159,6 +163,8 @@ private T executeRequest( throw new ScmBadRequestException(body); case HTTP_NOT_FOUND: throw new ScmItemNotFoundException(body); + case HTTP_UNAUTHORIZED: + throw new ScmUnauthorizedException(body, "github", "v1", ""); default: throw new ScmCommunicationException( "Unexpected status code " + response.statusCode() + " " + response, diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabOAuthTokenFetcher.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabOAuthTokenFetcher.java index c5e59cd2d95..9444afff638 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabOAuthTokenFetcher.java +++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabOAuthTokenFetcher.java @@ -188,7 +188,7 @@ public Optional isValid(PersonalAccessToken personalAccessToken) { GitlabOauthTokenInfo info = gitlabApiClient.getOAuthTokenInfo(personalAccessToken.getToken()); return Optional.of(Sets.newHashSet(info.getScope()).containsAll(DEFAULT_TOKEN_SCOPES)); - } catch (ScmItemNotFoundException | ScmCommunicationException e) { + } catch (ScmItemNotFoundException | ScmCommunicationException | ScmUnauthorizedException e) { return Optional.of(Boolean.FALSE); } } else { @@ -201,14 +201,18 @@ public Optional isValid(PersonalAccessToken personalAccessToken) { } else { return Optional.of(Boolean.FALSE); } - } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) { + } catch (ScmItemNotFoundException + | ScmCommunicationException + | ScmBadRequestException + | ScmUnauthorizedException e) { return Optional.of(Boolean.FALSE); } } } @Override - public Optional> isValid(PersonalAccessTokenParams params) { + public Optional> isValid(PersonalAccessTokenParams params) + throws ScmCommunicationException { GitlabApiClient gitlabApiClient = getApiClient(params.getScmProviderUrl()); if (gitlabApiClient == null || !gitlabApiClient.isConnected(params.getScmProviderUrl())) { if (OAUTH_PROVIDER_NAME.equals(params.getScmTokenName())) { @@ -234,7 +238,7 @@ public Optional> isValid(PersonalAccessTokenParams params) // latest GitLab version, we just perform check by accessing something from API. // TODO: add PAT scope validation return Optional.of(Pair.of(Boolean.TRUE, user.getUsername())); - } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) { + } catch (ScmItemNotFoundException | ScmBadRequestException | ScmUnauthorizedException e) { return Optional.empty(); } } diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java index d094b114a70..53faa2663da 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java +++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java @@ -12,7 +12,6 @@ package org.eclipse.che.api.factory.server.gitlab; import static java.lang.String.format; -import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static java.util.regex.Pattern.compile; import static org.eclipse.che.commons.lang.StringUtils.trimEnd; @@ -31,6 +30,7 @@ import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException; import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException; import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException; +import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException; import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.env.EnvironmentContext; @@ -90,7 +90,7 @@ private boolean isUserTokenPresent(String repositoryUrl) { PersonalAccessToken accessToken = token.get(); return accessToken.getScmTokenName().equals(OAUTH_PROVIDER_NAME); } - } catch (ScmConfigurationPersistenceException exception) { + } catch (ScmConfigurationPersistenceException | ScmCommunicationException exception) { return false; } } @@ -115,9 +115,9 @@ private boolean isApiRequestRelevant(String repositoryUrl) { // If the token request catches the unauthorised error, it means that the provided url // belongs to Gitlab. gitlabApiClient.getOAuthTokenInfo(""); - } catch (ScmCommunicationException e) { - return e.getStatusCode() == HTTP_UNAUTHORIZED; - } catch (ScmItemNotFoundException | IllegalArgumentException e) { + } catch (ScmUnauthorizedException e) { + return true; + } catch (ScmItemNotFoundException | IllegalArgumentException | ScmCommunicationException e) { return false; } } diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUserDataFetcher.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUserDataFetcher.java index 15f2cffc7ad..e276d2c6987 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUserDataFetcher.java +++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUserDataFetcher.java @@ -26,6 +26,7 @@ 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.api.factory.server.scm.exception.ScmUnauthorizedException; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.lang.StringUtils; import org.eclipse.che.inject.ConfigurationException; @@ -72,7 +73,8 @@ public GitlabUserDataFetcher( @Override protected GitUserData fetchGitUserDataWithOAuthToken(String token) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { for (String gitlabServerEndpoint : this.registeredGitlabEndpoints) { GitlabUser user = new GitlabApiClient(gitlabServerEndpoint).getUser(token); return new GitUserData(user.getName(), user.getEmail()); @@ -83,7 +85,8 @@ protected GitUserData fetchGitUserDataWithOAuthToken(String token) @Override protected GitUserData fetchGitUserDataWithPersonalAccessToken( PersonalAccessToken personalAccessToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException { + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException { GitlabUser user = new GitlabApiClient(personalAccessToken.getScmProviderUrl()) .getUser(personalAccessToken.getToken()); diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/ApiExceptionMapper.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/ApiExceptionMapper.java index 122c5b4d34f..b8a1c1a3e48 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/ApiExceptionMapper.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/ApiExceptionMapper.java @@ -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,11 +11,15 @@ */ package org.eclipse.che.api.factory.server; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.eclipse.che.dto.server.DtoFactory.newDto; + import java.util.Map; import org.eclipse.che.api.core.ApiException; import org.eclipse.che.api.core.BadRequestException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.UnauthorizedException; +import org.eclipse.che.api.core.rest.shared.dto.ExtendedError; import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException; import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException; import org.eclipse.che.api.factory.server.scm.exception.UnknownScmProviderException; @@ -48,6 +52,16 @@ public static ApiException toApiException(ScmUnauthorizedException scmUnauthoriz + scmUnauthorizedException.getMessage()); } + public static ApiException toApiException(ScmCommunicationException scmCommunicationException) { + ApiException apiException = getApiException(scmCommunicationException); + return (apiException != null) + ? apiException + : new ServerException( + "Error occurred during SCM communication." + + "Cause: " + + scmCommunicationException.getMessage()); + } + public static ApiException toApiException( DevfileException devfileException, DevfileLocation location) { ApiException cause = getApiException(devfileException.getCause()); @@ -70,15 +84,25 @@ private static ApiException getApiException(Throwable throwable) { "oauth_version", scmCause.getOauthVersion(), "oauth_provider", scmCause.getOauthProvider(), "oauth_authentication_url", scmCause.getAuthenticateUrl())); - } else if (throwable instanceof UnknownScmProviderException) { - return new ServerException( - "Provided location is unknown or misconfigured on the server side. Error message: " - + throwable.getMessage()); - } else if (throwable instanceof ScmCommunicationException) { - return new ServerException( - "There is an error happened when communicate with SCM server. Error message: " - + throwable.getMessage()); + } else { + if (throwable instanceof UnknownScmProviderException) { + return new ServerException( + appendErrorMessage( + "Provided location is unknown or misconfigured on the server side", + throwable.getMessage())); + } else if (throwable instanceof ScmCommunicationException) { + return new ServerException( + newDto(ExtendedError.class) + .withMessage( + appendErrorMessage( + "Error occurred during SCM communication.", throwable.getMessage())) + .withErrorCode(404)); + } } return null; } + + private static String appendErrorMessage(String message, String errorMessage) { + return message + (isNullOrEmpty(errorMessage) ? "" : " Error message: " + errorMessage); + } } diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java index 496e2c16264..86aef36214f 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/FactoryService.java @@ -159,13 +159,14 @@ public void refreshToken(@Parameter(description = "Factory url") @QueryParam("ur personalAccessTokenManager.getAndStore(scmServerUrl); } } - } catch (ScmCommunicationException - | ScmConfigurationPersistenceException - | UnknownScmProviderException - | UnsatisfiedScmPreconditionException e) { + } catch (ScmConfigurationPersistenceException | UnsatisfiedScmPreconditionException e) { throw new ApiException(e); } catch (ScmUnauthorizedException e) { throw toApiException(e); + } catch (ScmCommunicationException e) { + throw toApiException(e); + } catch (UnknownScmProviderException e) { + // ignore the exception as it is not a problem if the provider from the given URL is unknown } } diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AbstractGitUserDataFetcher.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AbstractGitUserDataFetcher.java index 8b1b94d27a9..21d92a937a8 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AbstractGitUserDataFetcher.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AbstractGitUserDataFetcher.java @@ -56,11 +56,13 @@ public GitUserData fetchGitUserData() } protected abstract GitUserData fetchGitUserDataWithOAuthToken(String token) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException; + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException; protected abstract GitUserData fetchGitUserDataWithPersonalAccessToken( PersonalAccessToken personalAccessToken) - throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException; + throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException, + ScmUnauthorizedException; protected abstract String getLocalAuthenticateUrl(); } diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenFetcher.java index da86a930bbd..0abd3e095cb 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenFetcher.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenFetcher.java @@ -75,6 +75,8 @@ Optional isValid(PersonalAccessToken personalAccessToken) * the token has expected scope of permissions, false if the token scopes does not match the * expected ones. Empty optional if {@link PersonalAccessTokenFetcher} is not able to confirm * or deny that token is valid. + * @throws ScmCommunicationException - problem occurred during communication with SCM server. */ - Optional> isValid(PersonalAccessTokenParams params); + Optional> isValid(PersonalAccessTokenParams params) + throws ScmCommunicationException; } diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenManager.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenManager.java index 9bbe0bd9ad2..dab000515a5 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenManager.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/PersonalAccessTokenManager.java @@ -48,9 +48,10 @@ PersonalAccessToken fetchAndSave(Subject cheUser, String scmServerUrl) * @return personal access token * @throws ScmConfigurationPersistenceException - problem occurred during communication with * permanent storage. + * @throws ScmCommunicationException - problem occurred during communication with SCM server. */ Optional get(Subject cheUser, String scmServerUrl) - throws ScmConfigurationPersistenceException; + throws ScmConfigurationPersistenceException, ScmCommunicationException; /** * Gets {@link PersonalAccessToken} from permanent storage. @@ -79,10 +80,11 @@ PersonalAccessToken get(String scmServerUrl) * @return personal access token * @throws ScmConfigurationPersistenceException - problem occurred during communication with * permanent storage. + * @throws ScmCommunicationException - problem occurred during communication with SCM server. */ Optional get( Subject cheUser, String oAuthProviderName, @Nullable String scmServerUrl) - throws ScmConfigurationPersistenceException; + throws ScmConfigurationPersistenceException, ScmCommunicationException; /** * Gets {@link PersonalAccessToken} from permanent storage. If the token is not found try to fetch diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/ScmPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/ScmPersonalAccessTokenFetcher.java index f276c055043..cb863226f78 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/ScmPersonalAccessTokenFetcher.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/ScmPersonalAccessTokenFetcher.java @@ -94,7 +94,7 @@ public boolean isValid(PersonalAccessToken personalAccessToken) * fetchers return an scm username, return it. Otherwise, return null. */ public Optional getScmUsername(PersonalAccessTokenParams params) - throws UnknownScmProviderException { + throws UnknownScmProviderException, ScmCommunicationException { for (PersonalAccessTokenFetcher fetcher : personalAccessTokenFetchers) { Optional> isValid = fetcher.isValid(params); if (isValid.isPresent() && isValid.get().first) {