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
parent
5a601bb840
commit
756342b0c0
|
|
@ -130,7 +130,8 @@ public class GitlabApiClient {
|
||||||
throw new ScmItemNotFoundException(body);
|
throw new ScmItemNotFoundException(body);
|
||||||
default:
|
default:
|
||||||
throw new ScmCommunicationException(
|
throw new ScmCommunicationException(
|
||||||
"Unexpected status code " + response.statusCode() + " " + response.toString());
|
"Unexpected status code " + response.statusCode() + " " + response,
|
||||||
|
response.statusCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException | InterruptedException | UncheckedIOException e) {
|
} catch (IOException | InterruptedException | UncheckedIOException e) {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
package org.eclipse.che.api.factory.server.gitlab;
|
package org.eclipse.che.api.factory.server.gitlab;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
|
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
|
||||||
import static java.util.regex.Pattern.compile;
|
import static java.util.regex.Pattern.compile;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
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.PersonalAccessTokenManager;
|
||||||
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
|
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.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.scm.exception.ScmUnauthorizedException;
|
||||||
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
||||||
import org.eclipse.che.commons.annotation.Nullable;
|
import org.eclipse.che.commons.annotation.Nullable;
|
||||||
|
|
@ -92,10 +94,29 @@ public class GitlabUrlParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValid(@NotNull String url) {
|
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())
|
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) {
|
private Optional<Matcher> getPatternMatcherByUrl(String url) {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,8 @@ public class GitlabFactoryParametersResolverTest {
|
||||||
/** Check url which is not a Gitlab url can't be accepted by this resolver */
|
/** Check url which is not a Gitlab url can't be accepted by this resolver */
|
||||||
@Test
|
@Test
|
||||||
public void checkInvalidAcceptUrl() {
|
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
|
// shouldn't be accepted
|
||||||
assertFalse(gitlabFactoryParametersResolver.accept(parameters));
|
assertFalse(gitlabFactoryParametersResolver.accept(parameters));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ public class GitlabScmFileResolverTest {
|
||||||
@Test
|
@Test
|
||||||
public void checkInvalidAcceptUrl() {
|
public void checkInvalidAcceptUrl() {
|
||||||
// shouldn't be accepted
|
// 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 */
|
/** Check Gitlab url will be be accepted by this resolver */
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,24 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.che.api.factory.server.gitlab;
|
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.mockito.Mockito.mock;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertFalse;
|
||||||
import static org.testng.Assert.assertTrue;
|
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.scm.PersonalAccessTokenManager;
|
||||||
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.testng.MockitoTestNGListener;
|
import org.mockito.testng.MockitoTestNGListener;
|
||||||
|
import org.testng.annotations.BeforeClass;
|
||||||
import org.testng.annotations.BeforeMethod;
|
import org.testng.annotations.BeforeMethod;
|
||||||
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.DataProvider;
|
||||||
import org.testng.annotations.Listeners;
|
import org.testng.annotations.Listeners;
|
||||||
|
|
@ -32,6 +42,18 @@ public class GitlabUrlParserTest {
|
||||||
/** Instance of component that will be tested. */
|
/** Instance of component that will be tested. */
|
||||||
private GitlabUrlParser gitlabUrlParser;
|
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
|
@BeforeMethod
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
gitlabUrlParser =
|
gitlabUrlParser =
|
||||||
|
|
@ -60,6 +82,32 @@ public class GitlabUrlParserTest {
|
||||||
assertEquals(gitlabUrl.getSubfolder(), subfolder);
|
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")
|
@DataProvider(name = "UrlsProvider")
|
||||||
public Object[][] urls() {
|
public Object[][] urls() {
|
||||||
return new Object[][] {
|
return new Object[][] {
|
||||||
|
|
|
||||||
|
|
@ -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
|
* This program and the accompanying materials are made
|
||||||
* available under the terms of the Eclipse Public License 2.0
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
* which is available at https://www.eclipse.org/legal/epl-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 */
|
/** Thrown when problem occurred during communication with scm provider */
|
||||||
public class ScmCommunicationException extends Exception {
|
public class ScmCommunicationException extends Exception {
|
||||||
|
private int statusCode;
|
||||||
|
|
||||||
public ScmCommunicationException(String message) {
|
public ScmCommunicationException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScmCommunicationException(String message, int statusCode) {
|
||||||
|
super(message);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
public ScmCommunicationException(String message, Throwable cause) {
|
public ScmCommunicationException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getStatusCode() {
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue