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 84e25709b3..6e3faed9a7 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 @@ -130,7 +130,8 @@ public class GitlabApiClient { throw new ScmItemNotFoundException(body); default: throw new ScmCommunicationException( - "Unexpected status code " + response.statusCode() + " " + response.toString()); + "Unexpected status code " + response.statusCode() + " " + response, + response.statusCode()); } } } catch (IOException | InterruptedException | UncheckedIOException e) { 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 d831e0509f..2609a0bc39 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,6 +12,7 @@ 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 com.google.common.base.Splitter; @@ -27,6 +28,7 @@ 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.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; @@ -92,10 +94,29 @@ public class GitlabUrlParser { } public boolean isValid(@NotNull String url) { - // If Gitlab URL is not configured, try to find it in a manually added user namespace - // token. return gitlabUrlPatterns.stream().anyMatch(pattern -> pattern.matcher(url).matches()) - || isUserTokenPresent(url); + // If the Gitlab URL is not configured, try to find it in a manually added user namespace + // token. + || isUserTokenPresent(url) + // Try to call an API request to see if the URL matches Gitlab. + || isApiRequestRelevant(url); + } + + private boolean isApiRequestRelevant(String repositoryUrl) { + Optional serverUrlOptional = getServerUrl(repositoryUrl); + if (serverUrlOptional.isPresent()) { + GitlabApiClient gitlabApiClient = new GitlabApiClient(serverUrlOptional.get()); + try { + // If the token request catches the unauthorised error, it means that the provided url + // belongs to Gitlab. + gitlabApiClient.getTokenInfo(""); + } catch (ScmCommunicationException e) { + return e.getStatusCode() == HTTP_UNAUTHORIZED; + } catch (ScmItemNotFoundException e) { + return false; + } + } + return false; } private Optional getPatternMatcherByUrl(String url) { diff --git a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabFactoryParametersResolverTest.java b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabFactoryParametersResolverTest.java index 17702b4412..59a6a2b9f0 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabFactoryParametersResolverTest.java +++ b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabFactoryParametersResolverTest.java @@ -87,7 +87,8 @@ public class GitlabFactoryParametersResolverTest { /** Check url which is not a Gitlab url can't be accepted by this resolver */ @Test public void checkInvalidAcceptUrl() { - Map parameters = singletonMap(URL_PARAMETER_NAME, "http://github.com"); + Map parameters = + singletonMap(URL_PARAMETER_NAME, "http://github.com/user/repo"); // shouldn't be accepted assertFalse(gitlabFactoryParametersResolver.accept(parameters)); } diff --git a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabScmFileResolverTest.java b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabScmFileResolverTest.java index 6c7974e7cc..f2dccda7ca 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabScmFileResolverTest.java +++ b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabScmFileResolverTest.java @@ -58,7 +58,7 @@ public class GitlabScmFileResolverTest { @Test public void checkInvalidAcceptUrl() { // shouldn't be accepted - assertFalse(gitlabScmFileResolver.accept("http://github.com")); + assertFalse(gitlabScmFileResolver.accept("http://github.com/user/repo")); } /** Check Gitlab url will be be accepted by this resolver */ diff --git a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java index a9d77020bc..94931742b8 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java +++ b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java @@ -11,14 +11,24 @@ */ package org.eclipse.che.api.factory.server.gitlab; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +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 org.mockito.Mockito.mock; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.common.Slf4jNotifier; import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager; import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Listeners; @@ -32,6 +42,18 @@ public class GitlabUrlParserTest { /** Instance of component that will be tested. */ private GitlabUrlParser gitlabUrlParser; + private WireMockServer wireMockServer; + private WireMock wireMock; + + @BeforeClass + public void prepare() { + wireMockServer = + new WireMockServer(wireMockConfig().notifier(new Slf4jNotifier(false)).dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + wireMock = new WireMock("localhost", wireMockServer.port()); + } + @BeforeMethod public void setUp() { gitlabUrlParser = @@ -60,6 +82,32 @@ public class GitlabUrlParserTest { assertEquals(gitlabUrl.getSubfolder(), subfolder); } + @Test + public void shouldValidateUrlByApiRequest() { + // given + String url = wireMockServer.url("/user/repo"); + stubFor(get(urlEqualTo("/oauth/token/info")).willReturn(aResponse().withStatus(401))); + + // when + boolean result = gitlabUrlParser.isValid(url); + + // then + assertTrue(result); + } + + @Test + public void shouldNotValidateUrlByApiRequest() { + // given + String url = wireMockServer.url("/user/repo"); + stubFor(get(urlEqualTo("/oauth/token/info")).willReturn(aResponse().withStatus(500))); + + // when + boolean result = gitlabUrlParser.isValid(url); + + // then + assertFalse(result); + } + @DataProvider(name = "UrlsProvider") public Object[][] urls() { return new Object[][] { diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/exception/ScmCommunicationException.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/exception/ScmCommunicationException.java index 757569c62e..6bfd7fa31f 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/exception/ScmCommunicationException.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/exception/ScmCommunicationException.java @@ -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/ @@ -13,11 +13,22 @@ package org.eclipse.che.api.factory.server.scm.exception; /** Thrown when problem occurred during communication with scm provider */ public class ScmCommunicationException extends Exception { + private int statusCode; + public ScmCommunicationException(String message) { super(message); } + public ScmCommunicationException(String message, int statusCode) { + super(message); + this.statusCode = statusCode; + } + public ScmCommunicationException(String message, Throwable cause) { super(message, cause); } + + public int getStatusCode() { + return statusCode; + } }