Apply Gitlab server url validation by testing an API request (#421)

Support factory from public Gitlab-server repository without any oAuth configuration in the Che side. Add a new check that detects a Gitlab server url by testing it by a Gitlab Api request.
pull/422/head
Igor Vinokur 2023-01-19 14:07:50 +02:00 committed by GitHub
parent 5a601bb840
commit 756342b0c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 7 deletions

View File

@ -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) {

View File

@ -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<String> 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<Matcher> getPatternMatcherByUrl(String url) {

View File

@ -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<String, String> parameters = singletonMap(URL_PARAMETER_NAME, "http://github.com");
Map<String, String> parameters =
singletonMap(URL_PARAMETER_NAME, "http://github.com/user/repo");
// shouldn't be accepted
assertFalse(gitlabFactoryParametersResolver.accept(parameters));
}

View File

@ -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 */

View File

@ -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[][] {

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/
@ -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;
}
}