mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-07 20:02:04 +02:00
Fix repository resolution for ssh urls with context path
Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
committed by
SCM-Manager
parent
0361ae3c6c
commit
f28eaeca15
2
gradle/changelog/ssh_repo_resolution.yaml
Normal file
2
gradle/changelog/ssh_repo_resolution.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: fixed
|
||||
description: Resolution of repositories from ssh urls with context paths
|
||||
@@ -41,6 +41,7 @@ import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -306,6 +307,22 @@ public class ScmConfiguration implements Configuration {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context path from the base url (see {@link #getBaseUrl()}) without starting or ending slashes.
|
||||
*
|
||||
* @since 2.42.0
|
||||
*/
|
||||
public String getServerContextPath() {
|
||||
String path = URI.create(getBaseUrl()).getPath();
|
||||
if (path.startsWith("/") && path.length() > 1) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
if (path.endsWith("/")) {
|
||||
return path.substring(0, path.length() - 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date format for the user interface. This format is a
|
||||
* JavaScript date format, from the library moment.js.
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
package sonia.scm.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -43,4 +45,11 @@ class ScmConfigurationTest {
|
||||
assertThat(scmConfiguration.isDefaultPluginAuthUrl()).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({"https://hog.hitchiker.com/scm,scm", "https://hog.hitchiker.com/scm/,scm", "https://hog.hitchiker.com/,", "https://hog.hitchiker.com,"})
|
||||
void shouldReturnContextPath(String input, String expected) {
|
||||
scmConfiguration.setBaseUrl(input);
|
||||
|
||||
assertThat(scmConfiguration.getServerContextPath()).isEqualTo(expected == null ? "" : expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,13 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.protocolcommand.git;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.protocolcommand.RepositoryContext;
|
||||
import sonia.scm.protocolcommand.RepositoryContextResolver;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
@@ -35,34 +38,43 @@ import sonia.scm.repository.RepositoryManager;
|
||||
import javax.inject.Inject;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
|
||||
public class GitRepositoryContextResolver implements RepositoryContextResolver {
|
||||
|
||||
private RepositoryManager repositoryManager;
|
||||
private RepositoryLocationResolver locationResolver;
|
||||
private final RepositoryManager repositoryManager;
|
||||
private final RepositoryLocationResolver locationResolver;
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
@Inject
|
||||
public GitRepositoryContextResolver(RepositoryManager repositoryManager, RepositoryLocationResolver locationResolver) {
|
||||
public GitRepositoryContextResolver(RepositoryManager repositoryManager, RepositoryLocationResolver locationResolver, ScmConfiguration configuration) {
|
||||
this.repositoryManager = repositoryManager;
|
||||
this.locationResolver = locationResolver;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public RepositoryContext resolve(String[] args) {
|
||||
NamespaceAndName namespaceAndName = extractNamespaceAndName(args);
|
||||
NamespaceAndName namespaceAndName = extractNamespaceAndName(args).orElseThrow(() -> NotFoundException.notFound(ContextEntry.ContextBuilder.entity("path", String.join("/", args))));
|
||||
Repository repository = repositoryManager.get(namespaceAndName);
|
||||
Path path = locationResolver.forClass(Path.class).getLocation(repository.getId()).resolve("data");
|
||||
return new RepositoryContext(repository, path);
|
||||
}
|
||||
|
||||
private NamespaceAndName extractNamespaceAndName(String[] args) {
|
||||
private Optional<NamespaceAndName> extractNamespaceAndName(String[] args) {
|
||||
String path = args[args.length - 1];
|
||||
Iterator<String> it = Splitter.on('/').omitEmptyStrings().split(path).iterator();
|
||||
String type = it.next();
|
||||
if (type.equals(configuration.getServerContextPath())) {
|
||||
type = it.next();
|
||||
}
|
||||
if ("repo".equals(type)) {
|
||||
String ns = it.next();
|
||||
String name = it.next();
|
||||
return new NamespaceAndName(ns, name);
|
||||
return of((new NamespaceAndName(ns, name)));
|
||||
}
|
||||
return null;
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,31 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.protocolcommand.git;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.protocolcommand.RepositoryContext;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryLocationResolver;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -53,19 +58,47 @@ class GitRepositoryContextResolverTest {
|
||||
RepositoryManager repositoryManager;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
RepositoryLocationResolver locationResolver;
|
||||
@Mock(answer = Answers.CALLS_REAL_METHODS)
|
||||
ScmConfiguration scmConfiguration;
|
||||
|
||||
@InjectMocks
|
||||
GitRepositoryContextResolver resolver;
|
||||
|
||||
@BeforeEach
|
||||
void mockConfiguration() {
|
||||
when(scmConfiguration.getBaseUrl()).thenReturn("https://hog.hitchhiker.com/scm");
|
||||
}
|
||||
|
||||
@Nested
|
||||
class WithRepository {
|
||||
|
||||
@BeforeEach
|
||||
void mockRepository(@TempDir Path repositoryPath) throws IOException {
|
||||
when(scmConfiguration.getBaseUrl()).thenReturn("https://hog.hitchhiker.com/scm");
|
||||
when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(REPOSITORY);
|
||||
when(locationResolver.forClass(any()).getLocation("id")).thenReturn(repositoryPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveCorrectRepository(@TempDir Path repositoryPath) {
|
||||
RepositoryContext context = resolver.resolve(new String[]{"git", "repo/space/X/something/else"});
|
||||
|
||||
assertThat(context.getRepository()).isSameAs(REPOSITORY);
|
||||
assertThat(context.getDirectory()).isEqualTo(repositoryPath.resolve("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveCorrectRepositoryWithContextPath(@TempDir Path repositoryPath) throws IOException {
|
||||
RepositoryContext context = resolver.resolve(new String[]{"git", "scm/repo/space/X/something/else"});
|
||||
|
||||
assertThat(context.getRepository()).isSameAs(REPOSITORY);
|
||||
assertThat(context.getDirectory()).isEqualTo(repositoryPath.resolve("data"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveCorrectRepository() throws IOException {
|
||||
when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(REPOSITORY);
|
||||
Path repositoryPath = File.createTempFile("test", "scm").toPath();
|
||||
when(locationResolver.forClass(any()).getLocation("id")).thenReturn(repositoryPath);
|
||||
|
||||
RepositoryContext context = resolver.resolve(new String[] {"git", "repo/space/X/something/else"});
|
||||
|
||||
assertThat(context.getRepository()).isSameAs(REPOSITORY);
|
||||
assertThat(context.getDirectory()).isEqualTo(repositoryPath.resolve("data"));
|
||||
void shouldRejectWrongContextPath() {
|
||||
assertThrows(NotFoundException.class,
|
||||
() -> resolver.resolve(new String[]{"git", "vogons/repo/space/X/something/else"}));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user