chore: Support configuring the GitHub OAuth endpoint (#350)

Currently the GitHub OAuth provider is hardcoded to https://github.com endpoint. In order to support Github Enterprise Server, the endpoint of the GitHub OAuth provider is configurable by the oauth secret.
pull/354/head
Igor Vinokur 2022-09-08 14:51:12 +03:00 committed by GitHub
parent 9f72001242
commit c2a2d09194
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 234 additions and 102 deletions

View File

@ -204,6 +204,10 @@ che.oauth.github.authuri= https://github.com/login/oauth/authorize
# GitHub OAuth token URI.
che.oauth.github.tokenuri= https://github.com/login/oauth/access_token
# GitHub server address.
# Prerequisite: OAuth 2 integration is configured on the GitHub server.
che.integration.github.oauth_endpoint=NULL
# GitHub OAuth redirect URIs.
# Separate multiple values with comma, for example: URI,URI,URI
che.oauth.github.redirecturis= http://localhost:${CHE_PORT}/api/oauth/callback

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2018 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/
@ -33,7 +33,7 @@ public class OpenShiftGitHubOAuthAuthenticator extends GitHubOAuthAuthenticator
@Nullable @Named("che.oauth.github.tokenuri") String tokenUri)
throws IOException {
super("NULL", "NULL", redirectUris, authUri, tokenUri);
super("NULL", "NULL", redirectUris, null, authUri, tokenUri);
if (!isNullOrEmpty(authUri)
&& !isNullOrEmpty(tokenUri)

View File

@ -59,6 +59,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

View File

@ -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/
@ -12,6 +12,7 @@
package org.eclipse.che.security.oauth;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
import com.google.api.client.util.store.MemoryDataStoreFactory;
import jakarta.mail.internet.AddressException;
@ -24,17 +25,27 @@ import org.eclipse.che.security.oauth.shared.User;
/** OAuth authentication for github account. */
@Singleton
public class GitHubOAuthAuthenticator extends OAuthAuthenticator {
private final String userRequestUrl;
public GitHubOAuthAuthenticator(
String clientId, String clientSecret, String[] redirectUris, String authUri, String tokenUri)
String clientId,
String clientSecret,
String[] redirectUris,
String authEndpoint,
String authUri,
String tokenUri)
throws IOException {
userRequestUrl =
isNullOrEmpty(authEndpoint) || trimEnd(authEndpoint, '/').equals("https://github.com")
? "https://api.github.com/user"
: trimEnd(authEndpoint, '/') + "/api/v3/user";
configure(
clientId, clientSecret, redirectUris, authUri, tokenUri, new MemoryDataStoreFactory());
}
@Override
public User getUser(OAuthToken accessToken) throws OAuthAuthenticationException {
GitHubUser user =
getJson("https://api.github.com/user", accessToken.getToken(), GitHubUser.class);
GitHubUser user = getJson(userRequestUrl, accessToken.getToken(), GitHubUser.class);
final String email = user.getEmail();
if (isNullOrEmpty(email)) {
@ -65,7 +76,7 @@ public class GitHubOAuthAuthenticator extends OAuthAuthenticator {
if (token == null
|| token.getToken() == null
|| token.getToken().isEmpty()
|| getJson("https://api.github.com/user", token.getToken(), GitHubUser.class) == null) {
|| getJson(userRequestUrl, token.getToken(), GitHubUser.class) == null) {
return null;
}
} catch (OAuthAuthenticationException e) {

View File

@ -12,6 +12,7 @@
package org.eclipse.che.security.oauth;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
import java.io.IOException;
import java.nio.file.Files;
@ -42,12 +43,18 @@ public class GitHubOAuthAuthenticatorProvider implements Provider<OAuthAuthentic
@Nullable @Named("che.oauth2.github.clientid_filepath") String gitHubClientIdPath,
@Nullable @Named("che.oauth2.github.clientsecret_filepath") String gitHubClientSecretPath,
@Nullable @Named("che.oauth.github.redirecturis") String[] redirectUris,
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint,
@Nullable @Named("che.oauth.github.authuri") String authUri,
@Nullable @Named("che.oauth.github.tokenuri") String tokenUri)
throws IOException {
authenticator =
getOAuthAuthenticator(
gitHubClientIdPath, gitHubClientSecretPath, redirectUris, authUri, tokenUri);
gitHubClientIdPath,
gitHubClientSecretPath,
redirectUris,
oauthEndpoint,
authUri,
tokenUri);
LOG.debug("{} GitHub OAuth Authenticator is used.", authenticator);
}
@ -60,10 +67,20 @@ public class GitHubOAuthAuthenticatorProvider implements Provider<OAuthAuthentic
String clientIdPath,
String clientSecretPath,
String[] redirectUris,
String oauthEndpoint,
String authUri,
String tokenUri)
throws IOException {
String trimmedOauthEndpoint = isNullOrEmpty(oauthEndpoint) ? null : trimEnd(oauthEndpoint, '/');
authUri =
isNullOrEmpty(trimmedOauthEndpoint)
? authUri
: trimmedOauthEndpoint + "/login/oauth/authorize";
tokenUri =
isNullOrEmpty(trimmedOauthEndpoint)
? tokenUri
: trimmedOauthEndpoint + "/login/oauth/access_token";
if (!isNullOrEmpty(clientIdPath)
&& !isNullOrEmpty(clientSecretPath)
&& !isNullOrEmpty(authUri)
@ -74,7 +91,7 @@ public class GitHubOAuthAuthenticatorProvider implements Provider<OAuthAuthentic
final String clientSecret = Files.readString(Path.of(clientSecretPath)).trim();
if (!isNullOrEmpty(clientId) && !isNullOrEmpty(clientSecret)) {
return new GitHubOAuthAuthenticator(
clientId, clientSecret, redirectUris, authUri, tokenUri);
clientId, clientSecret, redirectUris, trimmedOauthEndpoint, authUri, tokenUri);
}
}
return new NoopOAuthAuthenticator();

View File

@ -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/
@ -41,13 +41,19 @@ public class GitHubOAuthAuthenticatorProviderTest {
String gitHubClientIdPath,
String gitHubClientSecretPath,
String[] redirectUris,
String oauthEndpoint,
String authUri,
String tokenUri)
throws IOException {
// given
GitHubOAuthAuthenticatorProvider provider =
new GitHubOAuthAuthenticatorProvider(
gitHubClientIdPath, gitHubClientSecretPath, redirectUris, authUri, tokenUri);
gitHubClientIdPath,
gitHubClientSecretPath,
redirectUris,
oauthEndpoint,
authUri,
tokenUri);
// when
OAuthAuthenticator authenticator = provider.get();
// then
@ -62,7 +68,12 @@ public class GitHubOAuthAuthenticatorProviderTest {
// given
GitHubOAuthAuthenticatorProvider provider =
new GitHubOAuthAuthenticatorProvider(
emptyFile.getPath(), emptyFile.getPath(), new String[] {TEST_URI}, TEST_URI, TEST_URI);
emptyFile.getPath(),
emptyFile.getPath(),
new String[] {TEST_URI},
null,
TEST_URI,
TEST_URI);
// when
OAuthAuthenticator authenticator = provider.get();
// then
@ -80,6 +91,27 @@ public class GitHubOAuthAuthenticatorProviderTest {
credentialFile.getPath(),
credentialFile.getPath(),
new String[] {TEST_URI},
null,
TEST_URI,
TEST_URI);
// when
OAuthAuthenticator authenticator = provider.get();
// then
assertNotNull(authenticator);
assertTrue(GitHubOAuthAuthenticator.class.isAssignableFrom(authenticator.getClass()));
}
@Test
public void shouldProvideValidGitHubOAuthAuthenticatorWithConfiguredOAuthEndpoint()
throws IOException {
// given
GitHubOAuthAuthenticatorProvider provider =
new GitHubOAuthAuthenticatorProvider(
credentialFile.getPath(),
credentialFile.getPath(),
new String[] {TEST_URI},
"https://custom.github.com/",
TEST_URI,
TEST_URI);
// when
@ -93,22 +125,40 @@ public class GitHubOAuthAuthenticatorProviderTest {
@DataProvider(name = "noopConfig")
public Object[][] noopConfig() {
return new Object[][] {
{null, null, null, null, null},
{credentialFile.getPath(), emptyFile.getPath(), null, TEST_URI, null},
{emptyFile.getPath(), emptyFile.getPath(), null, null, TEST_URI},
{null, emptyFile.getPath(), null, TEST_URI, TEST_URI},
{null, credentialFile.getPath(), new String[] {}, null, null},
{emptyFile.getPath(), null, new String[] {}, "", ""},
{credentialFile.getPath(), null, new String[] {}, "", null},
{null, emptyFile.getPath(), new String[] {}, null, ""},
{credentialFile.getPath(), null, new String[] {}, TEST_URI, null},
{null, emptyFile.getPath(), new String[] {}, TEST_URI, TEST_URI},
{emptyFile.getPath(), null, new String[] {TEST_URI}, null, null},
{credentialFile.getPath(), null, new String[] {TEST_URI}, "", ""},
{credentialFile.getPath(), credentialFile.getPath(), new String[] {TEST_URI}, null, TEST_URI},
{credentialFile.getPath(), emptyFile.getPath(), new String[] {TEST_URI}, TEST_URI, null},
{credentialFile.getPath(), credentialFile.getPath(), new String[] {TEST_URI}, TEST_URI, ""},
{emptyFile.getPath(), emptyFile.getPath(), new String[] {TEST_URI}, "", TEST_URI}
{null, null, null, null, null, null},
{null, null, null, "", null, null},
{null, null, null, TEST_URI, null, null},
{credentialFile.getPath(), emptyFile.getPath(), null, null, TEST_URI, null},
{emptyFile.getPath(), emptyFile.getPath(), null, null, null, TEST_URI},
{null, emptyFile.getPath(), null, null, TEST_URI, TEST_URI},
{null, credentialFile.getPath(), new String[] {}, null, null, null},
{emptyFile.getPath(), null, new String[] {}, null, "", ""},
{credentialFile.getPath(), null, new String[] {}, null, "", null},
{null, emptyFile.getPath(), new String[] {}, null, null, ""},
{credentialFile.getPath(), null, new String[] {}, null, TEST_URI, null},
{null, emptyFile.getPath(), new String[] {}, null, TEST_URI, TEST_URI},
{emptyFile.getPath(), null, new String[] {TEST_URI}, null, null, null},
{credentialFile.getPath(), null, new String[] {TEST_URI}, null, "", ""},
{
credentialFile.getPath(),
credentialFile.getPath(),
new String[] {TEST_URI},
null,
null,
TEST_URI
},
{
credentialFile.getPath(), emptyFile.getPath(), new String[] {TEST_URI}, null, TEST_URI, null
},
{
credentialFile.getPath(),
credentialFile.getPath(),
new String[] {TEST_URI},
null,
TEST_URI,
""
},
{emptyFile.getPath(), emptyFile.getPath(), new String[] {TEST_URI}, null, "", TEST_URI}
};
}
}

View File

@ -86,6 +86,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>

View File

@ -11,11 +11,13 @@
*/
package org.eclipse.che.api.factory.server.github;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
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.time.Duration.ofSeconds;
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;
@ -37,6 +39,7 @@ import java.util.function.Function;
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.commons.annotation.Nullable;
import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -46,11 +49,8 @@ public class GithubApiClient {
private static final Logger LOG = LoggerFactory.getLogger(GithubApiClient.class);
/** GitHub API endpoint URL. */
public static final String GITHUB_API_SERVER = "https://api.github.com";
/** GitHub endpoint URL. */
public static final String GITHUB_SERVER = "https://github.com";
public static final String GITHUB_SAAS_ENDPOINT = "https://github.com";
/** GitHub HTTP header containing OAuth scopes. */
public static final String GITHUB_OAUTH_SCOPES_HEADER = "X-OAuth-Scopes";
@ -62,19 +62,16 @@ public class GithubApiClient {
private static final Duration DEFAULT_HTTP_TIMEOUT = ofSeconds(10);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/** Default constructor, binds http client to https://api.github.com */
public GithubApiClient() {
this(GITHUB_API_SERVER);
}
/**
* Used for URL injection in testing.
*
* @param apiServerUrl the GitHub API url
*/
GithubApiClient(final String apiServerUrl) {
this.apiServerUrl = URI.create(apiServerUrl);
this.scmServerUrl = URI.create(GITHUB_SERVER);
/** Default constructor, binds http client to GitHub API url */
public GithubApiClient(@Nullable String serverUrl) {
String trimmedServerUrl = !isNullOrEmpty(serverUrl) ? trimEnd(serverUrl, '/') : null;
this.apiServerUrl =
URI.create(
isNullOrEmpty(trimmedServerUrl) || trimmedServerUrl.equals(GITHUB_SAAS_ENDPOINT)
? "https://api.github.com/"
: trimmedServerUrl + "/api/v3/");
this.scmServerUrl =
URI.create(isNullOrEmpty(trimmedServerUrl) ? GITHUB_SAAS_ENDPOINT : trimmedServerUrl);
this.httpClient =
HttpClient.newBuilder()
.executor(
@ -101,7 +98,7 @@ public class GithubApiClient {
*/
public GithubUser getUser(String authenticationToken)
throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
final URI uri = apiServerUrl.resolve("/user");
final URI uri = apiServerUrl.resolve("./user");
HttpRequest request = buildGithubApiRequest(uri, authenticationToken);
LOG.trace("executeRequest={}", request);
return executeRequest(
@ -120,7 +117,7 @@ public class GithubApiClient {
String id, String username, String repoName, String authenticationToken)
throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
final URI uri =
apiServerUrl.resolve(String.format("/repos/%1s/%2s/pulls/%3s", username, repoName, id));
apiServerUrl.resolve(String.format("./repos/%1s/%2s/pulls/%3s", username, repoName, id));
HttpRequest request = buildGithubApiRequest(uri, authenticationToken);
LOG.trace("executeRequest={}", request);
return executeRequest(
@ -149,7 +146,7 @@ public class GithubApiClient {
*/
public String[] getTokenScopes(String authenticationToken)
throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
final URI uri = apiServerUrl.resolve("/user");
final URI uri = apiServerUrl.resolve("./user");
HttpRequest request = buildGithubApiRequest(uri, authenticationToken);
LOG.trace("executeRequest={}", request);
return executeRequest(
@ -213,6 +210,6 @@ public class GithubApiClient {
* @return If the provided url is recognized by the current client
*/
public boolean isConnected(String scmServerUrl) {
return this.scmServerUrl.equals(URI.create(scmServerUrl));
return this.scmServerUrl.equals(URI.create(trimEnd(scmServerUrl, '/')));
}
}

View File

@ -34,6 +34,7 @@ import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationExceptio
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.UnknownScmProviderException;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.security.oauth.OAuthAPI;
@ -117,8 +118,11 @@ public class GithubPersonalAccessTokenFetcher implements PersonalAccessTokenFetc
.build();
@Inject
public GithubPersonalAccessTokenFetcher(@Named("che.api") String apiEndpoint, OAuthAPI oAuthAPI) {
this(apiEndpoint, oAuthAPI, new GithubApiClient());
public GithubPersonalAccessTokenFetcher(
@Named("che.api") String apiEndpoint,
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint,
OAuthAPI oAuthAPI) {
this(apiEndpoint, oAuthAPI, new GithubApiClient(oauthEndpoint));
}
/**
@ -161,7 +165,7 @@ public class GithubPersonalAccessTokenFetcher implements PersonalAccessTokenFetc
if (valid.isEmpty()) {
throw new ScmCommunicationException(
"Unable to verify if current token is a valid GitHub token. Token's scm-url needs to be '"
+ GithubApiClient.GITHUB_SERVER
+ GithubApiClient.GITHUB_SAAS_ENDPOINT
+ "' and was '"
+ token.getScmProviderUrl()
+ "'");

View File

@ -11,13 +11,19 @@
*/
package org.eclipse.che.api.factory.server.github;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
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;
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
import jakarta.validation.constraints.NotNull;
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.core.ApiException;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
@ -30,6 +36,7 @@ import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException
import org.eclipse.che.api.factory.server.scm.exception.UnknownScmProviderException;
import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException;
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.subject.Subject;
import org.slf4j.Logger;
@ -47,27 +54,42 @@ public class GithubURLParser {
private final PersonalAccessTokenManager tokenManager;
private final DevfileFilenamesProvider devfileFilenamesProvider;
private final GithubApiClient apiClient;
private final String oauthEndpoint;
/**
* Regexp to find repository details (repository name, project name and branch and subfolder)
* Examples of valid URLs are in the test class.
*/
private final Pattern githubPattern;
@Inject
public GithubURLParser(
PersonalAccessTokenManager tokenManager,
DevfileFilenamesProvider devfileFilenamesProvider,
GithubApiClient apiClient) {
this.tokenManager = tokenManager;
this.devfileFilenamesProvider = devfileFilenamesProvider;
this.apiClient = apiClient;
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint) {
this(tokenManager, devfileFilenamesProvider, new GithubApiClient(oauthEndpoint), oauthEndpoint);
}
/**
* Regexp to find repository details (repository name, project name and branch and subfolder)
* Examples of valid URLs are in the test class.
*/
protected static final Pattern GITHUB_PATTERN =
Pattern.compile(
"^(?:http)(?:s)?(?:\\:\\/\\/)github.com/(?<repoUser>[^/]++)/(?<repoName>[^/]++)((/)|(?:/tree/(?<branchName>[^/]++)(?:/(?<subFolder>.*))?)|(/pull/(?<pullRequestId>[^/]++)))?$");
/** Constructor used for testing only. */
GithubURLParser(
PersonalAccessTokenManager tokenManager,
DevfileFilenamesProvider devfileFilenamesProvider,
GithubApiClient githubApiClient,
String oauthEndpoint) {
this.tokenManager = tokenManager;
this.devfileFilenamesProvider = devfileFilenamesProvider;
this.apiClient = githubApiClient;
this.oauthEndpoint = oauthEndpoint;
String endpoint =
isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
this.githubPattern =
compile(
format(
"^%s/(?<repoUser>[^/]++)/(?<repoName>[^/]++)((/)|(?:/tree/(?<branchName>[^/]++)(?:/(?<subFolder>.*))?)|(/pull/(?<pullRequestId>[^/]++)))?$",
endpoint));
}
public boolean isValid(@NotNull String url) {
return GITHUB_PATTERN.matcher(url).matches();
return githubPattern.matcher(url).matches();
}
public GithubUrl parseWithoutAuthentication(String url) throws ApiException {
@ -80,14 +102,18 @@ public class GithubURLParser {
private GithubUrl parse(String url, boolean authenticationRequired) throws ApiException {
// Apply github url to the regexp
Matcher matcher = GITHUB_PATTERN.matcher(url);
Matcher matcher = githubPattern.matcher(url);
if (!matcher.matches()) {
throw new IllegalArgumentException(
String.format(
format(
"The given github url %s is not a valid URL github url. It should start with https://github.com/<user>/<repo>",
url));
}
String serverUrl =
isNullOrEmpty(oauthEndpoint) || trimEnd(oauthEndpoint, '/').equals(GITHUB_SAAS_ENDPOINT)
? null
: trimEnd(oauthEndpoint, '/');
String repoUser = matcher.group("repoUser");
String repoName = matcher.group("repoName");
if (repoName.matches("^[\\w-][\\w.-]*?\\.git$")) {
@ -98,7 +124,8 @@ public class GithubURLParser {
String pullRequestId = matcher.group("pullRequestId");
if (pullRequestId != null) {
try {
String githubEndpoint = "https://github.com";
String githubEndpoint =
isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
Subject subject = EnvironmentContext.getCurrent().getSubject();
PersonalAccessToken personalAccessToken = null;
Optional<PersonalAccessToken> tokenOptional = tokenManager.get(subject, githubEndpoint);
@ -114,7 +141,7 @@ public class GithubURLParser {
String prState = pullRequest.getState();
if (!"open".equalsIgnoreCase(prState)) {
throw new IllegalArgumentException(
String.format(
format(
"The given Pull Request url %s is not Opened, (found %s), thus it can't be opened as branch may have been removed.",
url, prState));
}
@ -139,6 +166,7 @@ public class GithubURLParser {
return new GithubUrl()
.withUsername(repoUser)
.withRepository(repoName)
.withServerUrl(serverUrl)
.withBranch(branchName)
.withSubfolder(matcher.group("subFolder"))
.withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames());

View File

@ -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/
@ -12,8 +12,8 @@
package org.eclipse.che.api.factory.server.github;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import com.google.common.base.Strings;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -47,6 +47,8 @@ public class GithubUrl implements RemoteFactoryUrl {
/** Subfolder if any */
private String subfolder;
private String serverUrl;
/** Devfile filenames list */
private final List<String> devfileFilenames = new ArrayList<>();
@ -110,7 +112,7 @@ public class GithubUrl implements RemoteFactoryUrl {
}
protected GithubUrl withBranch(String branch) {
if (!Strings.isNullOrEmpty(branch)) {
if (!isNullOrEmpty(branch)) {
this.branch = branch;
}
return this;
@ -136,6 +138,11 @@ public class GithubUrl implements RemoteFactoryUrl {
return this;
}
public GithubUrl withServerUrl(String serverUrl) {
this.serverUrl = serverUrl;
return this;
}
/**
* Provides list of configured devfile filenames with locations
*
@ -167,7 +174,7 @@ public class GithubUrl implements RemoteFactoryUrl {
*/
public String rawFileLocation(String fileName) {
return new StringJoiner("/")
.add("https://raw.githubusercontent.com")
.add(isNullOrEmpty(serverUrl) ? "https://raw.githubusercontent.com" : serverUrl + "/raw")
.add(username)
.add(repository)
.add(firstNonNull(branch, "HEAD"))
@ -177,7 +184,7 @@ public class GithubUrl implements RemoteFactoryUrl {
@Override
public String getHostName() {
return HOSTNAME;
return isNullOrEmpty(serverUrl) ? HOSTNAME : serverUrl;
}
/**
@ -186,6 +193,11 @@ public class GithubUrl implements RemoteFactoryUrl {
* @return location of the repository.
*/
protected String repositoryLocation() {
return HOSTNAME + "/" + this.username + "/" + this.repository + ".git";
return (isNullOrEmpty(serverUrl) ? HOSTNAME : serverUrl)
+ "/"
+ this.username
+ "/"
+ this.repository
+ ".git";
}
}

View File

@ -29,6 +29,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.subject.Subject;
import org.eclipse.che.security.oauth.OAuthAPI;
@ -49,8 +50,11 @@ public class GithubUserDataFetcher implements GitUserDataFetcher {
ImmutableSet.of("repo", "user:email", "read:user");
@Inject
public GithubUserDataFetcher(@Named("che.api") String apiEndpoint, OAuthAPI oAuthAPI) {
this(apiEndpoint, oAuthAPI, new GithubApiClient());
public GithubUserDataFetcher(
@Named("che.api") String apiEndpoint,
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint,
OAuthAPI oAuthAPI) {
this(apiEndpoint, oAuthAPI, new GithubApiClient(oauthEndpoint));
}
/** Constructor used for testing only. */

View File

@ -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/
@ -58,7 +58,7 @@ public class GithubApiClientTest {
@Test
public void testGetUser() throws Exception {
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token token1"))
.willReturn(
aResponse()
@ -81,7 +81,7 @@ public class GithubApiClientTest {
@Test
public void testGetTokenScopes() throws Exception {
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token token1"))
.willReturn(
aResponse()
@ -99,7 +99,7 @@ public class GithubApiClientTest {
@Test
public void testGetTokenScopesWithNoScopeHeader() throws Exception {
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token token1"))
.willReturn(
aResponse()
@ -119,7 +119,7 @@ public class GithubApiClientTest {
@Test
public void testGetTokenScopesWithNoScope() throws Exception {
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token token1"))
.willReturn(
aResponse()
@ -144,6 +144,6 @@ public class GithubApiClientTest {
@Test
public void shouldReturnTrueWhenConnectedToGithub() {
assertTrue(client.isConnected("https://github.com"));
assertTrue(client.isConnected(wireMockServer.url("/")));
}
}

View File

@ -21,7 +21,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -102,8 +101,7 @@ public class GithubFactoryParametersResolverTest {
@BeforeMethod
protected void init() {
githubUrlParser =
new GithubURLParser(
personalAccessTokenManager, devfileFilenamesProvider, mock(GithubApiClient.class));
new GithubURLParser(personalAccessTokenManager, devfileFilenamesProvider, null);
assertNotNull(this.githubUrlParser);
githubFactoryParametersResolver =
new GithubFactoryParametersResolver(

View File

@ -59,7 +59,7 @@ public class GithubGitUserDataFetcherTest {
new GithubUserDataFetcher(
"http://che.api", oAuthAPI, new GithubApiClient(wireMockServer.url("/")));
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token " + githubOauthToken))
.willReturn(
aResponse()

View File

@ -143,7 +143,7 @@ public class GithubPersonalAccessTokenFetcherTest {
when(oAuthAPI.getToken(anyString())).thenReturn(oAuthToken);
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token " + githubOauthToken))
.willReturn(
aResponse()
@ -151,7 +151,7 @@ public class GithubPersonalAccessTokenFetcherTest {
.withHeader(GithubApiClient.GITHUB_OAUTH_SCOPES_HEADER, "")
.withBodyFile("github/rest/user/response.json")));
githubPATFetcher.fetchPersonalAccessToken(subject, GithubApiClient.GITHUB_SERVER);
githubPATFetcher.fetchPersonalAccessToken(subject, wireMockServer.url("/"));
}
@Test(
@ -161,7 +161,7 @@ public class GithubPersonalAccessTokenFetcherTest {
Subject subject = new SubjectImpl("Username", "id1", "token", false);
when(oAuthAPI.getToken(anyString())).thenThrow(UnauthorizedException.class);
githubPATFetcher.fetchPersonalAccessToken(subject, GithubApiClient.GITHUB_SERVER);
githubPATFetcher.fetchPersonalAccessToken(subject, wireMockServer.url("/"));
}
@Test
@ -171,7 +171,7 @@ public class GithubPersonalAccessTokenFetcherTest {
when(oAuthAPI.getToken(anyString())).thenReturn(oAuthToken);
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token " + githubOauthToken))
.willReturn(
aResponse()
@ -182,14 +182,14 @@ public class GithubPersonalAccessTokenFetcherTest {
.withBodyFile("github/rest/user/response.json")));
PersonalAccessToken token =
githubPATFetcher.fetchPersonalAccessToken(subject, GithubApiClient.GITHUB_SERVER);
githubPATFetcher.fetchPersonalAccessToken(subject, wireMockServer.url("/"));
assertNotNull(token);
}
@Test
public void shouldValidatePersonalToken() throws Exception {
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token " + githubOauthToken))
.willReturn(
aResponse()
@ -201,7 +201,7 @@ public class GithubPersonalAccessTokenFetcherTest {
PersonalAccessToken token =
new PersonalAccessToken(
"https://github.com",
wireMockServer.url("/"),
"cheUser",
"username",
"123456789",
@ -215,7 +215,7 @@ public class GithubPersonalAccessTokenFetcherTest {
@Test
public void shouldValidateOauthToken() throws Exception {
stubFor(
get(urlEqualTo("/user"))
get(urlEqualTo("/api/v3/user"))
.withHeader(HttpHeaders.AUTHORIZATION, equalTo("token " + githubOauthToken))
.willReturn(
aResponse()
@ -227,7 +227,7 @@ public class GithubPersonalAccessTokenFetcherTest {
PersonalAccessToken token =
new PersonalAccessToken(
"https://github.com",
wireMockServer.url("/"),
"cheUser",
"username",
"123456789",
@ -240,11 +240,11 @@ public class GithubPersonalAccessTokenFetcherTest {
@Test
public void shouldNotValidateExpiredOauthToken() throws Exception {
stubFor(get(urlEqualTo("/user")).willReturn(aResponse().withStatus(HTTP_FORBIDDEN)));
stubFor(get(urlEqualTo("/api/v3/user")).willReturn(aResponse().withStatus(HTTP_FORBIDDEN)));
PersonalAccessToken token =
new PersonalAccessToken(
"https://github.com",
wireMockServer.url("/"),
"cheUser",
"username",
"123456789",

View File

@ -13,7 +13,6 @@ package org.eclipse.che.api.factory.server.github;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.*;
@ -45,8 +44,7 @@ public class GithubScmFileResolverTest {
@BeforeMethod
protected void init() {
githubURLParser =
new GithubURLParser(
personalAccessTokenManager, devfileFilenamesProvider, mock(GithubApiClient.class));
new GithubURLParser(personalAccessTokenManager, devfileFilenamesProvider, null);
assertNotNull(this.githubURLParser);
githubScmFileResolver =
new GithubScmFileResolver(
@ -65,7 +63,7 @@ public class GithubScmFileResolverTest {
@Test
public void checkValidAcceptUrl() {
// should be accepted
assertTrue(githubScmFileResolver.accept("http://github.com/test/repo.git"));
assertTrue(githubScmFileResolver.accept("https://github.com/test/repo.git"));
}
@Test
@ -77,7 +75,8 @@ public class GithubScmFileResolverTest {
when(personalAccessTokenManager.fetchAndSave(any(), anyString()))
.thenReturn(personalAccessToken);
String content = githubScmFileResolver.fileContent("http://github.com/test/repo.git", filename);
String content =
githubScmFileResolver.fileContent("https://github.com/test/repo.git", filename);
assertEquals(content, rawContent);
}