chore: Add an ability to manually configure git provider oAuth token (#313)
Read user namespace oauth token before checking the oAuth configuration in case when user manually added a bitbucket / github / gitlab oAuth secret to the user namespace.pull/318/head
parent
73b48498ff
commit
a47f115553
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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,7 +15,7 @@ import java.net.URL;
|
|||
|
||||
/**
|
||||
* Dummy implementation of @{@link OAuthAuthenticator} used in the case if no Bitbucket Server
|
||||
* integration is configured.
|
||||
* integration is configured to register an empty @{@link BitbucketServerApiClient}.
|
||||
*/
|
||||
public class NoopOAuthAuthenticator extends OAuthAuthenticator {
|
||||
public NoopOAuthAuthenticator() {
|
||||
|
|
@ -49,7 +49,6 @@ public class NoopOAuthAuthenticator extends OAuthAuthenticator {
|
|||
|
||||
@Override
|
||||
public String getLocalAuthenticateUrl() {
|
||||
throw new RuntimeException(
|
||||
"The fallback noop authenticator cannot be used for authentication. Make sure OAuth is properly configured.");
|
||||
return "Noop URL";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -25,6 +25,7 @@ import javax.inject.Named;
|
|||
import org.eclipse.che.api.factory.server.bitbucket.server.BitbucketPersonalAccessToken;
|
||||
import org.eclipse.che.api.factory.server.bitbucket.server.BitbucketServerApiClient;
|
||||
import org.eclipse.che.api.factory.server.bitbucket.server.BitbucketUser;
|
||||
import org.eclipse.che.api.factory.server.bitbucket.server.HttpBitbucketServerApiClient;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher;
|
||||
import org.eclipse.che.api.factory.server.scm.exception.ScmBadRequestException;
|
||||
|
|
@ -33,6 +34,7 @@ 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.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.subject.Subject;
|
||||
import org.eclipse.che.security.oauth1.NoopOAuthAuthenticator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -104,8 +106,20 @@ public class BitbucketServerPersonalAccessTokenFetcher implements PersonalAccess
|
|||
public Optional<Boolean> isValid(PersonalAccessToken personalAccessToken)
|
||||
throws ScmCommunicationException, ScmUnauthorizedException {
|
||||
if (!bitbucketServerApiClient.isConnected(personalAccessToken.getScmProviderUrl())) {
|
||||
LOG.debug("not a valid url {} for current fetcher ", personalAccessToken.getScmProviderUrl());
|
||||
return Optional.empty();
|
||||
// If BitBucket oAuth is not configured check the manually added user namespace token.
|
||||
HttpBitbucketServerApiClient apiClient =
|
||||
new HttpBitbucketServerApiClient(
|
||||
personalAccessToken.getScmProviderUrl(), new NoopOAuthAuthenticator());
|
||||
try {
|
||||
apiClient.getUser(personalAccessToken.getScmUserName(), personalAccessToken.getToken());
|
||||
return Optional.of(Boolean.TRUE);
|
||||
} catch (ScmItemNotFoundException
|
||||
| ScmUnauthorizedException
|
||||
| ScmCommunicationException exception) {
|
||||
LOG.debug(
|
||||
"not a valid url {} for current fetcher ", personalAccessToken.getScmProviderUrl());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
try {
|
||||
BitbucketPersonalAccessToken bitbucketPersonalAccessToken =
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -17,13 +17,20 @@ import com.google.common.base.Splitter;
|
|||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
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.ScmUnauthorizedException;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -35,15 +42,19 @@ import org.eclipse.che.commons.lang.StringUtils;
|
|||
public class BitbucketURLParser {
|
||||
|
||||
private final DevfileFilenamesProvider devfileFilenamesProvider;
|
||||
private final PersonalAccessTokenManager personalAccessTokenManager;
|
||||
private static final String bitbucketUrlPatternTemplate =
|
||||
"^(?<host>%s)/scm/(?<project>[^/]++)/(?<repo>[^.]++).git(\\?at=)?(?<branch>[\\w\\d-_]*)";
|
||||
private final List<Pattern> bitbucketUrlPatterns = new ArrayList<>();
|
||||
private static final String OAUTH_PROVIDER_NAME = "bitbucket-server";
|
||||
|
||||
@Inject
|
||||
public BitbucketURLParser(
|
||||
@Nullable @Named("che.integration.bitbucket.server_endpoints") String bitbucketEndpoints,
|
||||
DevfileFilenamesProvider devfileFilenamesProvider) {
|
||||
DevfileFilenamesProvider devfileFilenamesProvider,
|
||||
PersonalAccessTokenManager personalAccessTokenManager) {
|
||||
this.devfileFilenamesProvider = devfileFilenamesProvider;
|
||||
this.personalAccessTokenManager = personalAccessTokenManager;
|
||||
if (bitbucketEndpoints != null) {
|
||||
for (String bitbucketEndpoint : Splitter.on(",").split(bitbucketEndpoints)) {
|
||||
String trimmedEndpoint = StringUtils.trimEnd(bitbucketEndpoint, '/');
|
||||
|
|
@ -53,9 +64,42 @@ public class BitbucketURLParser {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isUserTokenPresent(String repositoryUrl) {
|
||||
String serverUrl = getServerUrl(repositoryUrl);
|
||||
if (Pattern.compile(format(bitbucketUrlPatternTemplate, serverUrl))
|
||||
.matcher(repositoryUrl)
|
||||
.matches()) {
|
||||
try {
|
||||
Optional<PersonalAccessToken> token =
|
||||
personalAccessTokenManager.get(EnvironmentContext.getCurrent().getSubject(), serverUrl);
|
||||
return token.isPresent() && token.get().getScmTokenName().equals(OAUTH_PROVIDER_NAME);
|
||||
} catch (ScmConfigurationPersistenceException
|
||||
| ScmUnauthorizedException
|
||||
| ScmCommunicationException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isValid(@NotNull String url) {
|
||||
return !bitbucketUrlPatterns.isEmpty()
|
||||
&& bitbucketUrlPatterns.stream().anyMatch(pattern -> pattern.matcher(url).matches());
|
||||
if (!bitbucketUrlPatterns.isEmpty()) {
|
||||
return bitbucketUrlPatterns.stream().anyMatch(pattern -> pattern.matcher(url).matches());
|
||||
} else {
|
||||
// If Bitbucket server URL is not configured try to find it in a manually added user namespace
|
||||
// token.
|
||||
return isUserTokenPresent(url);
|
||||
}
|
||||
}
|
||||
|
||||
private String getServerUrl(String repositoryUrl) {
|
||||
return repositoryUrl.substring(
|
||||
0,
|
||||
repositoryUrl.indexOf("/scm") > 0 ? repositoryUrl.indexOf("/scm") : repositoryUrl.length());
|
||||
}
|
||||
|
||||
private Matcher getPatternMatcherByUrl(String url) {
|
||||
return Pattern.compile(format(bitbucketUrlPatternTemplate, getServerUrl(url))).matcher(url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -66,6 +110,10 @@ public class BitbucketURLParser {
|
|||
public BitbucketUrl parse(String url) {
|
||||
|
||||
if (bitbucketUrlPatterns.isEmpty()) {
|
||||
Matcher patternMatcher = getPatternMatcherByUrl(url);
|
||||
if (patternMatcher.matches()) {
|
||||
return parse(patternMatcher);
|
||||
}
|
||||
throw new UnsupportedOperationException(
|
||||
"The Bitbucket integration is not configured properly and cannot be used at this moment."
|
||||
+ "Please refer to docs to check the Bitbucket integration instructions");
|
||||
|
|
@ -82,6 +130,10 @@ public class BitbucketURLParser {
|
|||
format(
|
||||
"The given url %s is not a valid Bitbucket server URL. Check either URL or server configuration.",
|
||||
url)));
|
||||
return parse(matcher);
|
||||
}
|
||||
|
||||
private BitbucketUrl parse(Matcher matcher) {
|
||||
String host = matcher.group("host");
|
||||
String project = matcher.group("project");
|
||||
String repoName = matcher.group("repo");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -17,6 +17,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.subject.Subject;
|
||||
|
||||
/** Bitbucket Server API client. */
|
||||
|
|
@ -36,13 +37,14 @@ public interface BitbucketServerApiClient {
|
|||
throws ScmUnauthorizedException, ScmCommunicationException, ScmItemNotFoundException;
|
||||
|
||||
/**
|
||||
* @param slug
|
||||
* @param slug scm username.
|
||||
* @param token token to override. Pass {@code null} to use token from the authentication flow.
|
||||
* @return - Retrieve the {@link BitbucketUser} matching the supplied userSlug.
|
||||
* @throws ScmItemNotFoundException
|
||||
* @throws ScmUnauthorizedException
|
||||
* @throws ScmCommunicationException
|
||||
*/
|
||||
BitbucketUser getUser(String slug)
|
||||
BitbucketUser getUser(String slug, @Nullable String token)
|
||||
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -46,6 +46,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.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler;
|
||||
import org.eclipse.che.commons.subject.Subject;
|
||||
|
|
@ -128,12 +129,16 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public BitbucketUser getUser(String slug)
|
||||
public BitbucketUser getUser(String slug, @Nullable String token)
|
||||
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException {
|
||||
URI uri = serverUri.resolve("/rest/api/1.0/users/" + slug);
|
||||
HttpRequest request =
|
||||
HttpRequest.newBuilder(uri)
|
||||
.headers("Authorization", computeAuthorizationHeader("GET", uri.toString()))
|
||||
.headers(
|
||||
"Authorization",
|
||||
token != null
|
||||
? "Bearer " + token
|
||||
: computeAuthorizationHeader("GET", uri.toString()))
|
||||
.timeout(DEFAULT_HTTP_TIMEOUT)
|
||||
.build();
|
||||
|
||||
|
|
@ -308,7 +313,7 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient {
|
|||
throws ScmCommunicationException, ScmUnauthorizedException, ScmItemNotFoundException {
|
||||
|
||||
for (String userSlug : userSlugs) {
|
||||
BitbucketUser user = getUser(userSlug);
|
||||
BitbucketUser user = getUser(userSlug, null);
|
||||
try {
|
||||
getPersonalAccessTokens(userSlug);
|
||||
return Optional.of(user);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -17,6 +17,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.subject.Subject;
|
||||
|
||||
/**
|
||||
|
|
@ -37,7 +38,7 @@ public class NoopBitbucketServerApiClient implements BitbucketServerApiClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public BitbucketUser getUser(String slug)
|
||||
public BitbucketUser getUser(String slug, @Nullable String token)
|
||||
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException {
|
||||
throw new RuntimeException(
|
||||
"The fallback noop api client cannot be used for real operation. Make sure Bitbucket OAuth1 is properly configured.");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -19,6 +19,7 @@ import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
|||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
|
@ -68,7 +69,10 @@ public class BitbucketServerAuthorizingFactoryParametersResolverTest {
|
|||
@BeforeMethod
|
||||
protected void init() {
|
||||
bitbucketURLParser =
|
||||
new BitbucketURLParser("http://bitbucket.2mcl.com", devfileFilenamesProvider);
|
||||
new BitbucketURLParser(
|
||||
"http://bitbucket.2mcl.com",
|
||||
devfileFilenamesProvider,
|
||||
mock(PersonalAccessTokenManager.class));
|
||||
assertNotNull(this.bitbucketURLParser);
|
||||
bitbucketServerFactoryParametersResolver =
|
||||
new BitbucketServerAuthorizingFactoryParametersResolver(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -14,6 +14,7 @@ package org.eclipse.che.api.factory.server.bitbucket;
|
|||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
|
|
@ -47,7 +48,9 @@ public class BitbucketServerScmFileResolverTest {
|
|||
|
||||
@BeforeMethod
|
||||
protected void init() {
|
||||
bitbucketURLParser = new BitbucketURLParser(SCM_URL, devfileFilenamesProvider);
|
||||
bitbucketURLParser =
|
||||
new BitbucketURLParser(
|
||||
SCM_URL, devfileFilenamesProvider, mock(PersonalAccessTokenManager.class));
|
||||
assertNotNull(this.bitbucketURLParser);
|
||||
serverScmFileResolver =
|
||||
new BitbucketServerScmFileResolver(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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,9 +11,11 @@
|
|||
*/
|
||||
package org.eclipse.che.api.factory.server.bitbucket;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
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;
|
||||
|
|
@ -34,7 +36,9 @@ public class BitbucketURLParserTest {
|
|||
public void setUp() {
|
||||
bitbucketURLParser =
|
||||
new BitbucketURLParser(
|
||||
"https://bitbucket.2mcl.com,https://bbkt.com", devfileFilenamesProvider);
|
||||
"https://bitbucket.2mcl.com,https://bbkt.com",
|
||||
devfileFilenamesProvider,
|
||||
mock(PersonalAccessTokenManager.class));
|
||||
}
|
||||
|
||||
/** Check URLs are valid with regexp */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -95,7 +95,7 @@ public class HttpBitbucketServerApiClientTest {
|
|||
.withHeader("Content-Type", "application/json; charset=utf-8")
|
||||
.withBodyFile("bitbucket/rest/api/1.0/users/ksmster/response.json")));
|
||||
|
||||
BitbucketUser user = bitbucketServer.getUser("ksmster");
|
||||
BitbucketUser user = bitbucketServer.getUser("ksmster", null);
|
||||
assertNotNull(user);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -149,9 +149,13 @@ public class GitlabOAuthTokenFetcher implements PersonalAccessTokenFetcher {
|
|||
GitlabApiClient gitlabApiClient = getApiClient(personalAccessToken.getScmProviderUrl());
|
||||
if (gitlabApiClient == null
|
||||
|| !gitlabApiClient.isConnected(personalAccessToken.getScmProviderUrl())) {
|
||||
LOG.debug(
|
||||
"not a valid url {} for current fetcher ", personalAccessToken.getScmProviderUrl());
|
||||
return Optional.empty();
|
||||
if (personalAccessToken.getScmTokenName().equals(OAUTH_PROVIDER_NAME)) {
|
||||
gitlabApiClient = new GitlabApiClient(personalAccessToken.getScmProviderUrl());
|
||||
} else {
|
||||
LOG.debug(
|
||||
"not a valid url {} for current fetcher ", personalAccessToken.getScmProviderUrl());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
if (personalAccessToken.getScmTokenName() != null
|
||||
&& personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX)) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -17,12 +17,19 @@ import com.google.common.base.Splitter;
|
|||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
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.ScmUnauthorizedException;
|
||||
import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -33,6 +40,7 @@ import org.eclipse.che.commons.lang.StringUtils;
|
|||
public class GitlabUrlParser {
|
||||
|
||||
private final DevfileFilenamesProvider devfileFilenamesProvider;
|
||||
private final PersonalAccessTokenManager personalAccessTokenManager;
|
||||
private static final List<String> gitlabUrlPatternTemplates =
|
||||
List.of(
|
||||
"^(?<host>%s)/(?<user>[^/]++)/(?<project>[^./]++).git",
|
||||
|
|
@ -40,12 +48,15 @@ public class GitlabUrlParser {
|
|||
"^(?<host>%s)/(?<user>[^/]++)/(?<project>[^/]++)(/)?(?<repository>[^/]++)?(/)?",
|
||||
"^(?<host>%s)/(?<user>[^/]++)/(?<project>[^/]++)(/)?(?<repository>[^/]++)?/-/tree/(?<branch>[^/]++)(/)?(?<subfolder>[^/]++)?");
|
||||
private final List<Pattern> gitlabUrlPatterns = new ArrayList<>();
|
||||
private static final String OAUTH_PROVIDER_NAME = "gitlab";
|
||||
|
||||
@Inject
|
||||
public GitlabUrlParser(
|
||||
@Nullable @Named("che.integration.gitlab.server_endpoints") String bitbucketEndpoints,
|
||||
DevfileFilenamesProvider devfileFilenamesProvider) {
|
||||
DevfileFilenamesProvider devfileFilenamesProvider,
|
||||
PersonalAccessTokenManager personalAccessTokenManager) {
|
||||
this.devfileFilenamesProvider = devfileFilenamesProvider;
|
||||
this.personalAccessTokenManager = personalAccessTokenManager;
|
||||
if (bitbucketEndpoints != null) {
|
||||
for (String bitbucketEndpoint : Splitter.on(",").split(bitbucketEndpoints)) {
|
||||
String trimmedEndpoint = StringUtils.trimEnd(bitbucketEndpoint, '/');
|
||||
|
|
@ -57,9 +68,55 @@ public class GitlabUrlParser {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isUserTokenPresent(String repositoryUrl) {
|
||||
Optional<String> serverUrlOptional = getServerUrl(repositoryUrl);
|
||||
if (serverUrlOptional.isPresent()) {
|
||||
String serverUrl = serverUrlOptional.get();
|
||||
try {
|
||||
Optional<PersonalAccessToken> token =
|
||||
personalAccessTokenManager.get(EnvironmentContext.getCurrent().getSubject(), serverUrl);
|
||||
if (token.isPresent()) {
|
||||
PersonalAccessToken accessToken = token.get();
|
||||
return accessToken.getScmTokenName().equals(OAUTH_PROVIDER_NAME);
|
||||
}
|
||||
} catch (ScmConfigurationPersistenceException
|
||||
| ScmUnauthorizedException
|
||||
| ScmCommunicationException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isValid(@NotNull String url) {
|
||||
return !gitlabUrlPatterns.isEmpty()
|
||||
&& gitlabUrlPatterns.stream().anyMatch(pattern -> pattern.matcher(url).matches());
|
||||
if (!gitlabUrlPatterns.isEmpty()) {
|
||||
return gitlabUrlPatterns.stream().anyMatch(pattern -> pattern.matcher(url).matches());
|
||||
} else {
|
||||
// If Gitlab URL is not configured, try to find it in a manually added user namespace
|
||||
// token.
|
||||
return isUserTokenPresent(url);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Matcher> getPatternMatcherByUrl(String url) {
|
||||
Optional<String> serverUrlOptional = getServerUrl(url);
|
||||
if (serverUrlOptional.isPresent()) {
|
||||
String serverUrl = serverUrlOptional.get();
|
||||
return gitlabUrlPatternTemplates.stream()
|
||||
.map(t -> Pattern.compile(format(t, serverUrl)).matcher(url))
|
||||
.filter(Matcher::matches)
|
||||
.findAny();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<String> getServerUrl(String repositoryUrl) {
|
||||
Matcher serverUrlMatcher = Pattern.compile("[^/|:]/").matcher(repositoryUrl);
|
||||
if (serverUrlMatcher.find()) {
|
||||
return Optional.of(
|
||||
repositoryUrl.substring(0, repositoryUrl.indexOf(serverUrlMatcher.group()) + 1));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,6 +126,10 @@ public class GitlabUrlParser {
|
|||
public GitlabUrl parse(String url) {
|
||||
|
||||
if (gitlabUrlPatterns.isEmpty()) {
|
||||
Optional<Matcher> optionalMatcher = getPatternMatcherByUrl(url);
|
||||
if (optionalMatcher.isPresent()) {
|
||||
return parse(optionalMatcher.get());
|
||||
}
|
||||
throw new UnsupportedOperationException(
|
||||
"The gitlab integration is not configured properly and cannot be used at this moment."
|
||||
+ "Please refer to docs to check the Gitlab integration instructions");
|
||||
|
|
@ -85,6 +146,10 @@ public class GitlabUrlParser {
|
|||
format(
|
||||
"The given url %s is not a valid Gitlab server URL. Check either URL or server configuration.",
|
||||
url)));
|
||||
return parse(matcher);
|
||||
}
|
||||
|
||||
private GitlabUrl parse(Matcher matcher) {
|
||||
String host = matcher.group("host");
|
||||
String userName = matcher.group("user");
|
||||
String project = matcher.group("project");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -19,6 +19,7 @@ import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
|||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
|
@ -71,7 +72,11 @@ public class GitlabFactoryParametersResolverTest {
|
|||
|
||||
@BeforeMethod
|
||||
protected void init() {
|
||||
gitlabUrlParser = new GitlabUrlParser("http://gitlab.2mcl.com", devfileFilenamesProvider);
|
||||
gitlabUrlParser =
|
||||
new GitlabUrlParser(
|
||||
"http://gitlab.2mcl.com",
|
||||
devfileFilenamesProvider,
|
||||
mock(PersonalAccessTokenManager.class));
|
||||
assertNotNull(this.gitlabUrlParser);
|
||||
gitlabFactoryParametersResolver =
|
||||
new GitlabFactoryParametersResolver(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -14,6 +14,7 @@ package org.eclipse.che.api.factory.server.gitlab;
|
|||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
|
|
@ -47,7 +48,9 @@ public class GitlabScmFileResolverTest {
|
|||
|
||||
@BeforeMethod
|
||||
protected void init() {
|
||||
gitlabUrlParser = new GitlabUrlParser(SCM_URL, devfileFilenamesProvider);
|
||||
gitlabUrlParser =
|
||||
new GitlabUrlParser(
|
||||
SCM_URL, devfileFilenamesProvider, mock(PersonalAccessTokenManager.class));
|
||||
assertNotNull(this.gitlabUrlParser);
|
||||
gitlabScmFileResolver =
|
||||
new GitlabScmFileResolver(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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,9 +11,11 @@
|
|||
*/
|
||||
package org.eclipse.che.api.factory.server.gitlab;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
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;
|
||||
|
|
@ -33,7 +35,10 @@ public class GitlabUrlParserTest {
|
|||
@BeforeMethod
|
||||
public void setUp() {
|
||||
gitlabUrlParser =
|
||||
new GitlabUrlParser("https://gitlab1.com,https://gitlab.foo.xxx", devfileFilenamesProvider);
|
||||
new GitlabUrlParser(
|
||||
"https://gitlab1.com,https://gitlab.foo.xxx",
|
||||
devfileFilenamesProvider,
|
||||
mock(PersonalAccessTokenManager.class));
|
||||
}
|
||||
|
||||
/** Check URLs are valid with regexp */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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,13 @@ package org.eclipse.che.api.factory.server.gitlab;
|
|||
|
||||
import static java.lang.String.format;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
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.RemoteFactoryUrl.DevfileLocation;
|
||||
import org.mockito.Mock;
|
||||
|
|
@ -45,7 +47,9 @@ public class GitlabUrlTest {
|
|||
protected void init() {
|
||||
when(devfileFilenamesProvider.getConfiguredDevfileFilenames())
|
||||
.thenReturn(Arrays.asList("devfile.yaml", "foo.bar"));
|
||||
gitlabUrlParser = new GitlabUrlParser("https://gitlab.net", devfileFilenamesProvider);
|
||||
gitlabUrlParser =
|
||||
new GitlabUrlParser(
|
||||
"https://gitlab.net", devfileFilenamesProvider, mock(PersonalAccessTokenManager.class));
|
||||
}
|
||||
|
||||
/** Check when there is devfile in the repository */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
* Copyright (c) 2012-2022 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/
|
||||
|
|
@ -42,8 +42,8 @@ public interface PersonalAccessTokenManager {
|
|||
/**
|
||||
* Gets {@link PersonalAccessToken} from permanent storage.
|
||||
*
|
||||
* @param cheUser
|
||||
* @param scmServerUrl
|
||||
* @param cheUser Che user object
|
||||
* @param scmServerUrl Git provider endpoint
|
||||
* @return personal access token
|
||||
* @throws ScmConfigurationPersistenceException - problem occurred during communication with *
|
||||
* permanent storage.
|
||||
|
|
|
|||
Loading…
Reference in New Issue