diff --git a/assembly/assembly-wsmaster-war/pom.xml b/assembly/assembly-wsmaster-war/pom.xml
index 779a7749fc..7bb0fd9214 100644
--- a/assembly/assembly-wsmaster-war/pom.xml
+++ b/assembly/assembly-wsmaster-war/pom.xml
@@ -107,6 +107,10 @@
org.eclipse.che.core
che-core-api-auth-bitbucket
+
+ org.eclipse.che.core
+ che-core-api-auth-github
+
org.eclipse.che.core
che-core-api-auth-gitlab
diff --git a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java
index 08acaa2eaf..9e8e24dc11 100644
--- a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java
+++ b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java
@@ -48,7 +48,9 @@ import org.eclipse.che.api.factory.server.bitbucket.BitbucketServerScmFileResolv
import org.eclipse.che.api.factory.server.git.ssh.GitSshFactoryParametersResolver;
import org.eclipse.che.api.factory.server.git.ssh.GitSshScmFileResolver;
import org.eclipse.che.api.factory.server.github.GithubFactoryParametersResolver;
+import org.eclipse.che.api.factory.server.github.GithubFactoryParametersResolverSecond;
import org.eclipse.che.api.factory.server.github.GithubScmFileResolver;
+import org.eclipse.che.api.factory.server.github.GithubScmFileResolverSecond;
import org.eclipse.che.api.factory.server.gitlab.GitlabFactoryParametersResolver;
import org.eclipse.che.api.factory.server.gitlab.GitlabScmFileResolver;
import org.eclipse.che.api.metrics.WsMasterMetricsModule;
@@ -104,7 +106,6 @@ import org.eclipse.che.multiuser.permission.user.UserServicePermissionsFilter;
import org.eclipse.che.security.PBKDF2PasswordEncryptor;
import org.eclipse.che.security.PasswordEncryptor;
import org.eclipse.che.security.oauth.EmbeddedOAuthAPI;
-import org.eclipse.che.security.oauth.GitLabModule;
import org.eclipse.che.security.oauth.OAuthAPI;
import org.eclipse.che.security.oauth.OpenShiftOAuthModule;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientConfigFactory;
@@ -169,6 +170,9 @@ public class WsMasterModule extends AbstractModule {
Multibinder factoryParametersResolverMultibinder =
Multibinder.newSetBinder(binder(), FactoryParametersResolver.class);
factoryParametersResolverMultibinder.addBinding().to(GithubFactoryParametersResolver.class);
+ factoryParametersResolverMultibinder
+ .addBinding()
+ .to(GithubFactoryParametersResolverSecond.class);
factoryParametersResolverMultibinder
.addBinding()
.to(BitbucketServerAuthorizingFactoryParametersResolver.class);
@@ -185,6 +189,7 @@ public class WsMasterModule extends AbstractModule {
Multibinder scmFileResolverResolverMultibinder =
Multibinder.newSetBinder(binder(), ScmFileResolver.class);
scmFileResolverResolverMultibinder.addBinding().to(GithubScmFileResolver.class);
+ scmFileResolverResolverMultibinder.addBinding().to(GithubScmFileResolverSecond.class);
scmFileResolverResolverMultibinder.addBinding().to(BitbucketScmFileResolver.class);
scmFileResolverResolverMultibinder.addBinding().to(GitlabScmFileResolver.class);
scmFileResolverResolverMultibinder.addBinding().to(BitbucketServerScmFileResolver.class);
@@ -296,8 +301,9 @@ public class WsMasterModule extends AbstractModule {
install(new FactoryModuleBuilder().build(PassThroughProxyProvisionerFactory.class));
installDefaultSecureServerExposer(infrastructure);
install(new org.eclipse.che.security.BitbucketModule());
- install(new GitLabModule());
+ install(new org.eclipse.che.security.oauth.GitLabModule());
install(new org.eclipse.che.security.oauth.AzureDevOpsModule());
+ install(new org.eclipse.che.security.oauth.GithubModule());
configureMultiUserMode(persistenceProperties, infrastructure);
diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties
index 5bea92e00f..8c660ec936 100644
--- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties
+++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties
@@ -145,22 +145,42 @@ che.oauth.azure.devops.redirecturis=https://${CHE_HOST}/api/oauth/callback
# Location of the file with GitHub client id.
che.oauth2.github.clientid_filepath=NULL
+# Configuration of the GitHub OAuth2 client. Used to obtain personal access tokens.
+# Location of the file with GitHub client id (The second GitHub instance).
+che.oauth2.github.clientid_filepath_2=NULL
+
# Location of the file with GitHub client secret.
che.oauth2.github.clientsecret_filepath=NULL
+# Location of the file with GitHub client secret (The second GitHub instance).
+che.oauth2.github.clientsecret_filepath_2=NULL
+
# GitHub OAuth authorization URI.
che.oauth.github.authuri= https://github.com/login/oauth/authorize
+# GitHub OAuth authorization URI (The second GitHub instance).
+che.oauth.github.authuri_2= https://github.com/login/oauth/authorize
+
# GitHub OAuth token URI.
che.oauth.github.tokenuri= https://github.com/login/oauth/access_token
+# GitHub OAuth token URI (The second GitHub instance).
+che.oauth.github.tokenuri_2= 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 server address.
+# Prerequisite: OAuth 2 integration is configured on the GitHub server (The second GitHub instance).
+che.integration.github.oauth_endpoint_2=NULL
+
# GitHub server disable subdomain isolation flag.
che.integration.github.disable_subdomain_isolation=false
+# GitHub server disable subdomain isolation flag (The second GitHub instance).
+che.integration.github.disable_subdomain_isolation_2=false
+
# 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
diff --git a/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml b/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml
index a8aa3b15f3..3efba8b5f4 100644
--- a/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml
+++ b/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml
@@ -60,7 +60,7 @@
org.eclipse.che.core
- che-core-api-auth-github
+ che-core-api-auth-github-common
org.eclipse.che.core
diff --git a/multiuser/keycloak/che-multiuser-keycloak-token-provider/src/main/java/org/eclipse/che/multiuser/keycloak/token/provider/oauth/OpenShiftGitHubOAuthAuthenticator.java b/multiuser/keycloak/che-multiuser-keycloak-token-provider/src/main/java/org/eclipse/che/multiuser/keycloak/token/provider/oauth/OpenShiftGitHubOAuthAuthenticator.java
index ed6862f6c4..04114df298 100644
--- a/multiuser/keycloak/che-multiuser-keycloak-token-provider/src/main/java/org/eclipse/che/multiuser/keycloak/token/provider/oauth/OpenShiftGitHubOAuthAuthenticator.java
+++ b/multiuser/keycloak/che-multiuser-keycloak-token-provider/src/main/java/org/eclipse/che/multiuser/keycloak/token/provider/oauth/OpenShiftGitHubOAuthAuthenticator.java
@@ -33,7 +33,7 @@ public class OpenShiftGitHubOAuthAuthenticator extends GitHubOAuthAuthenticator
@Nullable @Named("che.oauth.github.tokenuri") String tokenUri)
throws IOException {
- super("NULL", "NULL", redirectUris, null, authUri, tokenUri);
+ super("NULL", "NULL", redirectUris, null, authUri, tokenUri, "github");
if (!isNullOrEmpty(authUri)
&& !isNullOrEmpty(tokenUri)
diff --git a/pom.xml b/pom.xml
index abddbb3346..a223e07608 100644
--- a/pom.xml
+++ b/pom.xml
@@ -687,6 +687,11 @@
che-core-api-auth-github
${che.version}
+
+ org.eclipse.che.core
+ che-core-api-auth-github-common
+ ${che.version}
+
org.eclipse.che.core
che-core-api-auth-gitlab
@@ -769,6 +774,11 @@
che-core-api-factory-github
${che.version}
+
+ org.eclipse.che.core
+ che-core-api-factory-github-common
+ ${che.version}
+
org.eclipse.che.core
che-core-api-factory-gitlab
diff --git a/wsmaster/che-core-api-auth-github-common/pom.xml b/wsmaster/che-core-api-auth-github-common/pom.xml
new file mode 100644
index 0000000000..83e619e933
--- /dev/null
+++ b/wsmaster/che-core-api-auth-github-common/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+ 4.0.0
+
+ che-master-parent
+ org.eclipse.che.core
+ 7.77.0-SNAPSHOT
+
+ che-core-api-auth-github-common
+ jar
+ Che Core :: API :: Authentication Github Common
+
+
+ com.google.guava
+ guava
+
+
+ com.google.http-client
+ google-http-client
+
+
+ jakarta.inject
+ jakarta.inject-api
+
+
+ org.eclipse.che.core
+ che-core-api-auth
+
+
+ org.eclipse.che.core
+ che-core-api-auth-shared
+
+
+ org.eclipse.che.core
+ che-core-commons-lang
+
+
+ org.slf4j
+ slf4j-api
+
+
+ com.github.tomakehurst
+ wiremock-jre8-standalone
+ test
+
+
+ org.testng
+ testng
+ test
+
+
+ org.wiremock
+ wiremock-standalone
+ test
+
+
+
diff --git a/wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/AbstractGitHubOAuthAuthenticatorProvider.java b/wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/AbstractGitHubOAuthAuthenticatorProvider.java
new file mode 100644
index 0000000000..7b7b9a06cf
--- /dev/null
+++ b/wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/AbstractGitHubOAuthAuthenticatorProvider.java
@@ -0,0 +1,114 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+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;
+import java.nio.file.Path;
+import java.util.Objects;
+import javax.inject.Provider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides implementation of GitHub {@link OAuthAuthenticator} based on available configuration.
+ *
+ * @author Pavol Baran
+ */
+public abstract class AbstractGitHubOAuthAuthenticatorProvider
+ implements Provider {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(AbstractGitHubOAuthAuthenticatorProvider.class);
+ private final String providerName;
+ private final OAuthAuthenticator authenticator;
+
+ public AbstractGitHubOAuthAuthenticatorProvider(
+ String gitHubClientIdPath,
+ String gitHubClientSecretPath,
+ String[] redirectUris,
+ String oauthEndpoint,
+ String authUri,
+ String tokenUri,
+ String providerName)
+ throws IOException {
+ this.providerName = providerName;
+ authenticator =
+ getOAuthAuthenticator(
+ gitHubClientIdPath,
+ gitHubClientSecretPath,
+ redirectUris,
+ oauthEndpoint,
+ authUri,
+ tokenUri);
+ LOG.debug("{} GitHub OAuth Authenticator is used.", authenticator);
+ }
+
+ @Override
+ public OAuthAuthenticator get() {
+ return authenticator;
+ }
+
+ private OAuthAuthenticator getOAuthAuthenticator(
+ 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)
+ && !isNullOrEmpty(tokenUri)
+ && Objects.nonNull(redirectUris)
+ && redirectUris.length != 0) {
+ final String clientId = Files.readString(Path.of(clientIdPath)).trim();
+ final String clientSecret = Files.readString(Path.of(clientSecretPath)).trim();
+ if (!isNullOrEmpty(clientId) && !isNullOrEmpty(clientSecret)) {
+ return new GitHubOAuthAuthenticator(
+ clientId,
+ clientSecret,
+ redirectUris,
+ trimmedOauthEndpoint,
+ authUri,
+ tokenUri,
+ providerName);
+ }
+ }
+ return new NoopOAuthAuthenticator();
+ }
+
+ static class NoopOAuthAuthenticator extends OAuthAuthenticator {
+ @Override
+ public String getOAuthProvider() {
+ return "Noop";
+ }
+
+ @Override
+ public String getEndpointUrl() {
+ return "Noop";
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticator.java b/wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticator.java
similarity index 95%
rename from wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticator.java
rename to wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticator.java
index 7cacae7c7a..7efc36db63 100644
--- a/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticator.java
+++ b/wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticator.java
@@ -20,16 +20,15 @@ import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
-import javax.inject.Singleton;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
/** OAuth authentication for github account. */
-@Singleton
public class GitHubOAuthAuthenticator extends OAuthAuthenticator {
private final String clientId;
private final String clientSecret;
private final String githubApiUrl;
private final String providerUrl;
+ private final String providerName;
public GitHubOAuthAuthenticator(
String clientId,
@@ -37,10 +36,12 @@ public class GitHubOAuthAuthenticator extends OAuthAuthenticator {
String[] redirectUris,
String authEndpoint,
String authUri,
- String tokenUri)
+ String tokenUri,
+ String providerName)
throws IOException {
this.clientId = clientId;
this.clientSecret = clientSecret;
+ this.providerName = providerName;
providerUrl = isNullOrEmpty(authEndpoint) ? "https://github.com" : trimEnd(authEndpoint, '/');
githubApiUrl =
providerUrl.equals("https://github.com")
@@ -52,7 +53,7 @@ public class GitHubOAuthAuthenticator extends OAuthAuthenticator {
@Override
public final String getOAuthProvider() {
- return "github";
+ return providerName;
}
@Override
diff --git a/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubUser.java b/wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/GitHubUser.java
similarity index 97%
rename from wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubUser.java
rename to wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/GitHubUser.java
index fbdba5f5f1..ce48134b75 100644
--- a/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubUser.java
+++ b/wsmaster/che-core-api-auth-github-common/src/main/java/org/eclipse/che/security/oauth/GitHubUser.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/
diff --git a/wsmaster/che-core-api-auth-github/pom.xml b/wsmaster/che-core-api-auth-github/pom.xml
index 59611099c5..d1e4d398a3 100644
--- a/wsmaster/che-core-api-auth-github/pom.xml
+++ b/wsmaster/che-core-api-auth-github/pom.xml
@@ -27,10 +27,6 @@
com.google.guava
guava
-
- com.google.http-client
- google-http-client
-
com.google.inject
guice
@@ -45,24 +41,16 @@
org.eclipse.che.core
- che-core-api-auth-shared
+ che-core-api-auth-github-common
+
+
+ org.eclipse.che.core
+ che-core-api-auth-github-common
org.eclipse.che.core
che-core-commons-annotations
-
- org.eclipse.che.core
- che-core-commons-inject
-
-
- org.eclipse.che.core
- che-core-commons-lang
-
-
- org.slf4j
- slf4j-api
-
com.github.tomakehurst
wiremock-jre8-standalone
diff --git a/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticatorProvider.java b/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticatorProvider.java
index d14c446e6f..85d286d64e 100644
--- a/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticatorProvider.java
+++ b/wsmaster/che-core-api-auth-github/src/main/java/org/eclipse/che/security/oauth/GitHubOAuthAuthenticatorProvider.java
@@ -11,20 +11,11 @@
*/
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;
-import java.nio.file.Path;
-import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
-import javax.inject.Provider;
import javax.inject.Singleton;
import org.eclipse.che.commons.annotation.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Provides implementation of GitHub {@link OAuthAuthenticator} based on available configuration.
@@ -32,9 +23,8 @@ import org.slf4j.LoggerFactory;
* @author Pavol Baran
*/
@Singleton
-public class GitHubOAuthAuthenticatorProvider implements Provider {
- private static final Logger LOG = LoggerFactory.getLogger(GitHubOAuthAuthenticatorProvider.class);
- private final OAuthAuthenticator authenticator;
+public class GitHubOAuthAuthenticatorProvider extends AbstractGitHubOAuthAuthenticatorProvider {
+ private static final String PROVIDER_NAME = "github";
@Inject
public GitHubOAuthAuthenticatorProvider(
@@ -45,65 +35,13 @@ public class GitHubOAuthAuthenticatorProvider implements Provider oAuthAuthenticators =
Multibinder.newSetBinder(binder(), OAuthAuthenticator.class);
oAuthAuthenticators.addBinding().toProvider(GitHubOAuthAuthenticatorProvider.class);
+ oAuthAuthenticators.addBinding().toProvider(GitHubOAuthAuthenticatorProviderSecond.class);
}
}
diff --git a/wsmaster/che-core-api-factory-github-common/pom.xml b/wsmaster/che-core-api-factory-github-common/pom.xml
new file mode 100644
index 0000000000..69801b5082
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github-common/pom.xml
@@ -0,0 +1,94 @@
+
+
+
+ 4.0.0
+
+ che-master-parent
+ org.eclipse.che.core
+ 7.77.0-SNAPSHOT
+
+ che-core-api-factory-github-common
+ jar
+ Che Core :: API :: Factory Resolver Github Common
+
+ true
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.google.guava
+ guava
+
+
+ jakarta.inject
+ jakarta.inject-api
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+ org.eclipse.che.core
+ che-core-api-auth
+
+
+ org.eclipse.che.core
+ che-core-api-auth-shared
+
+
+ org.eclipse.che.core
+ che-core-api-core
+
+
+ org.eclipse.che.core
+ che-core-api-dto
+
+
+ org.eclipse.che.core
+ che-core-api-factory
+
+
+ org.eclipse.che.core
+ che-core-api-factory-shared
+
+
+ org.eclipse.che.core
+ che-core-api-workspace
+
+
+ org.eclipse.che.core
+ che-core-api-workspace-shared
+
+
+ org.eclipse.che.core
+ che-core-commons-annotations
+
+
+ org.eclipse.che.core
+ che-core-commons-lang
+
+
+ org.slf4j
+ slf4j-api
+
+
+
diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubFactoryParametersResolver.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubFactoryParametersResolver.java
new file mode 100644
index 0000000000..2a4652ec55
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubFactoryParametersResolver.java
@@ -0,0 +1,189 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import static java.util.Collections.emptyMap;
+import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
+import static org.eclipse.che.dto.server.DtoFactory.newDto;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.Map;
+import org.eclipse.che.api.core.ApiException;
+import org.eclipse.che.api.core.BadRequestException;
+import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
+import org.eclipse.che.api.factory.server.FactoryParametersResolver;
+import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
+import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
+import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
+import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
+import org.eclipse.che.api.factory.shared.dto.*;
+import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
+import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
+import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
+import org.eclipse.che.security.oauth.AuthorisationRequestManager;
+
+/**
+ * Provides Factory Parameters resolver for github repositories.
+ *
+ * @author Florent Benoit
+ */
+public abstract class AbstractGithubFactoryParametersResolver extends BaseFactoryParameterResolver
+ implements FactoryParametersResolver {
+
+ /** Parser which will allow to check validity of URLs and create objects. */
+ private final AbstractGithubURLParser githubUrlParser;
+
+ private final URLFetcher urlFetcher;
+
+ /** Builder allowing to build objects from github URL. */
+ private final GithubSourceStorageBuilder githubSourceStorageBuilder;
+
+ private final URLFactoryBuilder urlFactoryBuilder;
+
+ /** ProjectDtoMerger */
+ private final ProjectConfigDtoMerger projectConfigDtoMerger;
+
+ private final PersonalAccessTokenManager personalAccessTokenManager;
+
+ private final String providerName;
+
+ public AbstractGithubFactoryParametersResolver(
+ AbstractGithubURLParser githubUrlParser,
+ URLFetcher urlFetcher,
+ GithubSourceStorageBuilder githubSourceStorageBuilder,
+ AuthorisationRequestManager authorisationRequestManager,
+ URLFactoryBuilder urlFactoryBuilder,
+ ProjectConfigDtoMerger projectConfigDtoMerger,
+ PersonalAccessTokenManager personalAccessTokenManager,
+ String providerName) {
+ super(authorisationRequestManager, urlFactoryBuilder, providerName);
+ this.providerName = providerName;
+ this.githubUrlParser = githubUrlParser;
+ this.urlFetcher = urlFetcher;
+ this.githubSourceStorageBuilder = githubSourceStorageBuilder;
+ this.urlFactoryBuilder = urlFactoryBuilder;
+ this.projectConfigDtoMerger = projectConfigDtoMerger;
+ this.personalAccessTokenManager = personalAccessTokenManager;
+ }
+
+ /**
+ * Check if this resolver can be used with the given parameters.
+ *
+ * @param factoryParameters map of parameters dedicated to factories
+ * @return true if it will be accepted by the resolver implementation or false if it is not
+ * accepted
+ */
+ @Override
+ public boolean accept(@NotNull final Map factoryParameters) {
+ // Check if url parameter is a github URL
+ return factoryParameters.containsKey(URL_PARAMETER_NAME)
+ && githubUrlParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
+ }
+
+ @Override
+ public String getProviderName() {
+ return providerName;
+ }
+
+ /**
+ * Create factory object based on provided parameters
+ *
+ * @param factoryParameters map containing factory data parameters provided through URL
+ * @throws BadRequestException when data are invalid
+ */
+ @Override
+ public FactoryMetaDto createFactory(@NotNull final Map factoryParameters)
+ throws ApiException {
+ // no need to check null value of url parameter as accept() method has performed the check
+ final GithubUrl githubUrl;
+ if (getSkipAuthorisation(factoryParameters)) {
+ githubUrl =
+ githubUrlParser.parseWithoutAuthentication(factoryParameters.get(URL_PARAMETER_NAME));
+ } else {
+ githubUrl = githubUrlParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
+ }
+
+ return createFactory(
+ factoryParameters,
+ githubUrl,
+ new GithubFactoryVisitor(githubUrl),
+ new GithubAuthorizingFileContentProvider(
+ githubUrl, urlFetcher, personalAccessTokenManager));
+ }
+
+ /**
+ * Visitor that puts the default devfile or updates devfile projects into the Github Factory, if
+ * needed.
+ */
+ private class GithubFactoryVisitor implements FactoryVisitor {
+
+ private final GithubUrl githubUrl;
+
+ private GithubFactoryVisitor(GithubUrl githubUrl) {
+ this.githubUrl = githubUrl;
+ }
+
+ @Override
+ public FactoryDevfileV2Dto visit(FactoryDevfileV2Dto factoryDto) {
+ ScmInfoDto scmInfo =
+ newDto(ScmInfoDto.class)
+ .withScmProviderName(githubUrl.getProviderName())
+ .withRepositoryUrl(githubUrl.repositoryLocation());
+ if (githubUrl.getBranch() != null) {
+ scmInfo.withBranch(githubUrl.getBranch());
+ }
+ return factoryDto.withScmInfo(scmInfo);
+ }
+
+ @Override
+ public FactoryDto visit(FactoryDto factory) {
+ if (factory.getWorkspace() != null) {
+ return projectConfigDtoMerger.merge(
+ factory,
+ () -> {
+ // Compute project configuration
+ return newDto(ProjectConfigDto.class)
+ .withSource(githubSourceStorageBuilder.buildWorkspaceConfigSource(githubUrl))
+ .withName(githubUrl.getRepository())
+ .withPath("/".concat(githubUrl.getRepository()));
+ });
+ } else if (factory.getDevfile() == null) {
+ // initialize default devfile
+ factory.setDevfile(urlFactoryBuilder.buildDefaultDevfile(githubUrl.getRepository()));
+ }
+
+ updateProjects(
+ factory.getDevfile(),
+ () ->
+ newDto(ProjectDto.class)
+ .withSource(githubSourceStorageBuilder.buildDevfileSource(githubUrl))
+ .withName(githubUrl.getRepository()),
+ project -> {
+ final String location = project.getSource().getLocation();
+ if (location.equals(githubUrl.repositoryLocation())) {
+ project.getSource().setBranch(githubUrl.getBranch());
+ }
+ });
+
+ return factory;
+ }
+ }
+
+ @Override
+ public RemoteFactoryUrl parseFactoryUrl(String factoryUrl) throws ApiException {
+ if (getSkipAuthorisation(emptyMap())) {
+ return githubUrlParser.parseWithoutAuthentication(factoryUrl);
+ } else {
+ return githubUrlParser.parse(factoryUrl);
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubPersonalAccessTokenFetcher.java
new file mode 100644
index 0000000000..76ce31d4c7
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubPersonalAccessTokenFetcher.java
@@ -0,0 +1,263 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.che.api.auth.shared.dto.OAuthToken;
+import org.eclipse.che.api.core.*;
+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.PersonalAccessTokenParams;
+import org.eclipse.che.api.factory.server.scm.exception.*;
+import org.eclipse.che.commons.lang.NameGenerator;
+import org.eclipse.che.commons.lang.Pair;
+import org.eclipse.che.commons.subject.Subject;
+import org.eclipse.che.security.oauth.OAuthAPI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** GitHub OAuth token retriever. */
+public abstract class AbstractGithubPersonalAccessTokenFetcher
+ implements PersonalAccessTokenFetcher {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(AbstractGithubPersonalAccessTokenFetcher.class);
+ private final String apiEndpoint;
+ private final OAuthAPI oAuthAPI;
+
+ /** GitHub API client. */
+ private final GithubApiClient githubApiClient;
+
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private final String providerName;
+
+ /** Collection of OAuth scopes required to make integration with GitHub work. */
+ public static final Set DEFAULT_TOKEN_SCOPES =
+ ImmutableSet.of("repo", "user:email", "read:user", "read:org", "workflow");
+
+ /**
+ * Map of OAuth GitHub scopes where each key is a scope and its value is the parent scope. The
+ * parent scope includes all of its children scopes. This map is used when determining if a token
+ * has the required scopes. See
+ * https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes
+ */
+ private static final Map SCOPE_MAP =
+ ImmutableMap.builderWithExpectedSize(35)
+ .put("repo", "repo")
+ .put("repo:status", "repo")
+ .put("repo_deployment", "repo")
+ .put("public_repo", "repo")
+ .put("repo:invite", "repo")
+ .put("security_events", "repo")
+ //
+ .put("workflow", "workflow")
+ //
+ .put("write:packages", "write:packages")
+ .put("read:packages", "write:packages")
+ //
+ .put("delete:packages", "delete:packages")
+ //
+ .put("admin:org", "admin:org")
+ .put("write:org", "admin:org")
+ .put("read:org", "admin:org")
+ //
+ .put("admin:public_key", "admin:public_key")
+ .put("write:public_key", "admin:public_key")
+ .put("read:public_key", "admin:public_key")
+ //
+ .put("admin:repo_hook", "admin:repo_hook")
+ .put("write:repo_hook", "admin:repo_hook")
+ .put("read:repo_hook", "admin:repo_hook")
+ //
+ .put("admin:org_hook", "admin:org_hook")
+ //
+ .put("gist", "gist")
+ //
+ .put("notifications", "notifications")
+ //
+ .put("user", "user")
+ .put("read:user", "user")
+ .put("user:email", "user")
+ .put("user:follow", "user")
+ //
+ .put("delete_repo", "delete_repo")
+ //
+ .put("write:discussion", "write:discussion")
+ .put("read:discussion", "write:discussion")
+ //
+ .put("admin:enterprise", "admin:enterprise")
+ .put("manage_billing:enterprise", "admin:enterprise")
+ .put("read:enterprise", "admin:enterprise")
+ //
+ .put("admin:gpg_key", "admin:gpg_key")
+ .put("write:gpg_key", "admin:gpg_key")
+ .put("read:gpg_key", "admin:gpg_key")
+ .build();
+
+ /**
+ * Constructor used for testing only.
+ *
+ * @param apiEndpoint
+ * @param oAuthAPI
+ * @param githubApiClient
+ */
+ AbstractGithubPersonalAccessTokenFetcher(
+ String apiEndpoint, OAuthAPI oAuthAPI, GithubApiClient githubApiClient, String providerName) {
+ this.apiEndpoint = apiEndpoint;
+ this.oAuthAPI = oAuthAPI;
+ this.githubApiClient = githubApiClient;
+ this.providerName = providerName;
+ }
+
+ @Override
+ public PersonalAccessToken fetchPersonalAccessToken(Subject cheSubject, String scmServerUrl)
+ throws ScmUnauthorizedException, ScmCommunicationException, UnknownScmProviderException {
+ OAuthToken oAuthToken;
+
+ if (githubApiClient == null || !githubApiClient.isConnected(scmServerUrl)) {
+ LOG.debug("not a valid url {} for current fetcher ", scmServerUrl);
+ return null;
+ }
+ try {
+ oAuthToken = oAuthAPI.getToken(providerName);
+ String tokenName = NameGenerator.generate(OAUTH_2_PREFIX, 5);
+ String tokenId = NameGenerator.generate("id-", 5);
+ Optional> valid =
+ isValid(
+ new PersonalAccessTokenParams(
+ scmServerUrl, tokenName, tokenId, oAuthToken.getToken(), null));
+ if (valid.isEmpty()) {
+ throw buildScmUnauthorizedException(cheSubject);
+ } else if (!valid.get().first) {
+ throw new ScmCommunicationException(
+ "Current token doesn't have the necessary privileges. Please make sure Che app scopes are correct and containing at least: "
+ + DEFAULT_TOKEN_SCOPES.toString());
+ }
+ return new PersonalAccessToken(
+ scmServerUrl,
+ cheSubject.getUserId(),
+ valid.get().second,
+ tokenName,
+ tokenId,
+ oAuthToken.getToken());
+ } catch (UnauthorizedException e) {
+ throw buildScmUnauthorizedException(cheSubject);
+ } catch (NotFoundException nfe) {
+ throw new UnknownScmProviderException(nfe.getMessage(), scmServerUrl);
+ } catch (ServerException | ForbiddenException | BadRequestException | ConflictException e) {
+ LOG.error(e.getMessage());
+ throw new ScmCommunicationException(e.getMessage(), e);
+ }
+ }
+
+ private ScmUnauthorizedException buildScmUnauthorizedException(Subject cheSubject) {
+ return new ScmUnauthorizedException(
+ cheSubject.getUserName()
+ + " is not authorized in "
+ + this.providerName
+ + " OAuth provider.",
+ this.providerName,
+ "2.0",
+ getLocalAuthenticateUrl());
+ }
+
+ @Override
+ @Deprecated
+ public Optional isValid(PersonalAccessToken personalAccessToken) {
+ if (!githubApiClient.isConnected(personalAccessToken.getScmProviderUrl())) {
+ LOG.debug("not a valid url {} for current fetcher ", personalAccessToken.getScmProviderUrl());
+ return Optional.empty();
+ }
+
+ try {
+ if (personalAccessToken.getScmTokenName() != null
+ && personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX)) {
+ String[] scopes = githubApiClient.getTokenScopes(personalAccessToken.getToken()).second;
+ return Optional.of(containsScopes(scopes, DEFAULT_TOKEN_SCOPES));
+ } else {
+ // No REST API for PAT-s in Github found yet. Just try to do some action.
+ GithubUser user = githubApiClient.getUser(personalAccessToken.getToken());
+ if (personalAccessToken.getScmUserName().equals(user.getLogin())) {
+ return Optional.of(Boolean.TRUE);
+ } else {
+ return Optional.of(Boolean.FALSE);
+ }
+ }
+ } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) {
+ return Optional.of(Boolean.FALSE);
+ }
+ }
+
+ @Override
+ public Optional> isValid(PersonalAccessTokenParams params) {
+ if (!githubApiClient.isConnected(params.getScmProviderUrl())) {
+ LOG.debug("not a valid url {} for current fetcher ", params.getScmProviderUrl());
+ return Optional.empty();
+ }
+ try {
+ if (params.getScmTokenName() != null && params.getScmTokenName().startsWith(OAUTH_2_PREFIX)) {
+ Pair pair = githubApiClient.getTokenScopes(params.getToken());
+ return Optional.of(
+ Pair.of(
+ containsScopes(pair.second, DEFAULT_TOKEN_SCOPES) ? Boolean.TRUE : Boolean.FALSE,
+ pair.first));
+ } else {
+ // TODO: add PAT scope validation
+ // No REST API for PAT-s in Github found yet. Just try to do some action.
+ GithubUser user = githubApiClient.getUser(params.getToken());
+ return Optional.of(Pair.of(Boolean.TRUE, user.getLogin()));
+ }
+ } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Checks if the tokenScopes array contains the requiredScopes.
+ *
+ * @param tokenScopes Scopes from token
+ * @param requiredScopes Mandatory scopes
+ * @return If all mandatory scopes are contained in the token's scopes
+ */
+ boolean containsScopes(String[] tokenScopes, Set requiredScopes) {
+ Arrays.sort(tokenScopes);
+ // We need check that the token has the required minimal scopes. The scopes can be normalized
+ // by GitHub, so we need to be careful for sub-scopes being included in parent scopes.
+ for (String requiredScope : requiredScopes) {
+ String parentScope = SCOPE_MAP.get(requiredScope);
+ if (parentScope == null) {
+ // requiredScope is not recognized as a GitHub scope, so just skip it.
+ continue;
+ }
+ if (Arrays.binarySearch(tokenScopes, parentScope) < 0
+ && Arrays.binarySearch(tokenScopes, requiredScope) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String getLocalAuthenticateUrl() {
+ return apiEndpoint
+ + "/oauth/authenticate?oauth_provider="
+ + providerName
+ + "&scope="
+ + Joiner.on(',').join(DEFAULT_TOKEN_SCOPES)
+ + "&request_method=POST&signature_method=rsa";
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubScmFileResolver.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubScmFileResolver.java
new file mode 100644
index 0000000000..8eb8016ff4
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubScmFileResolver.java
@@ -0,0 +1,77 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import static org.eclipse.che.api.factory.server.ApiExceptionMapper.toApiException;
+
+import jakarta.validation.constraints.NotNull;
+import java.io.IOException;
+import org.eclipse.che.api.core.ApiException;
+import org.eclipse.che.api.core.NotFoundException;
+import org.eclipse.che.api.factory.server.ScmFileResolver;
+import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
+import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
+import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
+
+/** Github specific SCM file resolver. */
+public abstract class AbstractGithubScmFileResolver implements ScmFileResolver {
+
+ private final AbstractGithubURLParser githubUrlParser;
+ private final URLFetcher urlFetcher;
+ private final PersonalAccessTokenManager personalAccessTokenManager;
+
+ public AbstractGithubScmFileResolver(
+ AbstractGithubURLParser githubUrlParser,
+ URLFetcher urlFetcher,
+ PersonalAccessTokenManager personalAccessTokenManager) {
+ this.githubUrlParser = githubUrlParser;
+ this.urlFetcher = urlFetcher;
+ this.personalAccessTokenManager = personalAccessTokenManager;
+ }
+
+ @Override
+ public boolean accept(@NotNull String repository) {
+ // Check if repository parameter is a github URL
+ return githubUrlParser.isValid(repository);
+ }
+
+ @Override
+ public String fileContent(@NotNull String repository, @NotNull String filePath)
+ throws ApiException {
+ final GithubUrl githubUrl = githubUrlParser.parse(repository);
+ try {
+ return fetchContent(githubUrl, filePath, false);
+ } catch (DevfileException exception) {
+ // This catch might mean that the authentication was rejected by user, try to repeat the fetch
+ // without authentication flow.
+ try {
+ return fetchContent(githubUrl, filePath, true);
+ } catch (DevfileException devfileException) {
+ throw toApiException(devfileException);
+ }
+ }
+ }
+
+ private String fetchContent(GithubUrl githubUrl, String filePath, boolean skipAuthentication)
+ throws DevfileException, NotFoundException {
+ try {
+ GithubAuthorizingFileContentProvider contentProvider =
+ new GithubAuthorizingFileContentProvider(
+ githubUrl, urlFetcher, personalAccessTokenManager);
+ return skipAuthentication
+ ? contentProvider.fetchContentWithoutAuthentication(filePath)
+ : contentProvider.fetchContent(filePath);
+ } catch (IOException e) {
+ throw new NotFoundException(e.getMessage());
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubURLParser.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubURLParser.java
new file mode 100644
index 0000000000..94e55d4391
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubURLParser.java
@@ -0,0 +1,256 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+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 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.net.URI;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.eclipse.che.api.core.ApiException;
+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.*;
+import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.commons.subject.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Parser of String Github URLs and provide {@link GithubUrl} objects.
+ *
+ * @author Florent Benoit
+ */
+public abstract class AbstractGithubURLParser {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractGithubURLParser.class);
+ 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;
+
+ private final Pattern githubSSHPattern;
+
+ private final boolean disableSubdomainIsolation;
+
+ private final String providerName;
+
+ /** Constructor used for testing only. */
+ AbstractGithubURLParser(
+ PersonalAccessTokenManager tokenManager,
+ DevfileFilenamesProvider devfileFilenamesProvider,
+ GithubApiClient githubApiClient,
+ String oauthEndpoint,
+ boolean disableSubdomainIsolation,
+ String providerName) {
+ this.tokenManager = tokenManager;
+ this.devfileFilenamesProvider = devfileFilenamesProvider;
+ this.apiClient = githubApiClient;
+ this.oauthEndpoint = oauthEndpoint;
+ this.disableSubdomainIsolation = disableSubdomainIsolation;
+ this.providerName = providerName;
+
+ String endpoint =
+ isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
+
+ this.githubPattern =
+ compile(
+ format(
+ "^%s/(?[^/]+)/(?[^/]++)((/)|(?:/tree/(?.++))|(/pull/(?\\d++)))?$",
+ endpoint));
+ this.githubSSHPattern =
+ compile(format("^git@%s:(?.*)/(?.*)$", URI.create(endpoint).getHost()));
+ }
+
+ public boolean isValid(@NotNull String url) {
+ String trimmedUrl = trimEnd(url, '/');
+ return githubPattern.matcher(trimmedUrl).matches()
+ || githubSSHPattern.matcher(trimmedUrl).matches();
+ }
+
+ public GithubUrl parseWithoutAuthentication(String url) throws ApiException {
+ return parse(trimEnd(url, '/'), false);
+ }
+
+ public GithubUrl parse(String url) throws ApiException {
+ return parse(trimEnd(url, '/'), true);
+ }
+
+ private GithubUrl parse(String url, boolean authenticationRequired) throws ApiException {
+ boolean isHTTPSUrl = githubPattern.matcher(url).matches();
+ Matcher matcher = isHTTPSUrl ? githubPattern.matcher(url) : githubSSHPattern.matcher(url);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException(
+ format("The given url %s is not a valid github URL. ", 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$")) {
+ repoName = repoName.substring(0, repoName.length() - 4);
+ }
+
+ String branchName = null;
+ String pullRequestId = null;
+ if (isHTTPSUrl) {
+ branchName = matcher.group("branchName");
+ pullRequestId = matcher.group("pullRequestId");
+ }
+
+ if (pullRequestId != null) {
+ GithubPullRequest pullRequest =
+ this.getPullRequest(pullRequestId, repoUser, repoName, authenticationRequired);
+ if (pullRequest != null) {
+ String state = pullRequest.getState();
+ if (!"open".equalsIgnoreCase(state)) {
+ throw new IllegalArgumentException(
+ 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, state));
+ }
+
+ GithubHead pullRequestHead = pullRequest.getHead();
+ repoUser = pullRequestHead.getUser().getLogin();
+ repoName = pullRequestHead.getRepo().getName();
+ branchName = pullRequestHead.getRef();
+ }
+ }
+
+ String latestCommit = null;
+ GithubCommit commit =
+ this.getLatestCommit(
+ repoUser, repoName, firstNonNull(branchName, "HEAD"), authenticationRequired);
+ if (commit != null) {
+ latestCommit = commit.getSha();
+ }
+
+ return new GithubUrl(providerName)
+ .withUsername(repoUser)
+ .withRepository(repoName)
+ .setIsHTTPSUrl(isHTTPSUrl)
+ .withServerUrl(serverUrl)
+ .withDisableSubdomainIsolation(disableSubdomainIsolation)
+ .withBranch(branchName)
+ .withLatestCommit(latestCommit)
+ .withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames())
+ .withUrl(url);
+ }
+
+ private GithubPullRequest getPullRequest(
+ String pullRequestId, String repoUser, String repoName, boolean authenticationRequired)
+ throws ApiException {
+ try {
+ // prepare token
+ String githubEndpoint =
+ isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
+ Subject subject = EnvironmentContext.getCurrent().getSubject();
+ PersonalAccessToken personalAccessToken = null;
+ Optional token = tokenManager.get(subject, githubEndpoint);
+ if (token.isPresent()) {
+ personalAccessToken = token.get();
+ } else if (authenticationRequired) {
+ personalAccessToken = tokenManager.fetchAndSave(subject, githubEndpoint);
+ }
+
+ // get pull request
+ return this.apiClient.getPullRequest(
+ pullRequestId,
+ repoUser,
+ repoName,
+ personalAccessToken != null ? personalAccessToken.getToken() : null);
+ } catch (UnknownScmProviderException e) {
+
+ // get pull request without authentication
+ try {
+ return this.apiClient.getPullRequest(pullRequestId, repoUser, repoName, null);
+ } catch (ScmItemNotFoundException
+ | ScmCommunicationException
+ | ScmBadRequestException exception) {
+ LOG.error("Failed to authenticate to GitHub", e);
+ }
+
+ } catch (ScmUnauthorizedException e) {
+ throw toApiException(e);
+ } catch (ScmCommunicationException
+ | UnsatisfiedScmPreconditionException
+ | ScmConfigurationPersistenceException e) {
+ LOG.error("Failed to authenticate to GitHub", e);
+ } catch (ScmItemNotFoundException | ScmBadRequestException e) {
+ LOG.error("Failed retrieve GitHub Pull Request", e);
+ }
+
+ return null;
+ }
+
+ private GithubCommit getLatestCommit(
+ String repoUser, String repoName, String branchName, boolean authenticationRequired)
+ throws ApiException {
+ try {
+ // prepare token
+ String githubEndpoint =
+ isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
+ Subject subject = EnvironmentContext.getCurrent().getSubject();
+ PersonalAccessToken personalAccessToken = null;
+ Optional token = tokenManager.get(subject, githubEndpoint);
+ if (token.isPresent()) {
+ personalAccessToken = token.get();
+ } else if (authenticationRequired) {
+ personalAccessToken = tokenManager.fetchAndSave(subject, githubEndpoint);
+ }
+
+ // get latest commit
+ return this.apiClient.getLatestCommit(
+ repoUser,
+ repoName,
+ branchName,
+ personalAccessToken != null ? personalAccessToken.getToken() : null);
+ } catch (UnknownScmProviderException | ScmUnauthorizedException e) {
+ // get latest commit without authentication
+ try {
+ return this.apiClient.getLatestCommit(repoUser, repoName, branchName, null);
+ } catch (ScmItemNotFoundException
+ | ScmCommunicationException
+ | ScmBadRequestException
+ | URISyntaxException exception) {
+ LOG.error("Failed to authenticate to GitHub", e);
+ }
+ } catch (ScmCommunicationException
+ | UnsatisfiedScmPreconditionException
+ | ScmConfigurationPersistenceException e) {
+ LOG.error("Failed to authenticate to GitHub", e);
+ } catch (ScmItemNotFoundException | ScmBadRequestException | URISyntaxException e) {
+ LOG.error("Failed to retrieve the latest commit", e);
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubUserDataFetcher.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubUserDataFetcher.java
new file mode 100644
index 0000000000..7ab4c7f2cc
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/AbstractGithubUserDataFetcher.java
@@ -0,0 +1,75 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.eclipse.che.api.auth.shared.dto.OAuthToken;
+import org.eclipse.che.api.factory.server.scm.AbstractGitUserDataFetcher;
+import org.eclipse.che.api.factory.server.scm.GitUserData;
+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.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.security.oauth.OAuthAPI;
+
+/** GitHub user data retriever. */
+public abstract class AbstractGithubUserDataFetcher extends AbstractGitUserDataFetcher {
+ private final String apiEndpoint;
+ /** GitHub API client. */
+ private final GithubApiClient githubApiClient;
+
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private final String providerName;
+ /** Collection of OAuth scopes required to make integration with GitHub work. */
+ public static final Set DEFAULT_TOKEN_SCOPES =
+ ImmutableSet.of("repo", "user:email", "read:user");
+
+ /** Constructor used for testing only. */
+ public AbstractGithubUserDataFetcher(
+ String apiEndpoint,
+ OAuthAPI oAuthTokenFetcher,
+ PersonalAccessTokenManager personalAccessTokenManager,
+ GithubApiClient githubApiClient,
+ String providerName) {
+ super(providerName, personalAccessTokenManager, oAuthTokenFetcher);
+ this.providerName = providerName;
+ this.githubApiClient = githubApiClient;
+ this.apiEndpoint = apiEndpoint;
+ }
+
+ @Override
+ protected GitUserData fetchGitUserDataWithOAuthToken(OAuthToken oAuthToken)
+ throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
+ GithubUser user = githubApiClient.getUser(oAuthToken.getToken());
+ return new GitUserData(user.getName(), user.getEmail());
+ }
+
+ @Override
+ protected GitUserData fetchGitUserDataWithPersonalAccessToken(
+ PersonalAccessToken personalAccessToken)
+ throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
+ GithubUser user = githubApiClient.getUser(personalAccessToken.getToken());
+ return new GitUserData(user.getName(), user.getEmail());
+ }
+
+ protected String getLocalAuthenticateUrl() {
+ return apiEndpoint
+ + "/oauth/authenticate?oauth_provider="
+ + providerName
+ + "&scope="
+ + Joiner.on(',').join(DEFAULT_TOKEN_SCOPES)
+ + "&request_method=POST&signature_method=rsa";
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java
similarity index 100%
rename from wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java
rename to wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubApiClient.java
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProvider.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProvider.java
similarity index 100%
rename from wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProvider.java
rename to wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProvider.java
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubCommit.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubCommit.java
similarity index 100%
rename from wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubCommit.java
rename to wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubCommit.java
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPullRequest.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubPullRequest.java
similarity index 100%
rename from wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPullRequest.java
rename to wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubPullRequest.java
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubSourceStorageBuilder.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubSourceStorageBuilder.java
similarity index 100%
rename from wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubSourceStorageBuilder.java
rename to wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubSourceStorageBuilder.java
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java
similarity index 97%
rename from wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java
rename to wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java
index 148bae9bf4..fcd106ef0f 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java
+++ b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java
@@ -29,8 +29,7 @@ import org.eclipse.che.api.factory.server.urlfactory.DefaultFactoryUrl;
* @author Florent Benoit
*/
public class GithubUrl extends DefaultFactoryUrl {
-
- private final String NAME = "github";
+ private final String providerName;
private static final String HOSTNAME = "https://github.com";
@@ -59,11 +58,13 @@ public class GithubUrl extends DefaultFactoryUrl {
* Creation of this instance is made by the parser so user may not need to create a new instance
* directly
*/
- protected GithubUrl() {}
+ protected GithubUrl(String providerName) {
+ this.providerName = providerName;
+ }
@Override
public String getProviderName() {
- return NAME;
+ return providerName;
}
/**
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUser.java b/wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubUser.java
similarity index 100%
rename from wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUser.java
rename to wsmaster/che-core-api-factory-github-common/src/main/java/org/eclipse/che/api/factory/server/github/GithubUser.java
diff --git a/wsmaster/che-core-api-factory-github/pom.xml b/wsmaster/che-core-api-factory-github/pom.xml
index bddfacee81..8afaf403a4 100644
--- a/wsmaster/che-core-api-factory-github/pom.xml
+++ b/wsmaster/che-core-api-factory-github/pom.xml
@@ -26,14 +26,6 @@
true
-
- com.fasterxml.jackson.core
- jackson-annotations
-
-
- com.fasterxml.jackson.core
- jackson-databind
-
com.google.guava
guava
@@ -46,10 +38,6 @@
jakarta.inject
jakarta.inject-api
-
- jakarta.validation
- jakarta.validation-api
-
org.eclipse.che.core
che-core-api-auth
@@ -70,6 +58,10 @@
org.eclipse.che.core
che-core-api-factory
+
+ org.eclipse.che.core
+ che-core-api-factory-github-common
+
org.eclipse.che.core
che-core-api-factory-shared
@@ -94,10 +86,6 @@
org.eclipse.che.core
che-core-commons-lang
-
- org.slf4j
- slf4j-api
-
ch.qos.logback
logback-classic
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolver.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolver.java
index f769f8fb32..5721690855 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolver.java
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolver.java
@@ -11,30 +11,12 @@
*/
package org.eclipse.che.api.factory.server.github;
-import static java.util.Collections.emptyMap;
-import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
-import static org.eclipse.che.dto.server.DtoFactory.newDto;
-
-import jakarta.validation.constraints.NotNull;
-import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
-import org.eclipse.che.api.core.ApiException;
-import org.eclipse.che.api.core.BadRequestException;
-import org.eclipse.che.api.factory.server.BaseFactoryParameterResolver;
-import org.eclipse.che.api.factory.server.FactoryParametersResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
-import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
-import org.eclipse.che.api.factory.shared.dto.FactoryDevfileV2Dto;
-import org.eclipse.che.api.factory.shared.dto.FactoryDto;
-import org.eclipse.che.api.factory.shared.dto.FactoryMetaDto;
-import org.eclipse.che.api.factory.shared.dto.FactoryVisitor;
-import org.eclipse.che.api.factory.shared.dto.ScmInfoDto;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
-import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
-import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
import org.eclipse.che.security.oauth.AuthorisationRequestManager;
/**
@@ -43,26 +25,10 @@ import org.eclipse.che.security.oauth.AuthorisationRequestManager;
* @author Florent Benoit
*/
@Singleton
-public class GithubFactoryParametersResolver extends BaseFactoryParameterResolver
- implements FactoryParametersResolver {
+public class GithubFactoryParametersResolver extends AbstractGithubFactoryParametersResolver {
private static final String PROVIDER_NAME = "github";
- /** Parser which will allow to check validity of URLs and create objects. */
- private final GithubURLParser githubUrlParser;
-
- private final URLFetcher urlFetcher;
-
- /** Builder allowing to build objects from github URL. */
- private final GithubSourceStorageBuilder githubSourceStorageBuilder;
-
- private final URLFactoryBuilder urlFactoryBuilder;
-
- /** ProjectDtoMerger */
- private final ProjectConfigDtoMerger projectConfigDtoMerger;
-
- private final PersonalAccessTokenManager personalAccessTokenManager;
-
@Inject
public GithubFactoryParametersResolver(
GithubURLParser githubUrlParser,
@@ -72,124 +38,14 @@ public class GithubFactoryParametersResolver extends BaseFactoryParameterResolve
URLFactoryBuilder urlFactoryBuilder,
ProjectConfigDtoMerger projectConfigDtoMerger,
PersonalAccessTokenManager personalAccessTokenManager) {
- super(authorisationRequestManager, urlFactoryBuilder, PROVIDER_NAME);
- this.githubUrlParser = githubUrlParser;
- this.urlFetcher = urlFetcher;
- this.githubSourceStorageBuilder = githubSourceStorageBuilder;
- this.urlFactoryBuilder = urlFactoryBuilder;
- this.projectConfigDtoMerger = projectConfigDtoMerger;
- this.personalAccessTokenManager = personalAccessTokenManager;
- }
-
- /**
- * Check if this resolver can be used with the given parameters.
- *
- * @param factoryParameters map of parameters dedicated to factories
- * @return true if it will be accepted by the resolver implementation or false if it is not
- * accepted
- */
- @Override
- public boolean accept(@NotNull final Map factoryParameters) {
- // Check if url parameter is a github URL
- return factoryParameters.containsKey(URL_PARAMETER_NAME)
- && githubUrlParser.isValid(factoryParameters.get(URL_PARAMETER_NAME));
- }
-
- @Override
- public String getProviderName() {
- return PROVIDER_NAME;
- }
-
- /**
- * Create factory object based on provided parameters
- *
- * @param factoryParameters map containing factory data parameters provided through URL
- * @throws BadRequestException when data are invalid
- */
- @Override
- public FactoryMetaDto createFactory(@NotNull final Map factoryParameters)
- throws ApiException {
- // no need to check null value of url parameter as accept() method has performed the check
- final GithubUrl githubUrl;
- if (getSkipAuthorisation(factoryParameters)) {
- githubUrl =
- githubUrlParser.parseWithoutAuthentication(factoryParameters.get(URL_PARAMETER_NAME));
- } else {
- githubUrl = githubUrlParser.parse(factoryParameters.get(URL_PARAMETER_NAME));
- }
-
- return createFactory(
- factoryParameters,
- githubUrl,
- new GithubFactoryVisitor(githubUrl),
- new GithubAuthorizingFileContentProvider(
- githubUrl, urlFetcher, personalAccessTokenManager));
- }
-
- /**
- * Visitor that puts the default devfile or updates devfile projects into the Github Factory, if
- * needed.
- */
- private class GithubFactoryVisitor implements FactoryVisitor {
-
- private final GithubUrl githubUrl;
-
- private GithubFactoryVisitor(GithubUrl githubUrl) {
- this.githubUrl = githubUrl;
- }
-
- @Override
- public FactoryDevfileV2Dto visit(FactoryDevfileV2Dto factoryDto) {
- ScmInfoDto scmInfo =
- newDto(ScmInfoDto.class)
- .withScmProviderName(githubUrl.getProviderName())
- .withRepositoryUrl(githubUrl.repositoryLocation());
- if (githubUrl.getBranch() != null) {
- scmInfo.withBranch(githubUrl.getBranch());
- }
- return factoryDto.withScmInfo(scmInfo);
- }
-
- @Override
- public FactoryDto visit(FactoryDto factory) {
- if (factory.getWorkspace() != null) {
- return projectConfigDtoMerger.merge(
- factory,
- () -> {
- // Compute project configuration
- return newDto(ProjectConfigDto.class)
- .withSource(githubSourceStorageBuilder.buildWorkspaceConfigSource(githubUrl))
- .withName(githubUrl.getRepository())
- .withPath("/".concat(githubUrl.getRepository()));
- });
- } else if (factory.getDevfile() == null) {
- // initialize default devfile
- factory.setDevfile(urlFactoryBuilder.buildDefaultDevfile(githubUrl.getRepository()));
- }
-
- updateProjects(
- factory.getDevfile(),
- () ->
- newDto(ProjectDto.class)
- .withSource(githubSourceStorageBuilder.buildDevfileSource(githubUrl))
- .withName(githubUrl.getRepository()),
- project -> {
- final String location = project.getSource().getLocation();
- if (location.equals(githubUrl.repositoryLocation())) {
- project.getSource().setBranch(githubUrl.getBranch());
- }
- });
-
- return factory;
- }
- }
-
- @Override
- public RemoteFactoryUrl parseFactoryUrl(String factoryUrl) throws ApiException {
- if (getSkipAuthorisation(emptyMap())) {
- return githubUrlParser.parseWithoutAuthentication(factoryUrl);
- } else {
- return githubUrlParser.parse(factoryUrl);
- }
+ super(
+ githubUrlParser,
+ urlFetcher,
+ githubSourceStorageBuilder,
+ authorisationRequestManager,
+ urlFactoryBuilder,
+ projectConfigDtoMerger,
+ personalAccessTokenManager,
+ PROVIDER_NAME);
}
}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolverSecond.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolverSecond.java
new file mode 100644
index 0000000000..5103711d48
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolverSecond.java
@@ -0,0 +1,51 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
+import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
+import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
+import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
+import org.eclipse.che.security.oauth.AuthorisationRequestManager;
+
+/**
+ * Provides Factory Parameters resolver for github repositories.
+ *
+ * @author Florent Benoit
+ */
+@Singleton
+public class GithubFactoryParametersResolverSecond extends AbstractGithubFactoryParametersResolver {
+
+ private static final String PROVIDER_NAME = "github_2";
+
+ @Inject
+ public GithubFactoryParametersResolverSecond(
+ GithubURLParserSecond githubUrlParser,
+ URLFetcher urlFetcher,
+ GithubSourceStorageBuilder githubSourceStorageBuilder,
+ AuthorisationRequestManager authorisationRequestManager,
+ URLFactoryBuilder urlFactoryBuilder,
+ ProjectConfigDtoMerger projectConfigDtoMerger,
+ PersonalAccessTokenManager personalAccessTokenManager) {
+ super(
+ githubUrlParser,
+ urlFetcher,
+ githubSourceStorageBuilder,
+ authorisationRequestManager,
+ urlFactoryBuilder,
+ projectConfigDtoMerger,
+ personalAccessTokenManager,
+ PROVIDER_NAME);
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java
index b5254194ac..e753514887 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java
@@ -23,8 +23,11 @@ public class GithubModule extends AbstractModule {
Multibinder tokenFetcherMultibinder =
Multibinder.newSetBinder(binder(), PersonalAccessTokenFetcher.class);
tokenFetcherMultibinder.addBinding().to(GithubPersonalAccessTokenFetcher.class);
+ tokenFetcherMultibinder.addBinding().to(GithubPersonalAccessTokenFetcherSecond.class);
+
Multibinder gitUserDataMultibinder =
Multibinder.newSetBinder(binder(), GitUserDataFetcher.class);
gitUserDataMultibinder.addBinding().to(GithubUserDataFetcher.class);
+ gitUserDataMultibinder.addBinding().to(GithubUserDataFetcherSecond.class);
}
}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcher.java
index 2508a0020d..7d28581f57 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcher.java
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcher.java
@@ -11,270 +11,27 @@
*/
package org.eclipse.che.api.factory.server.github;
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
-import org.eclipse.che.api.auth.shared.dto.OAuthToken;
-import org.eclipse.che.api.core.BadRequestException;
-import org.eclipse.che.api.core.ConflictException;
-import org.eclipse.che.api.core.ForbiddenException;
-import org.eclipse.che.api.core.NotFoundException;
-import org.eclipse.che.api.core.ServerException;
-import org.eclipse.che.api.core.UnauthorizedException;
-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.PersonalAccessTokenParams;
-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.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.lang.Pair;
-import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.security.oauth.OAuthAPI;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/** GitHub OAuth token retriever. */
-public class GithubPersonalAccessTokenFetcher implements PersonalAccessTokenFetcher {
-
- private static final Logger LOG = LoggerFactory.getLogger(GithubPersonalAccessTokenFetcher.class);
- private final String apiEndpoint;
- private final OAuthAPI oAuthAPI;
-
- /** GitHub API client. */
- private final GithubApiClient githubApiClient;
+public class GithubPersonalAccessTokenFetcher extends AbstractGithubPersonalAccessTokenFetcher {
/** Name of this OAuth provider as found in OAuthAPI. */
private static final String OAUTH_PROVIDER_NAME = "github";
- /** Collection of OAuth scopes required to make integration with GitHub work. */
- public static final Set DEFAULT_TOKEN_SCOPES =
- ImmutableSet.of("repo", "user:email", "read:user", "read:org", "workflow");
-
- /**
- * Map of OAuth GitHub scopes where each key is a scope and its value is the parent scope. The
- * parent scope includes all of its children scopes. This map is used when determining if a token
- * has the required scopes. See
- * https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes
- */
- private static final Map SCOPE_MAP =
- ImmutableMap.builderWithExpectedSize(35)
- .put("repo", "repo")
- .put("repo:status", "repo")
- .put("repo_deployment", "repo")
- .put("public_repo", "repo")
- .put("repo:invite", "repo")
- .put("security_events", "repo")
- //
- .put("workflow", "workflow")
- //
- .put("write:packages", "write:packages")
- .put("read:packages", "write:packages")
- //
- .put("delete:packages", "delete:packages")
- //
- .put("admin:org", "admin:org")
- .put("write:org", "admin:org")
- .put("read:org", "admin:org")
- //
- .put("admin:public_key", "admin:public_key")
- .put("write:public_key", "admin:public_key")
- .put("read:public_key", "admin:public_key")
- //
- .put("admin:repo_hook", "admin:repo_hook")
- .put("write:repo_hook", "admin:repo_hook")
- .put("read:repo_hook", "admin:repo_hook")
- //
- .put("admin:org_hook", "admin:org_hook")
- //
- .put("gist", "gist")
- //
- .put("notifications", "notifications")
- //
- .put("user", "user")
- .put("read:user", "user")
- .put("user:email", "user")
- .put("user:follow", "user")
- //
- .put("delete_repo", "delete_repo")
- //
- .put("write:discussion", "write:discussion")
- .put("read:discussion", "write:discussion")
- //
- .put("admin:enterprise", "admin:enterprise")
- .put("manage_billing:enterprise", "admin:enterprise")
- .put("read:enterprise", "admin:enterprise")
- //
- .put("admin:gpg_key", "admin:gpg_key")
- .put("write:gpg_key", "admin:gpg_key")
- .put("read:gpg_key", "admin:gpg_key")
- .build();
-
@Inject
public GithubPersonalAccessTokenFetcher(
@Named("che.api") String apiEndpoint,
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint,
OAuthAPI oAuthAPI) {
- this(apiEndpoint, oAuthAPI, new GithubApiClient(oauthEndpoint));
+ super(apiEndpoint, oAuthAPI, new GithubApiClient(oauthEndpoint), OAUTH_PROVIDER_NAME);
}
- /**
- * Constructor used for testing only.
- *
- * @param apiEndpoint
- * @param oAuthAPI
- * @param githubApiClient
- */
GithubPersonalAccessTokenFetcher(
- String apiEndpoint, OAuthAPI oAuthAPI, GithubApiClient githubApiClient) {
- this.apiEndpoint = apiEndpoint;
- this.oAuthAPI = oAuthAPI;
- this.githubApiClient = githubApiClient;
- }
-
- @Override
- public PersonalAccessToken fetchPersonalAccessToken(Subject cheSubject, String scmServerUrl)
- throws ScmUnauthorizedException, ScmCommunicationException, UnknownScmProviderException {
- OAuthToken oAuthToken;
-
- if (githubApiClient == null || !githubApiClient.isConnected(scmServerUrl)) {
- LOG.debug("not a valid url {} for current fetcher ", scmServerUrl);
- return null;
- }
- try {
- oAuthToken = oAuthAPI.getToken(OAUTH_PROVIDER_NAME);
- String tokenName = NameGenerator.generate(OAUTH_2_PREFIX, 5);
- String tokenId = NameGenerator.generate("id-", 5);
- Optional> valid =
- isValid(
- new PersonalAccessTokenParams(
- scmServerUrl, tokenName, tokenId, oAuthToken.getToken(), null));
- if (valid.isEmpty()) {
- throw buildScmUnauthorizedException(cheSubject);
- } else if (!valid.get().first) {
- throw new ScmCommunicationException(
- "Current token doesn't have the necessary privileges. Please make sure Che app scopes are correct and containing at least: "
- + DEFAULT_TOKEN_SCOPES.toString());
- }
- return new PersonalAccessToken(
- scmServerUrl,
- cheSubject.getUserId(),
- valid.get().second,
- tokenName,
- tokenId,
- oAuthToken.getToken());
- } catch (UnauthorizedException e) {
- throw buildScmUnauthorizedException(cheSubject);
- } catch (NotFoundException nfe) {
- throw new UnknownScmProviderException(nfe.getMessage(), scmServerUrl);
- } catch (ServerException | ForbiddenException | BadRequestException | ConflictException e) {
- LOG.error(e.getMessage());
- throw new ScmCommunicationException(e.getMessage(), e);
- }
- }
-
- private ScmUnauthorizedException buildScmUnauthorizedException(Subject cheSubject) {
- return new ScmUnauthorizedException(
- cheSubject.getUserName()
- + " is not authorized in "
- + OAUTH_PROVIDER_NAME
- + " OAuth provider.",
- OAUTH_PROVIDER_NAME,
- "2.0",
- getLocalAuthenticateUrl());
- }
-
- @Override
- @Deprecated
- public Optional isValid(PersonalAccessToken personalAccessToken) {
- if (!githubApiClient.isConnected(personalAccessToken.getScmProviderUrl())) {
- LOG.debug("not a valid url {} for current fetcher ", personalAccessToken.getScmProviderUrl());
- return Optional.empty();
- }
-
- try {
- if (personalAccessToken.getScmTokenName() != null
- && personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX)) {
- String[] scopes = githubApiClient.getTokenScopes(personalAccessToken.getToken()).second;
- return Optional.of(containsScopes(scopes, DEFAULT_TOKEN_SCOPES));
- } else {
- // No REST API for PAT-s in Github found yet. Just try to do some action.
- GithubUser user = githubApiClient.getUser(personalAccessToken.getToken());
- if (personalAccessToken.getScmUserName().equals(user.getLogin())) {
- return Optional.of(Boolean.TRUE);
- } else {
- return Optional.of(Boolean.FALSE);
- }
- }
- } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) {
- return Optional.of(Boolean.FALSE);
- }
- }
-
- @Override
- public Optional> isValid(PersonalAccessTokenParams params) {
- if (!githubApiClient.isConnected(params.getScmProviderUrl())) {
- LOG.debug("not a valid url {} for current fetcher ", params.getScmProviderUrl());
- return Optional.empty();
- }
- try {
- if (params.getScmTokenName() != null && params.getScmTokenName().startsWith(OAUTH_2_PREFIX)) {
- Pair pair = githubApiClient.getTokenScopes(params.getToken());
- return Optional.of(
- Pair.of(
- containsScopes(pair.second, DEFAULT_TOKEN_SCOPES) ? Boolean.TRUE : Boolean.FALSE,
- pair.first));
- } else {
- // TODO: add PAT scope validation
- // No REST API for PAT-s in Github found yet. Just try to do some action.
- GithubUser user = githubApiClient.getUser(params.getToken());
- return Optional.of(Pair.of(Boolean.TRUE, user.getLogin()));
- }
- } catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) {
- return Optional.empty();
- }
- }
-
- /**
- * Checks if the tokenScopes array contains the requiredScopes.
- *
- * @param tokenScopes Scopes from token
- * @param requiredScopes Mandatory scopes
- * @return If all mandatory scopes are contained in the token's scopes
- */
- boolean containsScopes(String[] tokenScopes, Set requiredScopes) {
- Arrays.sort(tokenScopes);
- // We need check that the token has the required minimal scopes. The scopes can be normalized
- // by GitHub, so we need to be careful for sub-scopes being included in parent scopes.
- for (String requiredScope : requiredScopes) {
- String parentScope = SCOPE_MAP.get(requiredScope);
- if (parentScope == null) {
- // requiredScope is not recognized as a GitHub scope, so just skip it.
- continue;
- }
- if (Arrays.binarySearch(tokenScopes, parentScope) < 0
- && Arrays.binarySearch(tokenScopes, requiredScope) < 0) {
- return false;
- }
- }
- return true;
- }
-
- private String getLocalAuthenticateUrl() {
- return apiEndpoint
- + "/oauth/authenticate?oauth_provider="
- + OAUTH_PROVIDER_NAME
- + "&scope="
- + Joiner.on(',').join(DEFAULT_TOKEN_SCOPES)
- + "&request_method=POST&signature_method=rsa";
+ @Named("che.api") String apiEndpoint, OAuthAPI oAuthAPI, GithubApiClient githubApiClient) {
+ super(apiEndpoint, oAuthAPI, githubApiClient, OAUTH_PROVIDER_NAME);
}
}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcherSecond.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcherSecond.java
new file mode 100644
index 0000000000..f80531964a
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubPersonalAccessTokenFetcherSecond.java
@@ -0,0 +1,33 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.eclipse.che.commons.annotation.Nullable;
+import org.eclipse.che.security.oauth.OAuthAPI;
+
+/** GitHub OAuth token retriever. */
+public class GithubPersonalAccessTokenFetcherSecond
+ extends AbstractGithubPersonalAccessTokenFetcher {
+
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private static final String OAUTH_PROVIDER_NAME = "github_2";
+
+ @Inject
+ public GithubPersonalAccessTokenFetcherSecond(
+ @Named("che.api") String apiEndpoint,
+ @Nullable @Named("che.integration.github.oauth_endpoint_2") String oauthEndpoint,
+ OAuthAPI oAuthAPI) {
+ super(apiEndpoint, oAuthAPI, new GithubApiClient(oauthEndpoint), OAUTH_PROVIDER_NAME);
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubScmFileResolver.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubScmFileResolver.java
index 01b51f7a24..c4c70f3319 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubScmFileResolver.java
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubScmFileResolver.java
@@ -11,69 +11,18 @@
*/
package org.eclipse.che.api.factory.server.github;
-import static org.eclipse.che.api.factory.server.ApiExceptionMapper.toApiException;
-
-import jakarta.validation.constraints.NotNull;
-import java.io.IOException;
import javax.inject.Inject;
-import org.eclipse.che.api.core.ApiException;
-import org.eclipse.che.api.core.NotFoundException;
-import org.eclipse.che.api.factory.server.ScmFileResolver;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
-import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
/** Github specific SCM file resolver. */
-public class GithubScmFileResolver implements ScmFileResolver {
-
- private final GithubURLParser githubUrlParser;
- private final URLFetcher urlFetcher;
- private final PersonalAccessTokenManager personalAccessTokenManager;
+public class GithubScmFileResolver extends AbstractGithubScmFileResolver {
@Inject
public GithubScmFileResolver(
GithubURLParser githubUrlParser,
URLFetcher urlFetcher,
PersonalAccessTokenManager personalAccessTokenManager) {
- this.githubUrlParser = githubUrlParser;
- this.urlFetcher = urlFetcher;
- this.personalAccessTokenManager = personalAccessTokenManager;
- }
-
- @Override
- public boolean accept(@NotNull String repository) {
- // Check if repository parameter is a github URL
- return githubUrlParser.isValid(repository);
- }
-
- @Override
- public String fileContent(@NotNull String repository, @NotNull String filePath)
- throws ApiException {
- final GithubUrl githubUrl = githubUrlParser.parse(repository);
- try {
- return fetchContent(githubUrl, filePath, false);
- } catch (DevfileException exception) {
- // This catch might mean that the authentication was rejected by user, try to repeat the fetch
- // without authentication flow.
- try {
- return fetchContent(githubUrl, filePath, true);
- } catch (DevfileException devfileException) {
- throw toApiException(devfileException);
- }
- }
- }
-
- private String fetchContent(GithubUrl githubUrl, String filePath, boolean skipAuthentication)
- throws DevfileException, NotFoundException {
- try {
- GithubAuthorizingFileContentProvider contentProvider =
- new GithubAuthorizingFileContentProvider(
- githubUrl, urlFetcher, personalAccessTokenManager);
- return skipAuthentication
- ? contentProvider.fetchContentWithoutAuthentication(filePath)
- : contentProvider.fetchContent(filePath);
- } catch (IOException e) {
- throw new NotFoundException(e.getMessage());
- }
+ super(githubUrlParser, urlFetcher, personalAccessTokenManager);
}
}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubScmFileResolverSecond.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubScmFileResolverSecond.java
new file mode 100644
index 0000000000..d3a3dd9fb9
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubScmFileResolverSecond.java
@@ -0,0 +1,28 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import javax.inject.Inject;
+import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
+import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
+
+/** Github specific SCM file resolver. */
+public class GithubScmFileResolverSecond extends AbstractGithubScmFileResolver {
+
+ @Inject
+ public GithubScmFileResolverSecond(
+ GithubURLParserSecond githubUrlParser,
+ URLFetcher urlFetcher,
+ PersonalAccessTokenManager personalAccessTokenManager) {
+ super(githubUrlParser, urlFetcher, personalAccessTokenManager);
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubURLParser.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubURLParser.java
index a8ea88b991..0bb94e7315 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubURLParser.java
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubURLParser.java
@@ -11,39 +11,12 @@
*/
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 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.net.URI;
-import java.net.URISyntaxException;
-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;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
-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.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.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;
-import org.slf4j.LoggerFactory;
/**
* Parser of String Github URLs and provide {@link GithubUrl} objects.
@@ -51,22 +24,10 @@ import org.slf4j.LoggerFactory;
* @author Florent Benoit
*/
@Singleton
-public class GithubURLParser {
+public class GithubURLParser extends AbstractGithubURLParser {
- private static final Logger LOG = LoggerFactory.getLogger(GithubURLParser.class);
- 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;
-
- private final Pattern githubSSHPattern;
-
- private final boolean disableSubdomainIsolation;
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private static final String OAUTH_PROVIDER_NAME = "github";
@Inject
public GithubURLParser(
@@ -75,204 +36,27 @@ public class GithubURLParser {
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint,
@Named("che.integration.github.disable_subdomain_isolation")
boolean disableSubdomainIsolation) {
- this(
+ super(
tokenManager,
devfileFilenamesProvider,
new GithubApiClient(oauthEndpoint),
oauthEndpoint,
- disableSubdomainIsolation);
+ disableSubdomainIsolation,
+ OAUTH_PROVIDER_NAME);
}
- /** Constructor used for testing only. */
GithubURLParser(
PersonalAccessTokenManager tokenManager,
DevfileFilenamesProvider devfileFilenamesProvider,
GithubApiClient githubApiClient,
String oauthEndpoint,
boolean disableSubdomainIsolation) {
- this.tokenManager = tokenManager;
- this.devfileFilenamesProvider = devfileFilenamesProvider;
- this.apiClient = githubApiClient;
- this.oauthEndpoint = oauthEndpoint;
- this.disableSubdomainIsolation = disableSubdomainIsolation;
-
- String endpoint =
- isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
-
- this.githubPattern =
- compile(
- format(
- "^%s/(?[^/]+)/(?[^/]++)((/)|(?:/tree/(?.++))|(/pull/(?\\d++)))?$",
- endpoint));
- this.githubSSHPattern =
- compile(format("^git@%s:(?.*)/(?.*)$", URI.create(endpoint).getHost()));
- }
-
- public boolean isValid(@NotNull String url) {
- String trimmedUrl = trimEnd(url, '/');
- return githubPattern.matcher(trimmedUrl).matches()
- || githubSSHPattern.matcher(trimmedUrl).matches();
- }
-
- public GithubUrl parseWithoutAuthentication(String url) throws ApiException {
- return parse(trimEnd(url, '/'), false);
- }
-
- public GithubUrl parse(String url) throws ApiException {
- return parse(trimEnd(url, '/'), true);
- }
-
- private GithubUrl parse(String url, boolean authenticationRequired) throws ApiException {
- boolean isHTTPSUrl = githubPattern.matcher(url).matches();
- Matcher matcher = isHTTPSUrl ? githubPattern.matcher(url) : githubSSHPattern.matcher(url);
- if (!matcher.matches()) {
- throw new IllegalArgumentException(
- format("The given url %s is not a valid github URL. ", 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$")) {
- repoName = repoName.substring(0, repoName.length() - 4);
- }
-
- String branchName = null;
- String pullRequestId = null;
- if (isHTTPSUrl) {
- branchName = matcher.group("branchName");
- pullRequestId = matcher.group("pullRequestId");
- }
-
- if (pullRequestId != null) {
- GithubPullRequest pullRequest =
- this.getPullRequest(pullRequestId, repoUser, repoName, authenticationRequired);
- if (pullRequest != null) {
- String state = pullRequest.getState();
- if (!"open".equalsIgnoreCase(state)) {
- throw new IllegalArgumentException(
- 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, state));
- }
-
- GithubHead pullRequestHead = pullRequest.getHead();
- repoUser = pullRequestHead.getUser().getLogin();
- repoName = pullRequestHead.getRepo().getName();
- branchName = pullRequestHead.getRef();
- }
- }
-
- String latestCommit = null;
- GithubCommit commit =
- this.getLatestCommit(
- repoUser, repoName, firstNonNull(branchName, "HEAD"), authenticationRequired);
- if (commit != null) {
- latestCommit = commit.getSha();
- }
-
- return new GithubUrl()
- .withUsername(repoUser)
- .withRepository(repoName)
- .setIsHTTPSUrl(isHTTPSUrl)
- .withServerUrl(serverUrl)
- .withDisableSubdomainIsolation(disableSubdomainIsolation)
- .withBranch(branchName)
- .withLatestCommit(latestCommit)
- .withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames())
- .withUrl(url);
- }
-
- private GithubPullRequest getPullRequest(
- String pullRequestId, String repoUser, String repoName, boolean authenticationRequired)
- throws ApiException {
- try {
- // prepare token
- String githubEndpoint =
- isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
- Subject subject = EnvironmentContext.getCurrent().getSubject();
- PersonalAccessToken personalAccessToken = null;
- Optional token = tokenManager.get(subject, githubEndpoint);
- if (token.isPresent()) {
- personalAccessToken = token.get();
- } else if (authenticationRequired) {
- personalAccessToken = tokenManager.fetchAndSave(subject, githubEndpoint);
- }
-
- // get pull request
- return this.apiClient.getPullRequest(
- pullRequestId,
- repoUser,
- repoName,
- personalAccessToken != null ? personalAccessToken.getToken() : null);
- } catch (UnknownScmProviderException e) {
-
- // get pull request without authentication
- try {
- return this.apiClient.getPullRequest(pullRequestId, repoUser, repoName, null);
- } catch (ScmItemNotFoundException
- | ScmCommunicationException
- | ScmBadRequestException exception) {
- LOG.error("Failed to authenticate to GitHub", e);
- }
-
- } catch (ScmUnauthorizedException e) {
- throw toApiException(e);
- } catch (ScmCommunicationException
- | UnsatisfiedScmPreconditionException
- | ScmConfigurationPersistenceException e) {
- LOG.error("Failed to authenticate to GitHub", e);
- } catch (ScmItemNotFoundException | ScmBadRequestException e) {
- LOG.error("Failed retrieve GitHub Pull Request", e);
- }
-
- return null;
- }
-
- private GithubCommit getLatestCommit(
- String repoUser, String repoName, String branchName, boolean authenticationRequired)
- throws ApiException {
- try {
- // prepare token
- String githubEndpoint =
- isNullOrEmpty(oauthEndpoint) ? GITHUB_SAAS_ENDPOINT : trimEnd(oauthEndpoint, '/');
- Subject subject = EnvironmentContext.getCurrent().getSubject();
- PersonalAccessToken personalAccessToken = null;
- Optional token = tokenManager.get(subject, githubEndpoint);
- if (token.isPresent()) {
- personalAccessToken = token.get();
- } else if (authenticationRequired) {
- personalAccessToken = tokenManager.fetchAndSave(subject, githubEndpoint);
- }
-
- // get latest commit
- return this.apiClient.getLatestCommit(
- repoUser,
- repoName,
- branchName,
- personalAccessToken != null ? personalAccessToken.getToken() : null);
- } catch (UnknownScmProviderException | ScmUnauthorizedException e) {
- // get latest commit without authentication
- try {
- return this.apiClient.getLatestCommit(repoUser, repoName, branchName, null);
- } catch (ScmItemNotFoundException
- | ScmCommunicationException
- | ScmBadRequestException
- | URISyntaxException exception) {
- LOG.error("Failed to authenticate to GitHub", e);
- }
- } catch (ScmCommunicationException
- | UnsatisfiedScmPreconditionException
- | ScmConfigurationPersistenceException e) {
- LOG.error("Failed to authenticate to GitHub", e);
- } catch (ScmItemNotFoundException | ScmBadRequestException | URISyntaxException e) {
- LOG.error("Failed to retrieve the latest commit", e);
- e.printStackTrace();
- }
-
- return null;
+ super(
+ tokenManager,
+ devfileFilenamesProvider,
+ githubApiClient,
+ oauthEndpoint,
+ disableSubdomainIsolation,
+ OAUTH_PROVIDER_NAME);
}
}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubURLParserSecond.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubURLParserSecond.java
new file mode 100644
index 0000000000..04f7d55a5b
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubURLParserSecond.java
@@ -0,0 +1,46 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
+import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider;
+import org.eclipse.che.commons.annotation.Nullable;
+
+/**
+ * Parser of String Github URLs and provide {@link GithubUrl} objects.
+ *
+ * @author Florent Benoit
+ */
+@Singleton
+public class GithubURLParserSecond extends AbstractGithubURLParser {
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private static final String OAUTH_PROVIDER_NAME = "github_2";
+
+ @Inject
+ public GithubURLParserSecond(
+ PersonalAccessTokenManager tokenManager,
+ DevfileFilenamesProvider devfileFilenamesProvider,
+ @Nullable @Named("che.integration.github.oauth_endpoint_2") String oauthEndpoint,
+ @Named("che.integration.github.disable_subdomain_isolation_2")
+ boolean disableSubdomainIsolation) {
+ super(
+ tokenManager,
+ devfileFilenamesProvider,
+ new GithubApiClient(oauthEndpoint),
+ oauthEndpoint,
+ disableSubdomainIsolation,
+ OAUTH_PROVIDER_NAME);
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcher.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcher.java
index 3b319cc988..49d91c7cd5 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcher.java
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcher.java
@@ -11,80 +11,41 @@
*/
package org.eclipse.che.api.factory.server.github;
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
-import org.eclipse.che.api.auth.shared.dto.OAuthToken;
-import org.eclipse.che.api.factory.server.scm.AbstractGitUserDataFetcher;
-import org.eclipse.che.api.factory.server.scm.GitUserData;
-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.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.security.oauth.OAuthAPI;
/** GitHub user data retriever. */
-public class GithubUserDataFetcher extends AbstractGitUserDataFetcher {
- private final String apiEndpoint;
- /** GitHub API client. */
- private final GithubApiClient githubApiClient;
-
+public class GithubUserDataFetcher extends AbstractGithubUserDataFetcher {
/** Name of this OAuth provider as found in OAuthAPI. */
private static final String OAUTH_PROVIDER_NAME = "github";
- /** Collection of OAuth scopes required to make integration with GitHub work. */
- public static final Set DEFAULT_TOKEN_SCOPES =
- ImmutableSet.of("repo", "user:email", "read:user");
-
@Inject
public GithubUserDataFetcher(
@Named("che.api") String apiEndpoint,
@Nullable @Named("che.integration.github.oauth_endpoint") String oauthEndpoint,
OAuthAPI oAuthTokenFetcher,
PersonalAccessTokenManager personalAccessTokenManager) {
- this(
+ super(
apiEndpoint,
oAuthTokenFetcher,
personalAccessTokenManager,
- new GithubApiClient(oauthEndpoint));
+ new GithubApiClient(oauthEndpoint),
+ OAUTH_PROVIDER_NAME);
}
- /** Constructor used for testing only. */
- public GithubUserDataFetcher(
+ GithubUserDataFetcher(
String apiEndpoint,
OAuthAPI oAuthTokenFetcher,
PersonalAccessTokenManager personalAccessTokenManager,
GithubApiClient githubApiClient) {
- super(OAUTH_PROVIDER_NAME, personalAccessTokenManager, oAuthTokenFetcher);
- this.githubApiClient = githubApiClient;
- this.apiEndpoint = apiEndpoint;
- }
-
- @Override
- protected GitUserData fetchGitUserDataWithOAuthToken(OAuthToken oAuthToken)
- throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
- GithubUser user = githubApiClient.getUser(oAuthToken.getToken());
- return new GitUserData(user.getName(), user.getEmail());
- }
-
- @Override
- protected GitUserData fetchGitUserDataWithPersonalAccessToken(
- PersonalAccessToken personalAccessToken)
- throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
- GithubUser user = githubApiClient.getUser(personalAccessToken.getToken());
- return new GitUserData(user.getName(), user.getEmail());
- }
-
- protected String getLocalAuthenticateUrl() {
- return apiEndpoint
- + "/oauth/authenticate?oauth_provider="
- + OAUTH_PROVIDER_NAME
- + "&scope="
- + Joiner.on(',').join(DEFAULT_TOKEN_SCOPES)
- + "&request_method=POST&signature_method=rsa";
+ super(
+ apiEndpoint,
+ oAuthTokenFetcher,
+ personalAccessTokenManager,
+ githubApiClient,
+ OAUTH_PROVIDER_NAME);
}
}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcherSecond.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcherSecond.java
new file mode 100644
index 0000000000..2b707e8f03
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcherSecond.java
@@ -0,0 +1,38 @@
+/*
+ * 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
+import org.eclipse.che.commons.annotation.Nullable;
+import org.eclipse.che.security.oauth.OAuthAPI;
+
+/** GitHub user data retriever. */
+public class GithubUserDataFetcherSecond extends AbstractGithubUserDataFetcher {
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private static final String OAUTH_PROVIDER_NAME = "github_2";
+
+ @Inject
+ public GithubUserDataFetcherSecond(
+ @Named("che.api") String apiEndpoint,
+ @Nullable @Named("che.integration.github.oauth_endpoint_2") String oauthEndpoint,
+ OAuthAPI oAuthTokenFetcher,
+ PersonalAccessTokenManager personalAccessTokenManager) {
+ super(
+ apiEndpoint,
+ oAuthTokenFetcher,
+ personalAccessTokenManager,
+ new GithubApiClient(oauthEndpoint),
+ OAUTH_PROVIDER_NAME);
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProviderTest.java b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProviderTest.java
index 1cd4564839..9fd553183b 100644
--- a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProviderTest.java
+++ b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubAuthorizingFileContentProviderTest.java
@@ -45,7 +45,7 @@ public class GithubAuthorizingFileContentProviderTest {
URLFetcher urlFetcher = mock(URLFetcher.class);
GithubUrl githubUrl =
- new GithubUrl()
+ new GithubUrl("github")
.withUsername("eclipse")
.withRepository("che")
.withBranch("main")
@@ -71,7 +71,7 @@ public class GithubAuthorizingFileContentProviderTest {
String raw_url = "https://raw.githubusercontent.com/foo/bar/branch-name/devfile.yaml";
GithubUrl githubUrl =
- new GithubUrl()
+ new GithubUrl("github")
.withUsername("eclipse")
.withRepository("che")
.withBranch("main")
@@ -92,7 +92,7 @@ public class GithubAuthorizingFileContentProviderTest {
public void shouldThrowNotFoundForPublicRepos() throws Exception {
String url = "https://raw.githubusercontent.com/foo/bar/branch-name/devfile.yaml";
- GithubUrl githubUrl = new GithubUrl().withUsername("eclipse").withRepository("che");
+ GithubUrl githubUrl = new GithubUrl("github").withUsername("eclipse").withRepository("che");
URLFetcher urlFetcher = Mockito.mock(URLFetcher.class);
FileContentProvider fileContentProvider =
@@ -109,7 +109,7 @@ public class GithubAuthorizingFileContentProviderTest {
@Test(expectedExceptions = DevfileException.class)
public void shouldThrowDevfileException() throws Exception {
String url = "https://raw.githubusercontent.com/foo/bar/branch-name/devfile.yaml";
- GithubUrl githubUrl = new GithubUrl().withUsername("eclipse").withRepository("che");
+ GithubUrl githubUrl = new GithubUrl("github").withUsername("eclipse").withRepository("che");
URLFetcher urlFetcher = Mockito.mock(URLFetcher.class);
FileContentProvider fileContentProvider =
@@ -128,7 +128,7 @@ public class GithubAuthorizingFileContentProviderTest {
String raw_url = "https://ghserver.com/foo/bar/branch-name/devfile.yaml";
URLFetcher urlFetcher = Mockito.mock(URLFetcher.class);
- GithubUrl githubUrl = new GithubUrl().withUsername("eclipse").withRepository("che");
+ GithubUrl githubUrl = new GithubUrl("github").withUsername("eclipse").withRepository("che");
FileContentProvider fileContentProvider =
new GithubAuthorizingFileContentProvider(githubUrl, urlFetcher, personalAccessTokenManager);
var personalAccessToken = new PersonalAccessToken(raw_url, "che", "my-token");
diff --git a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolverTest.java b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolverTest.java
index 853ed13f61..387acb6cfb 100644
--- a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolverTest.java
+++ b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubFactoryParametersResolverTest.java
@@ -99,7 +99,7 @@ public class GithubFactoryParametersResolverTest {
@Captor private ArgumentCaptor factoryUrlArgumentCaptor;
/** Instance of resolver that will be tested. */
- private GithubFactoryParametersResolver githubFactoryParametersResolver;
+ private AbstractGithubFactoryParametersResolver abstractGithubFactoryParametersResolver;
@BeforeMethod
protected void init() throws Exception {
@@ -110,7 +110,7 @@ public class GithubFactoryParametersResolverTest {
new GithubURLParser(
personalAccessTokenManager, devfileFilenamesProvider, githubApiClient, null, false);
- githubFactoryParametersResolver =
+ abstractGithubFactoryParametersResolver =
new GithubFactoryParametersResolver(
githubUrlParser,
urlFetcher,
@@ -119,14 +119,14 @@ public class GithubFactoryParametersResolverTest {
urlFactoryBuilder,
projectConfigDtoMerger,
personalAccessTokenManager);
- assertNotNull(this.githubFactoryParametersResolver);
+ assertNotNull(this.abstractGithubFactoryParametersResolver);
}
/** Check missing parameter name can't be accepted by this resolver */
@Test
public void checkMissingParameter() {
Map parameters = singletonMap("foo", "this is a foo bar");
- boolean accept = githubFactoryParametersResolver.accept(parameters);
+ boolean accept = abstractGithubFactoryParametersResolver.accept(parameters);
// shouldn't be accepted
assertFalse(accept);
}
@@ -135,7 +135,7 @@ public class GithubFactoryParametersResolverTest {
@Test
public void checkInvalidAcceptUrl() {
Map parameters = singletonMap(URL_PARAMETER_NAME, "http://www.eclipse.org/che");
- boolean accept = githubFactoryParametersResolver.accept(parameters);
+ boolean accept = abstractGithubFactoryParametersResolver.accept(parameters);
// shouldn't be accepted
assertFalse(accept);
}
@@ -145,7 +145,7 @@ public class GithubFactoryParametersResolverTest {
public void checkValidAcceptUrl() {
Map parameters =
singletonMap(URL_PARAMETER_NAME, "https://github.com/codenvy/codenvy.git");
- boolean accept = githubFactoryParametersResolver.accept(parameters);
+ boolean accept = abstractGithubFactoryParametersResolver.accept(parameters);
// shouldn't be accepted
assertTrue(accept);
}
@@ -168,7 +168,7 @@ public class GithubFactoryParametersResolverTest {
Map params = ImmutableMap.of(URL_PARAMETER_NAME, githubUrl);
// when
- FactoryDto factory = (FactoryDto) githubFactoryParametersResolver.createFactory(params);
+ FactoryDto factory = (FactoryDto) abstractGithubFactoryParametersResolver.createFactory(params);
// then
verify(urlFactoryBuilder).buildDefaultDevfile(eq("che"));
assertEquals(factory, computedFactory);
@@ -193,7 +193,7 @@ public class GithubFactoryParametersResolverTest {
"https://github.com/eclipse/che",
ERROR_QUERY_NAME,
"access_denied");
- githubFactoryParametersResolver.createFactory(params);
+ abstractGithubFactoryParametersResolver.createFactory(params);
// then
verify(urlFactoryBuilder)
.createFactoryFromDevfile(
@@ -210,7 +210,7 @@ public class GithubFactoryParametersResolverTest {
.thenReturn(new GithubCommit().withSha("test-sha"));
// when
- githubFactoryParametersResolver.createFactory(
+ abstractGithubFactoryParametersResolver.createFactory(
ImmutableMap.of(URL_PARAMETER_NAME, "https://github.com/eclipse/che"));
// then
verify(urlFactoryBuilder)
@@ -237,7 +237,7 @@ public class GithubFactoryParametersResolverTest {
Map params = ImmutableMap.of(URL_PARAMETER_NAME, githubUrl);
// when
- FactoryDto factory = (FactoryDto) githubFactoryParametersResolver.createFactory(params);
+ FactoryDto factory = (FactoryDto) abstractGithubFactoryParametersResolver.createFactory(params);
// then
assertNotNull(factory.getDevfile());
assertNull(factory.getWorkspace());
@@ -268,7 +268,7 @@ public class GithubFactoryParametersResolverTest {
Map params = ImmutableMap.of(URL_PARAMETER_NAME, githubUrl);
// when
- FactoryDto factory = (FactoryDto) githubFactoryParametersResolver.createFactory(params);
+ FactoryDto factory = (FactoryDto) abstractGithubFactoryParametersResolver.createFactory(params);
// then
assertNotNull(factory.getDevfile());
SourceDto source = factory.getDevfile().getProjects().get(0).getSource();
@@ -299,7 +299,7 @@ public class GithubFactoryParametersResolverTest {
Map params = ImmutableMap.of(URL_PARAMETER_NAME, githubUrl);
// when
- FactoryDto factory = (FactoryDto) githubFactoryParametersResolver.createFactory(params);
+ FactoryDto factory = (FactoryDto) abstractGithubFactoryParametersResolver.createFactory(params);
// then
assertNotNull(factory.getDevfile());
SourceDto source = factory.getDevfile().getProjects().get(0).getSource();
@@ -323,7 +323,7 @@ public class GithubFactoryParametersResolverTest {
Map params = ImmutableMap.of(URL_PARAMETER_NAME, githubUrl);
// when
FactoryDevfileV2Dto factory =
- (FactoryDevfileV2Dto) githubFactoryParametersResolver.createFactory(params);
+ (FactoryDevfileV2Dto) abstractGithubFactoryParametersResolver.createFactory(params);
// then
ScmInfo scmInfo = factory.getScmInfo();
assertNotNull(scmInfo);
@@ -343,7 +343,7 @@ public class GithubFactoryParametersResolverTest {
.thenReturn(Optional.of(generateDevfileFactory()));
// when
- githubFactoryParametersResolver.createFactory(params);
+ abstractGithubFactoryParametersResolver.createFactory(params);
// then
verify(urlFactoryBuilder)
@@ -359,7 +359,7 @@ public class GithubFactoryParametersResolverTest {
// given
githubUrlParser = mock(GithubURLParser.class);
- githubFactoryParametersResolver =
+ abstractGithubFactoryParametersResolver =
new GithubFactoryParametersResolver(
githubUrlParser,
urlFetcher,
@@ -370,7 +370,7 @@ public class GithubFactoryParametersResolverTest {
personalAccessTokenManager);
when(authorisationRequestManager.isStored(eq("github"))).thenReturn(true);
// when
- githubFactoryParametersResolver.parseFactoryUrl("url");
+ abstractGithubFactoryParametersResolver.parseFactoryUrl("url");
// then
verify(githubUrlParser).parseWithoutAuthentication("url");
verify(githubUrlParser, never()).parse("url");
@@ -381,7 +381,7 @@ public class GithubFactoryParametersResolverTest {
// given
githubUrlParser = mock(GithubURLParser.class);
- githubFactoryParametersResolver =
+ abstractGithubFactoryParametersResolver =
new GithubFactoryParametersResolver(
githubUrlParser,
urlFetcher,
@@ -392,7 +392,7 @@ public class GithubFactoryParametersResolverTest {
personalAccessTokenManager);
when(authorisationRequestManager.isStored(eq("github"))).thenReturn(false);
// when
- githubFactoryParametersResolver.parseFactoryUrl("url");
+ abstractGithubFactoryParametersResolver.parseFactoryUrl("url");
// then
verify(githubUrlParser).parse("url");
verify(githubUrlParser, never()).parseWithoutAuthentication("url");
diff --git a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubUrlTest.java b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubUrlTest.java
index 8c0ff82e14..c63b5181fe 100644
--- a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubUrlTest.java
+++ b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubUrlTest.java
@@ -139,7 +139,7 @@ public class GithubUrlTest {
public void testRawFileLocationWithDefaultBranchName() {
String file = ".che/che-theia-plugins.yaml";
- GithubUrl url = new GithubUrl().withUsername("eclipse").withRepository("che");
+ GithubUrl url = new GithubUrl("github").withUsername("eclipse").withRepository("che");
assertEquals(
url.rawFileLocation(file),
@@ -151,7 +151,7 @@ public class GithubUrlTest {
String file = ".che/che-theia-plugins.yaml";
GithubUrl url =
- new GithubUrl().withUsername("eclipse").withRepository("che").withBranch("main");
+ new GithubUrl("github").withUsername("eclipse").withRepository("che").withBranch("main");
assertEquals(
url.rawFileLocation(file),
@@ -163,7 +163,7 @@ public class GithubUrlTest {
String file = ".che/che-theia-plugins.yaml";
GithubUrl url =
- new GithubUrl()
+ new GithubUrl("github")
.withUsername("eclipse")
.withRepository("che")
.withBranch("main")
diff --git a/wsmaster/che-core-api-factory-shared/src/main/java/org/eclipse/che/api/factory/shared/dto/FactoryVisitor.java b/wsmaster/che-core-api-factory-shared/src/main/java/org/eclipse/che/api/factory/shared/dto/FactoryVisitor.java
index 87571978ba..3d9eead20c 100644
--- a/wsmaster/che-core-api-factory-shared/src/main/java/org/eclipse/che/api/factory/shared/dto/FactoryVisitor.java
+++ b/wsmaster/che-core-api-factory-shared/src/main/java/org/eclipse/che/api/factory/shared/dto/FactoryVisitor.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/
@@ -25,6 +25,7 @@ public interface FactoryVisitor {
* @param factoryDto factory to visit
* @return updated factory
*/
+ @Deprecated
FactoryDto visit(FactoryDto factoryDto);
/**
diff --git a/wsmaster/pom.xml b/wsmaster/pom.xml
index 944d579214..f6b12abeed 100644
--- a/wsmaster/pom.xml
+++ b/wsmaster/pom.xml
@@ -29,6 +29,7 @@
che-core-api-auth-azure-devops
che-core-api-auth-bitbucket
che-core-api-auth-github
+ che-core-api-auth-github-common
che-core-api-auth-gitlab
che-core-api-auth-openshift
che-core-api-workspace-shared
@@ -44,6 +45,7 @@
che-core-api-factory-shared
che-core-api-factory
che-core-api-factory-github
+ che-core-api-factory-github-common
che-core-api-factory-gitlab
che-core-api-factory-bitbucket
che-core-api-factory-bitbucket-server