diff --git a/assembly/assembly-wsmaster-war/pom.xml b/assembly/assembly-wsmaster-war/pom.xml index ad1befadac..c231fef264 100644 --- a/assembly/assembly-wsmaster-war/pom.xml +++ b/assembly/assembly-wsmaster-war/pom.xml @@ -139,10 +139,6 @@ org.eclipse.che.core che-core-api-factory-bitbucket-server - - org.eclipse.che.core - che-core-api-factory-git-ssh - org.eclipse.che.core che-core-api-factory-github 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 97867dfe71..274c1ff2f8 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 @@ -44,8 +44,6 @@ import org.eclipse.che.api.factory.server.bitbucket.BitbucketFactoryParametersRe import org.eclipse.che.api.factory.server.bitbucket.BitbucketScmFileResolver; import org.eclipse.che.api.factory.server.bitbucket.BitbucketServerAuthorizingFactoryParametersResolver; import org.eclipse.che.api.factory.server.bitbucket.BitbucketServerScmFileResolver; -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.GithubScmFileResolver; import org.eclipse.che.api.factory.server.gitlab.GitlabFactoryParametersResolver; @@ -177,7 +175,6 @@ public class WsMasterModule extends AbstractModule { factoryParametersResolverMultibinder .addBinding() .to(AzureDevOpsFactoryParametersResolver.class); - factoryParametersResolverMultibinder.addBinding().to(GitSshFactoryParametersResolver.class); Multibinder scmFileResolverResolverMultibinder = Multibinder.newSetBinder(binder(), ScmFileResolver.class); @@ -186,7 +183,6 @@ public class WsMasterModule extends AbstractModule { scmFileResolverResolverMultibinder.addBinding().to(GitlabScmFileResolver.class); scmFileResolverResolverMultibinder.addBinding().to(BitbucketServerScmFileResolver.class); scmFileResolverResolverMultibinder.addBinding().to(AzureDevOpsScmFileResolver.class); - scmFileResolverResolverMultibinder.addBinding().to(GitSshScmFileResolver.class); install(new org.eclipse.che.api.factory.server.scm.KubernetesScmModule()); install(new org.eclipse.che.api.factory.server.bitbucket.BitbucketServerModule()); diff --git a/pom.xml b/pom.xml index 2b8ffd1433..4531d77341 100644 --- a/pom.xml +++ b/pom.xml @@ -759,11 +759,6 @@ che-core-api-factory-bitbucket-server ${che.version} - - org.eclipse.che.core - che-core-api-factory-git-ssh - ${che.version} - org.eclipse.che.core che-core-api-factory-github diff --git a/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParser.java b/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParser.java index 6bfd60016c..807c400398 100644 --- a/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParser.java +++ b/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParser.java @@ -40,6 +40,8 @@ public class AzureDevOpsURLParser { */ private final Pattern azureDevOpsPattern; + private final Pattern azureSSHDevOpsPattern; + @Inject public AzureDevOpsURLParser( DevfileFilenamesProvider devfileFilenamesProvider, @@ -58,14 +60,22 @@ public class AzureDevOpsURLParser { + "([?&]version=GB(?[^&]++))?" + "(.*)", azureDevOpsScmApiEndpointHost)); + this.azureSSHDevOpsPattern = + compile( + format( + "^git@ssh\\.%s:v3/(?.*)/(?.*)/(?.*)$", + azureDevOpsScmApiEndpointHost)); } public boolean isValid(@NotNull String url) { - return azureDevOpsPattern.matcher(url).matches(); + return azureDevOpsPattern.matcher(url).matches() + || azureSSHDevOpsPattern.matcher(url).matches(); } public AzureDevOpsUrl parse(String url) { - Matcher matcher = azureDevOpsPattern.matcher(url); + boolean isHTTPSUrl = azureDevOpsPattern.matcher(url).matches(); + Matcher matcher = + isHTTPSUrl ? azureDevOpsPattern.matcher(url) : azureSSHDevOpsPattern.matcher(url); if (!matcher.matches()) { throw new IllegalArgumentException(format("The given url %s is not a valid.", url)); } @@ -83,23 +93,28 @@ public class AzureDevOpsURLParser { project = repoName; } - String organization = matcher.group("organization"); - String branch = matcher.group("branch"); - String tag = matcher.group("tag"); + String branch = null; + String tag = null; - // The url might have the following formats: - // - https://@///_git/ - // - https://@///_git/ - // For the first case we need to remove the `organization` from the url to distinguish it from - // `credentials` - // TODO: return empty credentials like the BitBucketUrl - String organizationCanIgnore = matcher.group("organizationCanIgnore"); - if (!isNullOrEmpty(organization) && organization.equals(organizationCanIgnore)) { - url = url.replace(organizationCanIgnore + "@", ""); + String organization = matcher.group("organization"); + if (isHTTPSUrl) { + branch = matcher.group("branch"); + tag = matcher.group("tag"); + // The url might have the following formats: + // - https://@///_git/ + // - https://@///_git/ + // For the first case we need to remove the `organization` from the url to distinguish it from + // `credentials` + // TODO: return empty credentials like the BitBucketUrl + String organizationCanIgnore = matcher.group("organizationCanIgnore"); + if (!isNullOrEmpty(organization) && organization.equals(organizationCanIgnore)) { + url = url.replace(organizationCanIgnore + "@", ""); + } } return new AzureDevOpsUrl() .withHostName(azureDevOpsScmApiEndpointHost) + .setIsHTTPSUrl(isHTTPSUrl) .withProject(project) .withRepository(repoName) .withOrganization(organization) diff --git a/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsUrl.java b/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsUrl.java index ac25cf9655..11a3954d25 100644 --- a/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsUrl.java +++ b/wsmaster/che-core-api-factory-azure-devops/src/main/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsUrl.java @@ -30,6 +30,7 @@ import org.eclipse.che.api.factory.server.urlfactory.DefaultFactoryUrl; */ public class AzureDevOpsUrl extends DefaultFactoryUrl { + private boolean isHTTPSUrl; private String hostName; private String repository; @@ -145,7 +146,17 @@ public class AzureDevOpsUrl extends DefaultFactoryUrl { } public String getRepositoryLocation() { - return getRepoPathJoiner().add("_git").add(repository).toString(); + if (isHTTPSUrl) { + return getRepoPathJoiner().add("_git").add(repository).toString(); + } + return "git@ssh." + + hostName.substring(8) + + ":v3/" + + organization + + "/" + + project + + "/" + + repository; } private StringJoiner getRepoPathJoiner() { @@ -157,6 +168,11 @@ public class AzureDevOpsUrl extends DefaultFactoryUrl { return hostName; } + public AzureDevOpsUrl setIsHTTPSUrl(boolean isHTTPSUrl) { + this.isHTTPSUrl = isHTTPSUrl; + return this; + } + public AzureDevOpsUrl withHostName(String hostName) { this.hostName = "https://" + hostName; return this; diff --git a/wsmaster/che-core-api-factory-azure-devops/src/test/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParserTest.java b/wsmaster/che-core-api-factory-azure-devops/src/test/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParserTest.java index 3ef9faade6..56e29dc0a6 100644 --- a/wsmaster/che-core-api-factory-azure-devops/src/test/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParserTest.java +++ b/wsmaster/che-core-api-factory-azure-devops/src/test/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLParserTest.java @@ -109,6 +109,48 @@ public class AzureDevOpsURLParserTest { "main", null }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", + "MyOrg", + "MyProject", + "MyRepo", + null, + null + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.git", + "MyOrg", + "MyProject", + "MyRepo", + null, + null + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot.git", + "MyOrg", + "MyProject", + "MyRepo.dot", + null, + null + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", + "MyOrg", + "MyProject", + "MyRepo", + null, + null + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen", + "MyOrg", + "MyProject", + "MyRepo-with-hypen", + null, + null + }, + {"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-", "MyOrg", "MyProject", "-", null, null}, + {"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j.git", "MyOrg", "MyProject", "-j", null, null}, { "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain", "MyOrg", diff --git a/wsmaster/che-core-api-factory-azure-devops/src/test/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLTest.java b/wsmaster/che-core-api-factory-azure-devops/src/test/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLTest.java new file mode 100644 index 0000000000..55e2b44677 --- /dev/null +++ b/wsmaster/che-core-api-factory-azure-devops/src/test/java/org/eclipse/che/api/factory/server/azure/devops/AzureDevOpsURLTest.java @@ -0,0 +1,219 @@ +/* + * 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.azure.devops; + +import static java.lang.String.format; +import static org.mockito.Mockito.mock; +import static org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Iterator; +import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider; +import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class AzureDevOpsURLTest { + private AzureDevOpsURLParser azureDevOpsURLParser; + + @BeforeMethod + protected void init() { + azureDevOpsURLParser = + new AzureDevOpsURLParser(mock(DevfileFilenamesProvider.class), "https://dev.azure.com/"); + } + + @Test(dataProvider = "urlsProvider") + public void checkDevfileLocation(String repoUrl, String fileUrl) { + + AzureDevOpsUrl azureDevOpsUrl = + azureDevOpsURLParser + .parse(repoUrl) + .withDevfileFilenames(Arrays.asList("devfile.yaml", "foo.bar")); + assertEquals(azureDevOpsUrl.devfileFileLocations().size(), 2); + Iterator iterator = + azureDevOpsUrl.devfileFileLocations().iterator(); + String location = iterator.next().location(); + assertEquals(location, format(fileUrl, "devfile.yaml")); + assertEquals(location, format(fileUrl, "foo.bar")); + } + + @DataProvider + public static Object[][] urlsProvider() { + return new Object[][] { + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.git", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot.git", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo.dot/items?path=/devfile.yaml&api-version=7.0" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo-with-hypen/items?path=/devfile.yaml&api-version=7.0" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/-", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-/items?path=/devfile.yaml&api-version=7.0" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/-j.git", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-j/items?path=/devfile.yaml&api-version=7.0" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.git", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot.git", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo.dot/items?path=/devfile.yaml&api-version=7.0" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo-with-hypen/items?path=/devfile.yaml&api-version=7.0" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/-", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-/items?path=/devfile.yaml&api-version=7.0" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j.git", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/-j/items?path=/devfile.yaml&api-version=7.0" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain&_a=contents", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=branch&version=main&api-version=7.0" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=branch&version=main&api-version=7.0" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag&_a=contents", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=tag&version=MyTag&api-version=7.0" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag", + "https://dev.azure.com/MyOrg/MyProject/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&versionType=tag&version=MyTag&api-version=7.0" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/_git/MyRepo", + "https://dev.azure.com/MyOrg/MyRepo/_apis/git/repositories/MyRepo/items?path=/devfile.yaml&api-version=7.0" + } + }; + } + + @Test(dataProvider = "repoProvider") + public void checkRepositoryLocation(String rawUrl, String repoUrl) { + AzureDevOpsUrl azureDevOpsUrl = azureDevOpsURLParser.parse(rawUrl); + assertEquals(azureDevOpsUrl.getRepositoryLocation(), repoUrl); + } + + @DataProvider + public static Object[][] repoProvider() { + return new Object[][] { + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.git", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot.git", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo.dot" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo-with-hypen" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/-", + "https://dev.azure.com/MyOrg/MyProject/_git/-" + }, + { + "https://dev.azure.com/MyOrg/MyProject/_git/-j.git", + "https://dev.azure.com/MyOrg/MyProject/_git/-j" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.git", + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot.git", + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.dot" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo" + }, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen", + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo-with-hypen" + }, + {"git@ssh.dev.azure.com:v3/MyOrg/MyProject/-", "git@ssh.dev.azure.com:v3/MyOrg/MyProject/-"}, + { + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j.git", + "git@ssh.dev.azure.com:v3/MyOrg/MyProject/-j" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain&_a=contents", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GBmain", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag&_a=contents", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo?path=MyFile&version=GTMyTag", + "https://dev.azure.com/MyOrg/MyProject/_git/MyRepo" + }, + { + "https://MyOrg@dev.azure.com/MyOrg/_git/MyRepo", + "https://dev.azure.com/MyOrg/MyRepo/_git/MyRepo" + } + }; + } +} diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java index b76d4d517a..f275b6b5f3 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParser.java @@ -16,6 +16,7 @@ import static java.util.regex.Pattern.compile; import com.google.common.base.Splitter; import jakarta.validation.constraints.NotNull; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -50,11 +51,13 @@ public class BitbucketServerURLParser { private final PersonalAccessTokenManager personalAccessTokenManager; private static final List bitbucketUrlPatternTemplates = List.of( - "^(?%s)/scm/~(?[^/]+)/(?.*).git$", - "^(?%s)/users/(?[^/]+)/repos/(?[^/]+)/browse(\\?at=(?.*))?", - "^(?%s)/users/(?[^/]+)/repos/(?[^/]+)/?", - "^(?%s)/scm/(?[^/~]+)/(?[^/]+).git", - "^(?%s)/projects/(?[^/]+)/repos/(?[^/]+)/browse(\\?at=(?.*))?"); + "^(?%s)://(?%s)/scm/~(?[^/]+)/(?.*).git$", + "^(?%s)://(?%s)/users/(?[^/]+)/repos/(?[^/]+)/browse(\\?at=(?.*))?", + "^(?%s)://(?%s)/users/(?[^/]+)/repos/(?[^/]+)/?", + "^(?%s)://(?%s)/scm/(?[^/~]+)/(?[^/]+).git", + "^(?%s)://(?%s)/projects/(?[^/]+)/repos/(?[^/]+)/browse(\\?at=(?.*))?", + "^(?%s)://git@(?%s):(?\\d*)/~(?[^/]+)/(?.*).git$", + "^(?%s)://git@(?%s):(?\\d*)/(?[^/]+)/(?.*).git$"); private final List bitbucketUrlPatterns = new ArrayList<>(); private static final String OAUTH_PROVIDER_NAME = "bitbucket-server"; @@ -70,16 +73,24 @@ public class BitbucketServerURLParser { if (bitbucketEndpoints != null) { for (String bitbucketEndpoint : Splitter.on(",").split(bitbucketEndpoints)) { String trimmedEndpoint = StringUtils.trimEnd(bitbucketEndpoint, '/'); + URI uri = URI.create(trimmedEndpoint); bitbucketUrlPatternTemplates.forEach( - t -> bitbucketUrlPatterns.add(Pattern.compile(format(t, trimmedEndpoint)))); + t -> { + String scheme = t.contains("git@") ? "ssh" : uri.getScheme(); + String host = uri.getHost(); + bitbucketUrlPatterns.add(Pattern.compile(format(t, scheme, host))); + }); } } } private boolean isUserTokenPresent(String repositoryUrl) { String serverUrl = getServerUrl(repositoryUrl); + URI uri = URI.create(repositoryUrl); + String schema = uri.getScheme(); + String host = uri.getHost(); if (bitbucketUrlPatternTemplates.stream() - .anyMatch(t -> Pattern.compile(format(t, serverUrl)).matcher(repositoryUrl).matches())) { + .anyMatch(t -> Pattern.compile(format(t, schema, host)).matcher(repositoryUrl).matches())) { try { Optional token = personalAccessTokenManager.get(EnvironmentContext.getCurrent().getSubject(), serverUrl); @@ -94,7 +105,9 @@ public class BitbucketServerURLParser { } public boolean isValid(@NotNull String url) { - if (!bitbucketUrlPatterns.isEmpty()) { + if (!url.contains("://")) { + return false; + } else if (!bitbucketUrlPatterns.isEmpty()) { return bitbucketUrlPatterns.stream().anyMatch(pattern -> pattern.matcher(url).matches()); } else { return @@ -126,18 +139,27 @@ public class BitbucketServerURLParser { } private String getServerUrl(String repositoryUrl) { + if (repositoryUrl.startsWith("ssh://git@")) { + String substring = repositoryUrl.substring(10); + return "https://" + substring.substring(0, substring.indexOf(":")); + } return repositoryUrl.substring( 0, repositoryUrl.indexOf("/scm") > 0 ? repositoryUrl.indexOf("/scm") : repositoryUrl.indexOf("/users") > 0 ? repositoryUrl.indexOf("/users") - : repositoryUrl.length()); + : repositoryUrl.indexOf("/projects") > 0 + ? repositoryUrl.indexOf("/projects") + : repositoryUrl.length()); } private Optional getPatternMatcherByUrl(String url) { + URI uri = URI.create(url); + String scheme = uri.getScheme(); + String host = uri.getHost(); return bitbucketUrlPatternTemplates.stream() - .map(t -> compile(format(t, getServerUrl(url))).matcher(url)) + .map(t -> compile(format(t, scheme, host)).matcher(url)) .filter(Matcher::matches) .findAny(); } @@ -174,7 +196,14 @@ public class BitbucketServerURLParser { } private BitbucketServerUrl parse(Matcher matcher) { + String scheme = matcher.group("scheme"); String host = matcher.group("host"); + String port = null; + try { + port = matcher.group("port"); + } catch (IllegalArgumentException e) { + // keep port with null, as the pattern doesn't have the port group + } String user = null; String project = null; try { @@ -191,7 +220,9 @@ public class BitbucketServerURLParser { } return new BitbucketServerUrl() + .withScheme(scheme) .withHostName(host) + .withPort(port) .withProject(project) .withUser(user) .withRepository(repoName) diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerUrl.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerUrl.java index 74afa28c80..458f332daf 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerUrl.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerUrl.java @@ -29,6 +29,9 @@ public class BitbucketServerUrl extends DefaultFactoryUrl { /** Hostname of bitbucket URL */ private String hostName; + private String scheme; + private String port; + /** Project part of bitbucket URL */ private String project; @@ -54,6 +57,11 @@ public class BitbucketServerUrl extends DefaultFactoryUrl { return NAME; } + @Override + public String getProviderUrl() { + return (scheme.equals("ssh") ? "https" : scheme) + "://" + hostName; + } + /** * Gets hostname of this bitbucket server url * @@ -68,6 +76,16 @@ public class BitbucketServerUrl extends DefaultFactoryUrl { return this; } + public BitbucketServerUrl withScheme(String scheme) { + this.scheme = scheme; + return this; + } + + public BitbucketServerUrl withPort(String port) { + this.port = port; + return this; + } + /** * Gets project of this bitbucket server url * @@ -171,7 +189,7 @@ public class BitbucketServerUrl extends DefaultFactoryUrl { public String rawFileLocation(String fileName) { StringJoiner joiner = new StringJoiner("/") - .add(hostName) + .add((scheme.equals("ssh") ? "https" : scheme) + "://" + hostName) .add("rest/api/1.0") .add(!isNullOrEmpty(user) && isNullOrEmpty(project) ? "users" : "projects") .add(firstNonNull(user, project)) @@ -192,7 +210,14 @@ public class BitbucketServerUrl extends DefaultFactoryUrl { * @return location of the repository. */ protected String repositoryLocation() { - return hostName + if (scheme.equals("ssh")) { + return String.format( + "%s://git@%s:%s/%s/%s.git", + scheme, hostName, port, (isNullOrEmpty(user) ? project : "~" + user), repository); + } + return scheme + + "://" + + hostName + "/scm/" + (isNullOrEmpty(user) ? project : "~" + user) + "/" diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerAuthorizingFileContentProviderTest.java b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerAuthorizingFileContentProviderTest.java index 62819f3ea0..2a52b60ae0 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerAuthorizingFileContentProviderTest.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerAuthorizingFileContentProviderTest.java @@ -29,18 +29,21 @@ import org.testng.annotations.Test; @Listeners(MockitoTestNGListener.class) public class BitbucketServerAuthorizingFileContentProviderTest { - public static final String TEST_HOSTNAME = "https://foo.bar"; + public static final String TEST_HOSTNAME = "foo.bar"; + public static final String TEST_SCHEME = "https"; @Mock private URLFetcher urlFetcher; @Mock private PersonalAccessTokenManager personalAccessTokenManager; @Test public void shouldFetchContentWithTokenIfPresent() throws Exception { - BitbucketServerUrl url = new BitbucketServerUrl().withHostName(TEST_HOSTNAME); + BitbucketServerUrl url = + new BitbucketServerUrl().withHostName(TEST_HOSTNAME).withScheme(TEST_SCHEME); BitbucketServerAuthorizingFileContentProvider fileContentProvider = new BitbucketServerAuthorizingFileContentProvider( url, urlFetcher, personalAccessTokenManager); - PersonalAccessToken token = new PersonalAccessToken(TEST_HOSTNAME, "user1", "token"); + PersonalAccessToken token = + new PersonalAccessToken(TEST_SCHEME + "://" + TEST_HOSTNAME, "user1", "token"); when(personalAccessTokenManager.get(anyString())).thenReturn(token); String fileURL = "https://foo.bar/scm/repo/.devfile"; @@ -54,13 +57,15 @@ public class BitbucketServerAuthorizingFileContentProviderTest { @Test public void shouldFetchTokenIfNotYetPresent() throws Exception { - BitbucketServerUrl url = new BitbucketServerUrl().withHostName(TEST_HOSTNAME); + BitbucketServerUrl url = + new BitbucketServerUrl().withHostName(TEST_HOSTNAME).withScheme(TEST_SCHEME); BitbucketServerAuthorizingFileContentProvider fileContentProvider = new BitbucketServerAuthorizingFileContentProvider( url, urlFetcher, personalAccessTokenManager); - PersonalAccessToken token = new PersonalAccessToken(TEST_HOSTNAME, "user1", "token"); - when(personalAccessTokenManager.get(eq(TEST_HOSTNAME))).thenReturn(token); + PersonalAccessToken token = + new PersonalAccessToken(TEST_SCHEME + "://" + TEST_HOSTNAME, "user1", "token"); + when(personalAccessTokenManager.get(eq(TEST_SCHEME + "://" + TEST_HOSTNAME))).thenReturn(token); String fileURL = "https://foo.bar/scm/repo/.devfile"; @@ -68,7 +73,7 @@ public class BitbucketServerAuthorizingFileContentProviderTest { fileContentProvider.fetchContent(fileURL); // then - verify(personalAccessTokenManager).get(eq(TEST_HOSTNAME)); + verify(personalAccessTokenManager).get(eq(TEST_SCHEME + "://" + TEST_HOSTNAME)); verify(urlFetcher).fetch(eq(fileURL), eq("Bearer token")); } @@ -78,6 +83,7 @@ public class BitbucketServerAuthorizingFileContentProviderTest { BitbucketServerUrl url = new BitbucketServerUrl() .withHostName(TEST_HOSTNAME) + .withScheme(TEST_SCHEME) .withProject("proj") .withRepository("repo") .withDevfileFilenames(Collections.singletonList(".devfile")); @@ -87,7 +93,8 @@ public class BitbucketServerAuthorizingFileContentProviderTest { BitbucketServerAuthorizingFileContentProvider fileContentProvider = new BitbucketServerAuthorizingFileContentProvider( url, urlFetcher, personalAccessTokenManager); - PersonalAccessToken token = new PersonalAccessToken(TEST_HOSTNAME, "user1", "token"); + PersonalAccessToken token = + new PersonalAccessToken(TEST_SCHEME + "://" + TEST_HOSTNAME, "user1", "token"); when(personalAccessTokenManager.get(anyString())).thenReturn(token); // when diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParserTest.java b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParserTest.java index 4af35e693f..8cdd21ead6 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParserTest.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLParserTest.java @@ -83,6 +83,24 @@ public class BitbucketServerURLParserTest { assertEquals(bitbucketServerUrl.getBranch(), branch); } + @Test(dataProvider = "parsing") + public void shouldParseWithoutPredefinedEndpoint( + String url, String user, String project, String repository, String branch) { + // given + bitbucketURLParser = + new BitbucketServerURLParser( + null, devfileFilenamesProvider, oAuthAPI, mock(PersonalAccessTokenManager.class)); + + // when + BitbucketServerUrl bitbucketServerUrl = bitbucketURLParser.parse(url); + + // then + assertEquals(bitbucketServerUrl.getUser(), user); + assertEquals(bitbucketServerUrl.getProject(), project); + assertEquals(bitbucketServerUrl.getRepository(), repository); + assertEquals(bitbucketServerUrl.getBranch(), branch); + } + @Test( expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = @@ -134,6 +152,8 @@ public class BitbucketServerURLParserTest { {"https://bitbucket.2mcl.com/users/user/repos/repo"}, {"https://bitbucket.2mcl.com/users/user/repos/repo/"}, {"https://bbkt.com/scm/project/test1.git"}, + {"ssh://git@bitbucket.2mcl.com:12345/~user/repo.git"}, + {"ssh://git@bitbucket.2mcl.com:12345/project/test1.git"} }; } @@ -141,6 +161,8 @@ public class BitbucketServerURLParserTest { public Object[][] expectedParsing() { return new Object[][] { {"https://bitbucket.2mcl.com/scm/project/test1.git", null, "project", "test1", null}, + {"ssh://git@bitbucket.2mcl.com:12345/project/test1.git", null, "project", "test1", null}, + {"ssh://git@bitbucket.2mcl.com:12345/~user/test1.git", "user", null, "test1", null}, { "https://bitbucket.2mcl.com/projects/project/repos/test1/browse?at=refs%2Fheads%2Fbranch", null, diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLTest.java b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLTest.java new file mode 100644 index 0000000000..49b9e51c4a --- /dev/null +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerURLTest.java @@ -0,0 +1,137 @@ +/* + * 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.bitbucket; + +import static java.lang.String.format; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Iterator; +import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager; +import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider; +import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl; +import org.eclipse.che.security.oauth.OAuthAPI; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class BitbucketServerURLTest { + private BitbucketServerURLParser bitbucketServerURLParser; + @Mock private DevfileFilenamesProvider devfileFilenamesProvider; + + @BeforeMethod + protected void init() { + when(devfileFilenamesProvider.getConfiguredDevfileFilenames()) + .thenReturn(Arrays.asList("devfile.yaml", "foo.bar")); + bitbucketServerURLParser = + new BitbucketServerURLParser( + "https://bitbucket.net", + devfileFilenamesProvider, + mock(OAuthAPI.class), + mock(PersonalAccessTokenManager.class)); + } + + @Test(dataProvider = "urlsProvider") + public void checkDevfileLocation(String repoUrl, String fileUrl) { + lenient() + .when(devfileFilenamesProvider.getConfiguredDevfileFilenames()) + .thenReturn(Arrays.asList("devfile.yaml", "foo.bar")); + + BitbucketServerUrl gitlabUrl = bitbucketServerURLParser.parse(repoUrl); + assertEquals(gitlabUrl.devfileFileLocations().size(), 2); + Iterator iterator = + gitlabUrl.devfileFileLocations().iterator(); + assertEquals(iterator.next().location(), format(fileUrl, "devfile.yaml")); + assertEquals(iterator.next().location(), format(fileUrl, "foo.bar")); + } + + @DataProvider + public static Object[][] urlsProvider() { + return new Object[][] { + { + "https://bitbucket.net/scm/~user/repo.git", + "https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s" + }, + { + "https://bitbucket.net/users/user/repos/repo/browse?at=branch", + "https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s?at=branch" + }, + { + "https://bitbucket.net/users/user/repos/repo", + "https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s" + }, + { + "https://bitbucket.net/scm/project/repo.git", + "https://bitbucket.net/rest/api/1.0/projects/project/repos/repo/raw/%s" + }, + { + "https://bitbucket.net/projects/project/repos/repo/browse?at=branch", + "https://bitbucket.net/rest/api/1.0/projects/project/repos/repo/raw/%s?at=branch" + }, + { + "ssh://git@bitbucket.net:12345/project/repo.git", + "https://bitbucket.net/rest/api/1.0/projects/project/repos/repo/raw/%s" + }, + { + "ssh://git@bitbucket.net:12345/~user/repo.git", + "https://bitbucket.net/rest/api/1.0/users/user/repos/repo/raw/%s" + } + }; + } + + @Test(dataProvider = "repoProvider") + public void checkRepositoryLocation(String rawUrl, String repoUrl) { + BitbucketServerUrl bitbucketServerUrl = bitbucketServerURLParser.parse(rawUrl); + assertEquals(bitbucketServerUrl.repositoryLocation(), repoUrl); + } + + @Test(dataProvider = "urlsProvider") + public void shouldReturnProviderUrl(String repoUrl, String ignored) { + // when + BitbucketServerUrl bitbucketServerUrl = bitbucketServerURLParser.parse(repoUrl); + + // then + assertEquals(bitbucketServerUrl.getProviderUrl(), "https://bitbucket.net"); + } + + @DataProvider + public static Object[][] repoProvider() { + return new Object[][] { + {"https://bitbucket.net/scm/~user/repo.git", "https://bitbucket.net/scm/~user/repo.git"}, + { + "https://bitbucket.net/users/user/repos/repo/browse?at=branch", + "https://bitbucket.net/scm/~user/repo.git" + }, + {"https://bitbucket.net/users/user/repos/repo", "https://bitbucket.net/scm/~user/repo.git"}, + {"https://bitbucket.net/scm/project/repo.git", "https://bitbucket.net/scm/project/repo.git"}, + { + "https://bitbucket.net/projects/project/repos/repo/browse?at=branch", + "https://bitbucket.net/scm/project/repo.git" + }, + { + "ssh://git@bitbucket.net:12345/project/repo.git", + "ssh://git@bitbucket.net:12345/project/repo.git" + }, + { + "ssh://git@bitbucket.net:12345/~user/repo.git", + "ssh://git@bitbucket.net:12345/~user/repo.git" + }, + }; + } +} diff --git a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParser.java b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParser.java index fb70810d69..8eb541794e 100644 --- a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParser.java +++ b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParser.java @@ -36,29 +36,39 @@ public class BitbucketURLParser { Pattern.compile( "^https?://(?[^/@]+)?@?bitbucket\\.org/(?[^/]+)/(?[^/]+)/?(\\.git)?(/(src|branch)/(?[^/]+)/?)?$"); + protected static final Pattern BITBUCKET_SSH_PATTERN = + Pattern.compile("^git@bitbucket.org:(?.*)/(?.*)$"); + public boolean isValid(@NotNull String url) { - return BITBUCKET_PATTERN.matcher(url).matches(); + return BITBUCKET_PATTERN.matcher(url).matches() || BITBUCKET_SSH_PATTERN.matcher(url).matches(); } public BitbucketUrl parse(String url) { // Apply bitbucket url to the regexp - Matcher matcher = BITBUCKET_PATTERN.matcher(url); + boolean isHTTPSUrl = BITBUCKET_PATTERN.matcher(url).matches(); + Matcher matcher = + isHTTPSUrl ? BITBUCKET_PATTERN.matcher(url) : BITBUCKET_SSH_PATTERN.matcher(url); if (!matcher.matches()) { throw new IllegalArgumentException( String.format("The given bitbucket url %s is not a valid URL bitbucket url. ", url)); } - String username = matcher.group("username"); + String workspaceId = matcher.group("workspaceId"); String repoName = matcher.group("repoName"); if (repoName.matches("^[\\w-][\\w.-]*?\\.git$")) { repoName = repoName.substring(0, repoName.length() - 4); } - String workspaceId = matcher.group("workspaceId"); - String branchName = matcher.group("branchName"); + String username = null; + String branchName = null; + if (isHTTPSUrl) { + username = matcher.group("username"); + branchName = matcher.group("branchName"); + } return new BitbucketUrl() .withUsername(username) .withRepository(repoName) + .setIsHTTPSUrl(isHTTPSUrl) .withBranch(branchName) .withWorkspaceId(workspaceId) .withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames()) diff --git a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrl.java b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrl.java index 08f18c3335..f8491987ef 100644 --- a/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrl.java +++ b/wsmaster/che-core-api-factory-bitbucket/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrl.java @@ -41,6 +41,8 @@ public class BitbucketUrl extends DefaultFactoryUrl { /** Repository part of the URL. */ private String repository; + private boolean isHTTPSUrl; + /** Branch name */ private String branch; @@ -81,6 +83,11 @@ public class BitbucketUrl extends DefaultFactoryUrl { return this; } + public BitbucketUrl setIsHTTPSUrl(boolean isHTTPSUrl) { + this.isHTTPSUrl = isHTTPSUrl; + return this; + } + public String getWorkspaceId() { return workspaceId; } @@ -164,13 +171,16 @@ public class BitbucketUrl extends DefaultFactoryUrl { * @return location of the repository. */ protected String repositoryLocation() { - return "https://" - + (this.username != null ? this.username + '@' : "") - + HOSTNAME - + "/" - + this.workspaceId - + "/" - + this.repository - + ".git"; + if (isHTTPSUrl) { + return "https://" + + (this.username != null ? this.username + '@' : "") + + HOSTNAME + + "/" + + workspaceId + + "/" + + repository + + ".git"; + } + return String.format("git@%s:%s/%s.git", HOSTNAME, workspaceId, repository); } } diff --git a/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParserTest.java b/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParserTest.java index 1a3ee3c61e..348d579d70 100644 --- a/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParserTest.java +++ b/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketURLParserTest.java @@ -76,6 +76,15 @@ public class BitbucketURLParserTest { {"https://bitbucket.org/eclipse/che.with.dots.git"}, {"https://bitbucket.org/eclipse/che-with-hyphen"}, {"https://bitbucket.org/eclipse/che-with-hyphen.git"}, + {"git@bitbucket.org:eclipse/che"}, + {"git@bitbucket.org:eclipse/che123"}, + {"git@bitbucket.org:eclipse/che/"}, + {"git@bitbucket.org:eclipse/che/src/4.2.x"}, + {"git@bitbucket.org:eclipse/che/src/master/"}, + {"git@bitbucket.org:eclipse/che.git"}, + {"git@bitbucket.org:eclipse/che.with.dots.git"}, + {"git@bitbucket.org:eclipse/che-with-hyphen"}, + {"git@bitbucket.org:eclipse/che-with-hyphen.git"}, {"https://username@bitbucket.org/eclipse/che"}, {"https://username@bitbucket.org/eclipse/che123"}, {"https://username@bitbucket.org/eclipse/che/"}, @@ -102,6 +111,16 @@ public class BitbucketURLParserTest { {"https://bitbucket.org/eclipse/che-with-hyphen.git", "eclipse", "che-with-hyphen", null}, {"https://bitbucket.org/eclipse/che/", "eclipse", "che", null}, {"https://bitbucket.org/eclipse/repositorygit", "eclipse", "repositorygit", null}, + {"git@bitbucket.org:eclipse/che", "eclipse", "che", null}, + {"git@bitbucket.org:eclipse/che123", "eclipse", "che123", null}, + {"git@bitbucket.org:eclipse/che.git", "eclipse", "che", null}, + {"git@bitbucket.org:eclipse/che.with.dot.git", "eclipse", "che.with.dot", null}, + {"git@bitbucket.org:eclipse/-.git", "eclipse", "-", null}, + {"git@bitbucket.org:eclipse/-j.git", "eclipse", "-j", null}, + {"git@bitbucket.org:eclipse/-", "eclipse", "-", null}, + {"git@bitbucket.org:eclipse/che-with-hyphen", "eclipse", "che-with-hyphen", null}, + {"git@bitbucket.org:eclipse/che-with-hyphen.git", "eclipse", "che-with-hyphen", null}, + {"git@bitbucket.org:eclipse/repositorygit", "eclipse", "repositorygit", null}, {"https://bitbucket.org/eclipse/che/src/4.2.x", "eclipse", "che", "4.2.x"}, {"https://bitbucket.org/eclipse/che/src/master/", "eclipse", "che", "master"} }; @@ -117,7 +136,15 @@ public class BitbucketURLParserTest { {"https://bitbucket.org/eclipse/івапівап.git", "івапівап.git"}, {"https://bitbucket.org/eclipse/ ", " "}, {"https://bitbucket.org/eclipse/.", "."}, - {"https://bitbucket.org/eclipse/ .git", " .git"} + {"https://bitbucket.org/eclipse/ .git", " .git"}, + {"git@bitbucket.org:eclipse/che .git", "che .git"}, + {"git@bitbucket.org:eclipse/.git", ".git"}, + {"git@bitbucket.org:eclipse/myB@dR&pository.git", "myB@dR&pository.git"}, + {"git@bitbucket.org:eclipse/.", "."}, + {"git@bitbucket.org:eclipse/івапівап.git", "івапівап.git"}, + {"git@bitbucket.org:eclipse/ ", " "}, + {"git@bitbucket.org:eclipse/.", "."}, + {"git@bitbucket.org:eclipse/ .git", " .git"} }; } } diff --git a/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrlTest.java b/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrlTest.java index 8b589d2030..cdce9e5954 100644 --- a/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrlTest.java +++ b/wsmaster/che-core-api-factory-bitbucket/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketUrlTest.java @@ -64,6 +64,22 @@ public class BitbucketUrlTest { assertEquals(iterator.next().location(), "https://bitbucket.org/eclipse/che/raw/HEAD/foo.bar"); } + @Test + public void shouldReturnDevfileLocationBySSHUrl() { + bitbucketUrl = bitbucketURLParser.parse("git@bitbucket.org:eclipse/che"); + + lenient() + .when(devfileFilenamesProvider.getConfiguredDevfileFilenames()) + .thenReturn(Arrays.asList("devfile.yaml", "foo.bar")); + + assertEquals(bitbucketUrl.devfileFileLocations().size(), 2); + Iterator iterator = bitbucketUrl.devfileFileLocations().iterator(); + assertEquals( + iterator.next().location(), "https://bitbucket.org/eclipse/che/raw/HEAD/devfile.yaml"); + + assertEquals(iterator.next().location(), "https://bitbucket.org/eclipse/che/raw/HEAD/foo.bar"); + } + @Test public void shouldReturnEmptyCredentials() { // when @@ -77,4 +93,11 @@ public class BitbucketUrlTest { public void checkRepositoryLocation() { assertEquals(bitbucketUrl.repositoryLocation(), "https://bitbucket.org/eclipse/che.git"); } + + @Test + public void shouldReturnRepositoryLocationBySSHUrl() { + bitbucketUrl = bitbucketURLParser.parse("git@bitbucket.org:eclipse/che"); + + assertEquals(bitbucketUrl.repositoryLocation(), "git@bitbucket.org:eclipse/che.git"); + } } diff --git a/wsmaster/che-core-api-factory-git-ssh/pom.xml b/wsmaster/che-core-api-factory-git-ssh/pom.xml deleted file mode 100644 index 7069c8ba1a..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/pom.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - 4.0.0 - - che-master-parent - org.eclipse.che.core - 7.73.0-SNAPSHOT - - che-core-api-factory-git-ssh - jar - Che Core :: API :: Factory Resolver Git Ssh - - true - - - - jakarta.inject - jakarta.inject-api - - - jakarta.validation - jakarta.validation-api - - - 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 - - - ch.qos.logback - logback-classic - test - - - com.github.tomakehurst - wiremock-jre8-standalone - test - - - jakarta.servlet - jakarta.servlet-api - test - - - jakarta.ws.rs - jakarta.ws.rs-api - test - - - org.eclipse.che.core - che-core-commons-json - test - - - org.hamcrest - hamcrest-core - test - - - org.mockito - mockito-core - test - - - org.mockito - mockito-testng - test - - - org.slf4j - jcl-over-slf4j - test - - - org.testng - testng - test - - - diff --git a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshAuthorizingFileContentProvider.java b/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshAuthorizingFileContentProvider.java deleted file mode 100644 index 094a2a2575..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshAuthorizingFileContentProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.git.ssh; - -import org.eclipse.che.api.factory.server.scm.AuthorizingFileContentProvider; -import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager; -import org.eclipse.che.api.workspace.server.devfile.URLFetcher; - -/** - * Git Ssh specific authorizing file content provider. - * - * @author Anatolii Bazko - */ -class GitSshAuthorizingFileContentProvider extends AuthorizingFileContentProvider { - - GitSshAuthorizingFileContentProvider( - GitSshUrl gitSshUrl, - URLFetcher urlFetcher, - PersonalAccessTokenManager personalAccessTokenManager) { - super(gitSshUrl, urlFetcher, personalAccessTokenManager); - } -} diff --git a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshFactoryParametersResolver.java b/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshFactoryParametersResolver.java deleted file mode 100644 index 0d5add093e..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshFactoryParametersResolver.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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.git.ssh; - -import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION; -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.factory.server.RawDevfileUrlFactoryParameterResolver; -import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager; -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.devfile.ProjectDto; -import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto; - -/** - * Provides Factory Parameters resolver for Git Ssh repositories. - * - * @author Anatolii Bazko - */ -@Singleton -public class GitSshFactoryParametersResolver extends RawDevfileUrlFactoryParameterResolver { - - private final GitSshURLParser gitSshURLParser; - - private final PersonalAccessTokenManager personalAccessTokenManager; - - @Inject - public GitSshFactoryParametersResolver( - GitSshURLParser gitSshURLParser, - URLFetcher urlFetcher, - URLFactoryBuilder urlFactoryBuilder, - PersonalAccessTokenManager personalAccessTokenManager) { - super(urlFactoryBuilder, urlFetcher); - this.gitSshURLParser = gitSshURLParser; - this.personalAccessTokenManager = personalAccessTokenManager; - } - - @Override - public boolean accept(@NotNull final Map factoryParameters) { - return factoryParameters.containsKey(URL_PARAMETER_NAME) - && gitSshURLParser.isValid(factoryParameters.get(URL_PARAMETER_NAME)); - } - - @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 GitSshUrl gitSshUrl = gitSshURLParser.parse(factoryParameters.get(URL_PARAMETER_NAME)); - - // create factory from the following location if location exists, else create default factory - return urlFactoryBuilder - .createFactoryFromDevfile( - gitSshUrl, - new GitSshAuthorizingFileContentProvider( - gitSshUrl, urlFetcher, personalAccessTokenManager), - extractOverrideParams(factoryParameters), - true) - .orElseGet(() -> newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo")) - .acceptVisitor(new GitSshFactoryVisitor(gitSshUrl)); - } - - /** - * Visitor that puts the default devfile or updates devfile projects into the Git Ssh Factory, if - * needed. - */ - private class GitSshFactoryVisitor implements FactoryVisitor { - - private final GitSshUrl gitSshUrl; - - private GitSshFactoryVisitor(GitSshUrl gitSshUrl) { - this.gitSshUrl = gitSshUrl; - } - - @Override - public FactoryDevfileV2Dto visit(FactoryDevfileV2Dto factoryDto) { - ScmInfoDto scmInfo = - newDto(ScmInfoDto.class) - .withScmProviderName(gitSshUrl.getProviderName()) - .withRepositoryUrl(gitSshUrl.getRepositoryLocation()); - return factoryDto.withScmInfo(scmInfo); - } - - @Override - public FactoryDto visit(FactoryDto factory) { - if (factory.getDevfile() == null) { - factory.setDevfile(urlFactoryBuilder.buildDefaultDevfile(gitSshUrl.getRepository())); - } - - updateProjects( - factory.getDevfile(), - () -> - newDto(ProjectDto.class) - .withSource( - newDto(SourceDto.class) - .withLocation(gitSshUrl.getRepositoryLocation()) - .withType("git")) - .withName(gitSshUrl.getRepository()), - project -> {}); - - return factory; - } - } - - @Override - public RemoteFactoryUrl parseFactoryUrl(String factoryUrl) { - return gitSshURLParser.parse(factoryUrl); - } -} diff --git a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshScmFileResolver.java b/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshScmFileResolver.java deleted file mode 100644 index a28b208cb8..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshScmFileResolver.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.git.ssh; - -import jakarta.validation.constraints.NotNull; -import javax.inject.Inject; -import org.eclipse.che.api.factory.server.ScmFileResolver; - -/** - * Git Ssh specific SCM file resolver. - * - * @author Anatolii Bazko - */ -public class GitSshScmFileResolver implements ScmFileResolver { - - private final GitSshURLParser gitSshURLParser; - - @Inject - public GitSshScmFileResolver(GitSshURLParser gitSshURLParser) { - this.gitSshURLParser = gitSshURLParser; - } - - @Override - public boolean accept(@NotNull String repository) { - return gitSshURLParser.isValid(repository); - } - - /** - * There is no way to get a file content from a git repository via ssh protocol. So this method - * always returns an empty string. It allows to start a workspace from an empty devfile. - */ - @Override - public String fileContent(@NotNull String repository, @NotNull String filePath) { - return ""; - } -} diff --git a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshURLParser.java b/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshURLParser.java deleted file mode 100644 index 9c25e825b7..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshURLParser.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.git.ssh; - -import static java.lang.String.format; -import static java.util.regex.Pattern.compile; - -import jakarta.validation.constraints.NotNull; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.inject.Inject; -import javax.inject.Singleton; -import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider; - -/** - * Parser of String Git Ssh URLs and provide {@link GitSshUrl} objects. - * - * @author Anatolii Bazko - */ -@Singleton -public class GitSshURLParser { - - private final Pattern gitSshPattern; - - private final DevfileFilenamesProvider devfileFilenamesProvider; - - @Inject - public GitSshURLParser(DevfileFilenamesProvider devfileFilenamesProvider) { - this.devfileFilenamesProvider = devfileFilenamesProvider; - this.gitSshPattern = compile("^git@(?[^:]++):(.*)/(?[^/]++)$"); - } - - public boolean isValid(@NotNull String url) { - return gitSshPattern.matcher(url).matches(); - } - - public GitSshUrl parse(String url) { - Matcher matcher = gitSshPattern.matcher(url); - if (!matcher.matches()) { - throw new IllegalArgumentException( - format("The given url %s is not a valid. It should start with git@", url)); - } - - String hostName = matcher.group("hostName"); - String repoName = matcher.group("repoName"); - if (repoName.endsWith(".git")) { - repoName = repoName.substring(0, repoName.length() - 4); - } - - return new GitSshUrl() - .withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames()) - .withHostName(hostName) - .withRepository(repoName) - .withRepositoryLocation(url) - .withUrl(url); - } -} diff --git a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshUrl.java b/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshUrl.java deleted file mode 100644 index ae180c9e07..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/src/main/java/org/eclipse/che/api/factory/server/git/ssh/GitSshUrl.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.git.ssh; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import org.eclipse.che.api.factory.server.urlfactory.DefaultFactoryUrl; - -/** - * Representation of Git Ssh URL, allowing to get details from it. - * - * @author Anatolii Bazko - */ -public class GitSshUrl extends DefaultFactoryUrl { - - private String repository; - private String hostName; - - private String repositoryLocation; - - private final List devfileFilenames = new ArrayList<>(); - - protected GitSshUrl() {} - - @Override - public String getProviderName() { - return "git-ssh"; - } - - @Override - public String getBranch() { - return null; - } - - public GitSshUrl withDevfileFilenames(List devfileFilenames) { - this.devfileFilenames.addAll(devfileFilenames); - return this; - } - - @Override - public void setDevfileFilename(String devfileName) { - this.devfileFilenames.clear(); - this.devfileFilenames.add(devfileName); - } - - @Override - public List devfileFileLocations() { - return devfileFilenames.stream().map(this::createDevfileLocation).collect(Collectors.toList()); - } - - @Override - public String rawFileLocation(String filename) { - return filename; - } - - private DevfileLocation createDevfileLocation(String devfileFilename) { - return new DevfileLocation() { - @Override - public Optional filename() { - return Optional.of(devfileFilename); - } - - @Override - public String location() { - return devfileFilename; - } - }; - } - - @Override - public String getHostName() { - return hostName; - } - - public GitSshUrl withHostName(String hostName) { - this.hostName = hostName; - return this; - } - - public String getRepositoryLocation() { - return repositoryLocation; - } - - public GitSshUrl withRepositoryLocation(String repositoryLocation) { - this.repositoryLocation = repositoryLocation; - return this; - } - - public String getRepository() { - return repository; - } - - public GitSshUrl withRepository(String repository) { - this.repository = repository; - return this; - } -} diff --git a/wsmaster/che-core-api-factory-git-ssh/src/test/java/org/eclipse/che/api/factory/server/git/ssh/GitSshURLParserTest.java b/wsmaster/che-core-api-factory-git-ssh/src/test/java/org/eclipse/che/api/factory/server/git/ssh/GitSshURLParserTest.java deleted file mode 100644 index 715e5f544b..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/src/test/java/org/eclipse/che/api/factory/server/git/ssh/GitSshURLParserTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.git.ssh; - -import static org.mockito.Mockito.mock; -import static org.testng.Assert.assertEquals; - -import org.eclipse.che.api.factory.server.urlfactory.DevfileFilenamesProvider; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -/** @author Anatalii Bazko */ -@Listeners(MockitoTestNGListener.class) -public class GitSshURLParserTest { - - private GitSshURLParser gitSshURLParser; - - @BeforeMethod - protected void start() { - gitSshURLParser = new GitSshURLParser(mock(DevfileFilenamesProvider.class)); - } - - @Test(dataProvider = "parsing") - public void testParse(String url, String hostName, String repository) { - GitSshUrl gitSshUrl = gitSshURLParser.parse(url); - - assertEquals(gitSshUrl.getHostName(), hostName); - assertEquals(gitSshUrl.getRepository(), repository); - } - - @DataProvider(name = "parsing") - public Object[][] expectedParsing() { - return new Object[][] { - {"git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo", "ssh.dev.azure.com", "MyRepo"}, - {"git@github.com:MyOrg/MyRepo.git", "github.com", "MyRepo"}, - }; - } -} diff --git a/wsmaster/che-core-api-factory-git-ssh/src/test/resources/logback-test.xml b/wsmaster/che-core-api-factory-git-ssh/src/test/resources/logback-test.xml deleted file mode 100644 index 704cbbf50f..0000000000 --- a/wsmaster/che-core-api-factory-git-ssh/src/test/resources/logback-test.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - %-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n%nopex - - - - - - - - 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 811853e51d..a8ea88b991 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 @@ -20,6 +20,7 @@ import static org.eclipse.che.api.factory.server.github.GithubApiClient.GITHUB_S 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; @@ -63,6 +64,8 @@ public class GithubURLParser { */ private final Pattern githubPattern; + private final Pattern githubSSHPattern; + private final boolean disableSubdomainIsolation; @Inject @@ -101,10 +104,14 @@ public class GithubURLParser { format( "^%s/(?[^/]+)/(?[^/]++)((/)|(?:/tree/(?.++))|(/pull/(?\\d++)))?$", endpoint)); + this.githubSSHPattern = + compile(format("^git@%s:(?.*)/(?.*)$", URI.create(endpoint).getHost())); } public boolean isValid(@NotNull String url) { - return githubPattern.matcher(trimEnd(url, '/')).matches(); + String trimmedUrl = trimEnd(url, '/'); + return githubPattern.matcher(trimmedUrl).matches() + || githubSSHPattern.matcher(trimmedUrl).matches(); } public GithubUrl parseWithoutAuthentication(String url) throws ApiException { @@ -116,13 +123,11 @@ public class GithubURLParser { } private GithubUrl parse(String url, boolean authenticationRequired) throws ApiException { - // Apply github url to the regexp - Matcher matcher = githubPattern.matcher(url); + boolean isHTTPSUrl = githubPattern.matcher(url).matches(); + Matcher matcher = isHTTPSUrl ? githubPattern.matcher(url) : githubSSHPattern.matcher(url); if (!matcher.matches()) { throw new IllegalArgumentException( - format( - "The given github url %s is not a valid URL github url. It should start with https://github.com//", - url)); + format("The given url %s is not a valid github URL. ", url)); } String serverUrl = @@ -135,9 +140,12 @@ public class GithubURLParser { repoName = repoName.substring(0, repoName.length() - 4); } - String branchName = matcher.group("branchName"); - - String pullRequestId = matcher.group("pullRequestId"); + String branchName = null; + String pullRequestId = null; + if (isHTTPSUrl) { + branchName = matcher.group("branchName"); + pullRequestId = matcher.group("pullRequestId"); + } if (pullRequestId != null) { GithubPullRequest pullRequest = @@ -169,6 +177,7 @@ public class GithubURLParser { return new GithubUrl() .withUsername(repoUser) .withRepository(repoName) + .setIsHTTPSUrl(isHTTPSUrl) .withServerUrl(serverUrl) .withDisableSubdomainIsolation(disableSubdomainIsolation) .withBranch(branchName) 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/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java index 46ea5be275..148bae9bf4 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/src/main/java/org/eclipse/che/api/factory/server/github/GithubUrl.java @@ -37,6 +37,8 @@ public class GithubUrl extends DefaultFactoryUrl { /** Username part of github URL */ private String username; + private boolean isHTTPSUrl = true; + /** Repository part of the URL. */ private String repository; @@ -78,6 +80,11 @@ public class GithubUrl extends DefaultFactoryUrl { return this; } + public GithubUrl setIsHTTPSUrl(boolean isHTTPSUrl) { + this.isHTTPSUrl = isHTTPSUrl; + return this; + } + /** * Gets repository of this github url * @@ -209,8 +216,17 @@ public class GithubUrl extends DefaultFactoryUrl { * @return location of the repository. */ protected String repositoryLocation() { - return (isNullOrEmpty(serverUrl) ? HOSTNAME : serverUrl) - + "/" + if (isHTTPSUrl) { + return (isNullOrEmpty(serverUrl) ? HOSTNAME : serverUrl) + + "/" + + this.username + + "/" + + this.repository + + ".git"; + } + return "git@" + + getHostName().substring(getHostName().indexOf("://") + 3) + + ":" + this.username + "/" + this.repository diff --git a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubURLParserTest.java b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubURLParserTest.java index 42a97931a4..3183546cac 100644 --- a/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubURLParserTest.java +++ b/wsmaster/che-core-api-factory-github/src/test/java/org/eclipse/che/api/factory/server/github/GithubURLParserTest.java @@ -108,7 +108,13 @@ public class GithubURLParserTest { {"https://github.com/eclipse/che.git"}, {"https://github.com/eclipse/che.with.dots.git"}, {"https://github.com/eclipse/che-with-hyphen"}, - {"https://github.com/eclipse/che-with-hyphen.git"} + {"https://github.com/eclipse/che-with-hyphen.git"}, + {"git@github.com:eclipse/che.git)"}, + {"git@github.com:eclipse/che)"}, + {"git@github.com:eclipse/che123)"}, + {"git@github.com:eclipse/che.with.dots.git)"}, + {"git@github.com:eclipse/che-with-hyphen)"}, + {"git@github.com:eclipse/che-with-hyphen.git)"} }; } @@ -133,7 +139,18 @@ public class GithubURLParserTest { "eclipse", "che", "branch/with/slash" - } + }, + {"git@github.com:eclipse/che", "eclipse", "che", null}, + {"git@github.com:eclipse/che123", "eclipse", "che123", null}, + {"git@github.com:eclipse/che.git", "eclipse", "che", null}, + {"git@github.com:eclipse/che.with.dot.git", "eclipse", "che.with.dot", null}, + {"git@github.com:eclipse/-.git", "eclipse", "-", null}, + {"git@github.com:eclipse/-j.git", "eclipse", "-j", null}, + {"git@github.com:eclipse/-", "eclipse", "-", null}, + {"git@github.com:eclipse/che-with-hyphen", "eclipse", "che-with-hyphen", null}, + {"git@github.com:eclipse/che-with-hyphen.git", "eclipse", "che-with-hyphen", null}, + {"git@github.com:eclipse/che/", "eclipse", "che", null}, + {"git@github.com:eclipse/repositorygit", "eclipse", "repositorygit", null}, }; } @@ -147,7 +164,15 @@ public class GithubURLParserTest { {"https://github.com/eclipse/івапівап.git", "івапівап.git"}, {"https://github.com/eclipse/ ", " "}, {"https://github.com/eclipse/.", "."}, - {"https://github.com/eclipse/ .git", " .git"} + {"https://github.com/eclipse/ .git", " .git"}, + {"git@github.com:eclipse/che .git", "che .git"}, + {"git@github.com:eclipse/.git", ".git"}, + {"git@github.com:eclipse/myB@dR&pository.git", "myB@dR&pository.git"}, + {"git@github.com:eclipse/.", "."}, + {"git@github.com:eclipse/івапівап.git", "івапівап.git"}, + {"git@github.com:eclipse/ ", " "}, + {"git@github.com:eclipse/.", "."}, + {"git@github.com:eclipse/ .git", " .git"} }; } 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 cdb5aeaf00..8c0ff82e14 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 @@ -64,6 +64,34 @@ public class GithubUrlTest { iterator.next().location(), "https://raw.githubusercontent.com/eclipse/che/HEAD/foo.bar"); } + @Test + public void shouldReturnDevfileLocationFromSSHUrl() throws Exception { + DevfileFilenamesProvider devfileFilenamesProvider = mock(DevfileFilenamesProvider.class); + + /** Parser used to create the url. */ + GithubURLParser githubUrlParser = + new GithubURLParser( + mock(PersonalAccessTokenManager.class), + devfileFilenamesProvider, + githubApiClient, + null, + false); + + when(devfileFilenamesProvider.getConfiguredDevfileFilenames()) + .thenReturn(Arrays.asList("devfile.yaml", "foo.bar")); + + GithubUrl githubUrl = githubUrlParser.parse("git@github.com:eclipse/che"); + + assertEquals(githubUrl.devfileFileLocations().size(), 2); + Iterator iterator = githubUrl.devfileFileLocations().iterator(); + assertEquals( + iterator.next().location(), + "https://raw.githubusercontent.com/eclipse/che/HEAD/devfile.yaml"); + + assertEquals( + iterator.next().location(), "https://raw.githubusercontent.com/eclipse/che/HEAD/foo.bar"); + } + /** Check the original repository */ @Test public void checkRepositoryLocation() throws Exception { @@ -86,6 +114,27 @@ public class GithubUrlTest { assertEquals(githubUrl.repositoryLocation(), "https://github.com/eclipse/che.git"); } + @Test + public void shouldReturnRepositoryLocationFromSSHUrl() throws Exception { + DevfileFilenamesProvider devfileFilenamesProvider = mock(DevfileFilenamesProvider.class); + + /** Parser used to create the url. */ + GithubURLParser githubUrlParser = + new GithubURLParser( + mock(PersonalAccessTokenManager.class), + devfileFilenamesProvider, + githubApiClient, + null, + false); + + when(devfileFilenamesProvider.getConfiguredDevfileFilenames()) + .thenReturn(Arrays.asList("devfile.yaml", "foo.bar")); + + GithubUrl githubUrl = githubUrlParser.parse("git@github.com:eclipse/che.git"); + + assertEquals(githubUrl.repositoryLocation(), "git@github.com:eclipse/che.git"); + } + @Test public void testRawFileLocationWithDefaultBranchName() { String file = ".che/che-theia-plugins.yaml"; diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrl.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrl.java index 057630821a..74f3f108c3 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrl.java +++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrl.java @@ -11,10 +11,10 @@ */ package org.eclipse.che.api.factory.server.gitlab; +import static com.google.common.base.Strings.isNullOrEmpty; import static java.net.URLEncoder.encode; import com.google.common.base.Charsets; -import com.google.common.base.Strings; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -37,6 +37,9 @@ public class GitlabUrl extends DefaultFactoryUrl { /** Hostname of the gitlab URL */ private String hostName; + /** Scheme of the gitlab URL */ + private String scheme; + /** Project part of the gitlab URL */ private String project; @@ -63,6 +66,11 @@ public class GitlabUrl extends DefaultFactoryUrl { return NAME; } + @Override + public String getProviderUrl() { + return (isNullOrEmpty(scheme) ? "https" : scheme) + "://" + hostName; + } + /** * Gets hostname of this gitlab url * @@ -77,6 +85,11 @@ public class GitlabUrl extends DefaultFactoryUrl { return this; } + public GitlabUrl withScheme(String scheme) { + this.scheme = scheme; + return this; + } + /** * Gets project of this bitbucket url * @@ -120,7 +133,7 @@ public class GitlabUrl extends DefaultFactoryUrl { } protected GitlabUrl withBranch(String branch) { - if (!Strings.isNullOrEmpty(branch)) { + if (!isNullOrEmpty(branch)) { this.branch = branch; } return this; @@ -158,7 +171,7 @@ public class GitlabUrl extends DefaultFactoryUrl { public String rawFileLocation(String fileName) { String resultUrl = new StringJoiner("/") - .add(hostName) + .add((isNullOrEmpty(scheme) ? "https" : scheme) + "://" + hostName) .add("api/v4/projects") // use URL-encoded path to the project as a selector instead of id .add(encode(subGroups, Charsets.UTF_8)) @@ -180,6 +193,9 @@ public class GitlabUrl extends DefaultFactoryUrl { * @return location of the repository. */ protected String repositoryLocation() { - return hostName + "/" + subGroups + ".git"; + if (isNullOrEmpty(scheme)) { + return "git@" + hostName + ":" + subGroups + ".git"; + } + return scheme + "://" + hostName + "/" + subGroups + ".git"; } } diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java index 7545ab38ee..952bd5c6b3 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java +++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParser.java @@ -18,6 +18,7 @@ import static org.eclipse.che.commons.lang.StringUtils.trimEnd; import com.google.common.base.Splitter; import jakarta.validation.constraints.NotNull; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -46,8 +47,10 @@ public class GitlabUrlParser { private final PersonalAccessTokenManager personalAccessTokenManager; private static final List gitlabUrlPatternTemplates = List.of( - "^(?%s)/(?([^/]++/?)+)/-/tree/(?.++)(/)?", - "^(?%s)/(?.*)"); // a wider one, should be the last in the + "^(?%s)://(?%s)/(?([^/]++/?)+)/-/tree/(?.++)(/)?", + "^(?%s)://(?%s)/(?.*)"); // a wider one, should be the last in + // the list + private final String gitlabSSHPatternTemplate = "^git@(?%s):(?.*)$"; // list private final List gitlabUrlPatterns = new ArrayList<>(); private static final String OAUTH_PROVIDER_NAME = "gitlab"; @@ -62,13 +65,18 @@ public class GitlabUrlParser { if (gitlabEndpoints != null) { for (String gitlabEndpoint : Splitter.on(",").split(gitlabEndpoints)) { String trimmedEndpoint = trimEnd(gitlabEndpoint, '/'); + URI uri = URI.create(trimmedEndpoint); + String schema = uri.getScheme(); + String host = uri.getHost(); for (String gitlabUrlPatternTemplate : gitlabUrlPatternTemplates) { - gitlabUrlPatterns.add(compile(format(gitlabUrlPatternTemplate, trimmedEndpoint))); + gitlabUrlPatterns.add(compile(format(gitlabUrlPatternTemplate, schema, host))); } + gitlabUrlPatterns.add(compile(format(gitlabSSHPatternTemplate, host))); } } else { gitlabUrlPatternTemplates.forEach( - t -> gitlabUrlPatterns.add(compile(format(t, "https://gitlab.com")))); + t -> gitlabUrlPatterns.add(compile(format(t, "https", "gitlab.com")))); + gitlabUrlPatterns.add(compile(format(gitlabSSHPatternTemplate, "gitlab.com"))); } } @@ -112,7 +120,7 @@ public class GitlabUrlParser { gitlabApiClient.getOAuthTokenInfo(""); } catch (ScmCommunicationException e) { return e.getStatusCode() == HTTP_UNAUTHORIZED; - } catch (ScmItemNotFoundException e) { + } catch (ScmItemNotFoundException | IllegalArgumentException e) { return false; } } @@ -120,18 +128,32 @@ public class GitlabUrlParser { } private Optional getPatternMatcherByUrl(String url) { - Optional serverUrlOptional = getServerUrl(url); - if (serverUrlOptional.isPresent()) { - String serverUrl = serverUrlOptional.get(); - return gitlabUrlPatternTemplates.stream() - .map(t -> compile(format(t, serverUrl)).matcher(url)) - .filter(Matcher::matches) - .findAny(); - } - return Optional.empty(); + URI uri = + URI.create( + url.matches(format(gitlabSSHPatternTemplate, ".*")) + ? "ssh://" + url.replace(":", "/") + : url); + String scheme = uri.getScheme(); + String host = uri.getHost(); + return gitlabUrlPatternTemplates.stream() + .map(t -> compile(format(t, scheme, host)).matcher(url)) + .filter(Matcher::matches) + .findAny() + .or( + () -> { + Matcher matcher = compile(format(gitlabSSHPatternTemplate, host)).matcher(url); + if (matcher.matches()) { + return Optional.of(matcher); + } + return Optional.empty(); + }); } private Optional getServerUrl(String repositoryUrl) { + if (repositoryUrl.startsWith("git@")) { + String substring = repositoryUrl.substring(4); + return Optional.of("https://" + substring.substring(0, substring.indexOf(":"))); + } Matcher serverUrlMatcher = compile("[^/|:]/").matcher(repositoryUrl); if (serverUrlMatcher.find()) { return Optional.of( @@ -162,6 +184,12 @@ public class GitlabUrlParser { } private GitlabUrl parse(Matcher matcher) { + String scheme = null; + try { + scheme = matcher.group("scheme"); + } catch (IllegalArgumentException e) { + // ok no such group + } String host = matcher.group("host"); String subGroups = trimEnd(matcher.group("subgroups"), '/'); if (subGroups.endsWith(".git")) { @@ -177,6 +205,7 @@ public class GitlabUrlParser { return new GitlabUrl() .withHostName(host) + .withScheme(scheme) .withSubGroups(subGroups) .withBranch(branch) .withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames()); diff --git a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabAuthorizingFileContentProviderTest.java b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabAuthorizingFileContentProviderTest.java index 0c784206ac..9685b6cf55 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabAuthorizingFileContentProviderTest.java +++ b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabAuthorizingFileContentProviderTest.java @@ -32,8 +32,7 @@ public class GitlabAuthorizingFileContentProviderTest { @Test public void shouldExpandRelativePaths() throws Exception { URLFetcher urlFetcher = Mockito.mock(URLFetcher.class); - GitlabUrl gitlabUrl = - new GitlabUrl().withHostName("https://gitlab.net").withSubGroups("eclipse/che"); + GitlabUrl gitlabUrl = new GitlabUrl().withHostName("gitlab.net").withSubGroups("eclipse/che"); FileContentProvider fileContentProvider = new GitlabAuthorizingFileContentProvider(gitlabUrl, urlFetcher, personalAccessTokenManager); var personalAccessToken = new PersonalAccessToken("foo", "che", "my-token"); diff --git a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java index 27aadf7c2a..e8022fd043 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java +++ b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlParserTest.java @@ -79,6 +79,22 @@ public class GitlabUrlParserTest { assertEquals(gitlabUrl.getBranch(), branch); } + /** Compare parsing */ + @Test(dataProvider = "parsing") + public void shouldParseWithoutPredefinedEndpoint( + String url, String project, String subGroups, String branch) { + // given + gitlabUrlParser = + new GitlabUrlParser(null, devfileFilenamesProvider, mock(PersonalAccessTokenManager.class)); + // when + GitlabUrl gitlabUrl = gitlabUrlParser.parse(url); + + // then + assertEquals(gitlabUrl.getProject(), project); + assertEquals(gitlabUrl.getSubGroups(), subGroups); + assertEquals(gitlabUrl.getBranch(), branch); + } + @Test public void shouldValidateUrlByApiRequest() { // given @@ -114,7 +130,12 @@ public class GitlabUrlParserTest { {"https://gitlab1.com/user/project/"}, {"https://gitlab1.com/user/project/repo/"}, {"https://gitlab1.com/user/project/-/tree/master/"}, - {"https://gitlab1.com/user/project/repo/-/tree/master/subfolder"} + {"https://gitlab1.com/user/project/repo/-/tree/master/subfolder"}, + {"git@gitlab1.com:user/project/test1.git"}, + {"git@gitlab1.com:user/project1.git"}, + {"git@gitlab.foo.xxx:scm/project/test1.git"}, + {"git@gitlab1.com:user/project/"}, + {"git@gitlab1.com:user/project/repo/"}, }; } @@ -131,6 +152,16 @@ public class GitlabUrlParserTest { }, {"https://gitlab1.com/user/project/", "project", "user/project", null}, {"https://gitlab1.com/user/project/repo/", "repo", "user/project/repo", null}, + {"git@gitlab1.com:user/project1.git", "project1", "user/project1", null}, + {"git@gitlab1.com:user/project/test1.git", "test1", "user/project/test1", null}, + { + "git@gitlab1.com:user/project/group1/group2/test1.git", + "test1", + "user/project/group1/group2/test1", + null + }, + {"git@gitlab1.com:user/project/", "project", "user/project", null}, + {"git@gitlab1.com:user/project/repo/", "repo", "user/project/repo", null}, {"https://gitlab1.com/user/project/-/tree/master/", "project", "user/project", "master"}, {"https://gitlab1.com/user/project/repo/-/tree/foo", "repo", "user/project/repo", "foo"}, { diff --git a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlTest.java b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlTest.java index e03e5b49f1..c392362af6 100644 --- a/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlTest.java +++ b/wsmaster/che-core-api-factory-gitlab/src/test/java/org/eclipse/che/api/factory/server/gitlab/GitlabUrlTest.java @@ -66,6 +66,15 @@ public class GitlabUrlTest { assertEquals(iterator.next().location(), format(fileUrl, "foo.bar")); } + @Test(dataProvider = "urlsProvider") + public void shouldReturnProviderUrl(String repoUrl, String ignored) { + // when + GitlabUrl gitlabUrl = gitlabUrlParser.parse(repoUrl); + + // then + assertEquals(gitlabUrl.getProviderUrl(), "https://gitlab.net"); + } + @DataProvider public static Object[][] urlsProvider() { return new Object[][] { @@ -77,6 +86,14 @@ public class GitlabUrlTest { "https://gitlab.net/eclipse/fooproj/che.git", "https://gitlab.net/api/v4/projects/eclipse%%2Ffooproj%%2Fche/repository/files/%s/raw" }, + { + "git@gitlab.net:eclipse/che.git", + "https://gitlab.net/api/v4/projects/eclipse%%2Fche/repository/files/%s/raw" + }, + { + "git@gitlab.net:eclipse/fooproj/che.git", + "https://gitlab.net/api/v4/projects/eclipse%%2Ffooproj%%2Fche/repository/files/%s/raw" + }, { "https://gitlab.net/eclipse/fooproj/-/tree/master/", "https://gitlab.net/api/v4/projects/eclipse%%2Ffooproj/repository/files/%s/raw?ref=master" @@ -100,6 +117,8 @@ public class GitlabUrlTest { return new Object[][] { {"https://gitlab.net/eclipse/che.git", "https://gitlab.net/eclipse/che.git"}, {"https://gitlab.net/eclipse/foo/che.git", "https://gitlab.net/eclipse/foo/che.git"}, + {"git@gitlab.net:eclipse/che.git", "git@gitlab.net:eclipse/che.git"}, + {"git@gitlab.net:eclipse/foo/che.git", "git@gitlab.net:eclipse/foo/che.git"}, { "https://gitlab.net/eclipse/fooproj/che/-/tree/master/", "https://gitlab.net/eclipse/fooproj/che.git" diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java index aa5de7bf30..14cf3fc176 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java @@ -80,7 +80,7 @@ public class AuthorizingFileContentProvider String authorization; if (isNullOrEmpty(credentials)) { PersonalAccessToken token = - personalAccessTokenManager.get(remoteFactoryUrl.getHostName()); + personalAccessTokenManager.get(remoteFactoryUrl.getProviderUrl()); authorization = formatAuthorization( token.getToken(), diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/DefaultFactoryUrl.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/DefaultFactoryUrl.java index 16078985ff..6560435f68 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/DefaultFactoryUrl.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/DefaultFactoryUrl.java @@ -61,6 +61,11 @@ public class DefaultFactoryUrl implements RemoteFactoryUrl { return URI.create(devfileFileLocation).getHost(); } + @Override + public String getProviderUrl() { + return getHostName(); + } + @Override public String getBranch() { return null; diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/RemoteFactoryUrl.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/RemoteFactoryUrl.java index 213be2cbd2..7a8f3e25b7 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/RemoteFactoryUrl.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/RemoteFactoryUrl.java @@ -39,6 +39,9 @@ public interface RemoteFactoryUrl { /** Remote hostname */ String getHostName(); + /** Remote provider URL */ + String getProviderUrl(); + /** Remote branch */ String getBranch(); diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/scm/AuthorizingFactoryParameterResolverTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/scm/AuthorizingFactoryParameterResolverTest.java index 6853e1a735..6b10db9cc5 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/scm/AuthorizingFactoryParameterResolverTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/scm/AuthorizingFactoryParameterResolverTest.java @@ -46,7 +46,7 @@ public class AuthorizingFactoryParameterResolverTest { @Test public void shouldFetchContentWithAuthentication() throws Exception { // given - when(remoteFactoryUrl.getHostName()).thenReturn("hostName"); + when(remoteFactoryUrl.getProviderUrl()).thenReturn("https://provider.url"); when(urlFetcher.fetch(anyString(), anyString())).thenReturn("content"); when(personalAccessTokenManager.get(anyString())).thenReturn(personalAccessToken); diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java index bc24344372..324eb6fa79 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java @@ -214,6 +214,11 @@ public class URLFactoryBuilderTest { return null; } + @Override + public String getProviderUrl() { + return null; + } + @Override public String getBranch() { return null; @@ -286,6 +291,11 @@ public class URLFactoryBuilderTest { return null; } + @Override + public String getProviderUrl() { + return null; + } + @Override public String getBranch() { return null; diff --git a/wsmaster/pom.xml b/wsmaster/pom.xml index 0b3699f0d3..fad46c1a16 100644 --- a/wsmaster/pom.xml +++ b/wsmaster/pom.xml @@ -40,7 +40,6 @@ che-core-api-account che-core-api-user che-core-api-factory-azure-devops - che-core-api-factory-git-ssh che-core-api-factory-shared che-core-api-factory che-core-api-factory-github