From adcfb3ee5aa4a08269f7fcea0929b7f133b64595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 4 Mar 2019 14:27:07 +0100 Subject: [PATCH] Do not map not found exception manually --- .../git/GitProtocolModule.java | 15 ++++ .../git/GitRepositoryContextResolver.java | 44 ++++++++++++ .../protocolcommand/git/GitSshProtocol.java | 68 +++++++++++++++++++ .../git/SshReceivePackFactory.java | 44 ++++++++++++ .../git/SshUploadPackFactory.java | 13 ++++ 5 files changed, 184 insertions(+) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitProtocolModule.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitSshProtocol.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshReceivePackFactory.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshUploadPackFactory.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitProtocolModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitProtocolModule.java new file mode 100644 index 0000000000..897f71ad4b --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitProtocolModule.java @@ -0,0 +1,15 @@ +package sonia.scm.protocolcommand.git; + +import com.google.inject.servlet.ServletModule; +import sonia.scm.plugin.Extension; +import sonia.scm.protocolcommand.RepositoryContextResolver; +import sonia.scm.protocolcommand.ScmSshProtocol; + +@Extension +public class GitProtocolModule extends ServletModule { + @Override + protected void configureServlets() { + bind(RepositoryContextResolver.class).to(GitRepositoryContextResolver.class); + bind(ScmSshProtocol.class).to(GitSshProtocol.class); + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java new file mode 100644 index 0000000000..8acfc68dce --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java @@ -0,0 +1,44 @@ +package sonia.scm.protocolcommand.git; + +import com.google.common.base.Splitter; +import sonia.scm.protocolcommand.RepositoryContext; +import sonia.scm.protocolcommand.RepositoryContextResolver; +import sonia.scm.repository.NamespaceAndName; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryLocationResolver; +import sonia.scm.repository.RepositoryManager; + +import javax.inject.Inject; +import java.nio.file.Path; +import java.util.Iterator; + +public class GitRepositoryContextResolver implements RepositoryContextResolver { + + private RepositoryManager repositoryManager; + private RepositoryLocationResolver locationResolver; + + @Inject + public GitRepositoryContextResolver(RepositoryManager repositoryManager, RepositoryLocationResolver locationResolver) { + this.repositoryManager = repositoryManager; + this.locationResolver = locationResolver; + } + + public RepositoryContext resolve(String[] args) { + NamespaceAndName namespaceAndName = extractNamespaceAndName(args); + Repository repository = repositoryManager.get(namespaceAndName); + Path path = locationResolver.getPath(repository.getId()).resolve("data"); + return new RepositoryContext(repository, path); + } + + private NamespaceAndName extractNamespaceAndName(String[] args) { + String path = args[args.length - 1]; + Iterator it = Splitter.on('/').omitEmptyStrings().split(path).iterator(); + String type = it.next(); + if ("repo".equals(type)) { + String ns = it.next(); + String name = it.next(); + return new NamespaceAndName(ns, name); + } + return null; + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitSshProtocol.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitSshProtocol.java new file mode 100644 index 0000000000..c80127f1a7 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitSshProtocol.java @@ -0,0 +1,68 @@ +package sonia.scm.protocolcommand.git; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryCache; +import org.eclipse.jgit.transport.ReceivePack; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.UploadPack; +import org.eclipse.jgit.util.FS; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.protocolcommand.CommandContext; +import sonia.scm.protocolcommand.RepositoryContext; +import sonia.scm.protocolcommand.ScmSshProtocol; +import sonia.scm.repository.RepositoryPermissions; + +import javax.inject.Inject; +import java.io.IOException; + +public class GitSshProtocol implements ScmSshProtocol { + + private static final Logger LOG = LoggerFactory.getLogger(GitSshProtocol.class); + + private SshUploadPackFactory uploadPackFactory; + private SshReceivePackFactory receivePackFactory; + + @Inject + public GitSshProtocol(SshUploadPackFactory uploadPackFactory, SshReceivePackFactory receivePackFactory) { + this.uploadPackFactory = uploadPackFactory; + this.receivePackFactory = receivePackFactory; + } + + @Override + public void handle(CommandContext commandContext, RepositoryContext repositoryContext) throws IOException { + String subCommand = commandContext.getArgs()[0]; + + if (RemoteConfig.DEFAULT_UPLOAD_PACK.equals(subCommand)) { + LOG.trace("got upload pack"); + upload(commandContext, repositoryContext); + } else if (RemoteConfig.DEFAULT_RECEIVE_PACK.equals(subCommand)) { + LOG.trace("got receive pack"); + receive(commandContext, repositoryContext); + } else { + throw new IllegalArgumentException("Unknown git command: " + commandContext.getCommand()); + } + } + + private void receive(CommandContext commandContext, RepositoryContext repositoryContext) throws IOException { + RepositoryPermissions.push(repositoryContext.getRepository()).check(); + try (Repository repository = open(repositoryContext)) { + ReceivePack receivePack = receivePackFactory.create(repositoryContext, repository); + receivePack.receive(commandContext.getInputStream(), commandContext.getOutputStream(), commandContext.getErrorStream()); + } + } + + private void upload(CommandContext commandContext, RepositoryContext repositoryContext) throws IOException { + RepositoryPermissions.pull(repositoryContext.getRepository()).check(); + try (Repository repository = open(repositoryContext)) { + UploadPack uploadPack = uploadPackFactory.create(repositoryContext, repository); + uploadPack.upload(commandContext.getInputStream(), commandContext.getOutputStream(), commandContext.getErrorStream()); + } + } + + private Repository open(RepositoryContext repositoryContext) throws IOException { + RepositoryCache.FileKey key = RepositoryCache.FileKey.lenient(repositoryContext.getDirectory().toFile(), FS.DETECTED); + return key.open(true); + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshReceivePackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshReceivePackFactory.java new file mode 100644 index 0000000000..819b92ca27 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshReceivePackFactory.java @@ -0,0 +1,44 @@ +package sonia.scm.protocolcommand.git; + +import com.google.inject.Inject; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceivePack; +import org.eclipse.jgit.transport.resolver.ReceivePackFactory; +import sonia.scm.protocolcommand.RepositoryContext; +import sonia.scm.repository.GitRepositoryHandler; +import sonia.scm.repository.spi.HookEventFacade; +import sonia.scm.web.CollectingPackParserListener; +import sonia.scm.web.GitReceiveHook; + +/** + * TODO we should have a single/abstract ReceivePackFactory for http and ssh. + */ +public class SshReceivePackFactory implements ReceivePackFactory { + + private final GitRepositoryHandler handler; + private final GitReceiveHook hook; + + @Inject + public SshReceivePackFactory(GitRepositoryHandler handler, HookEventFacade hookEventFacade) { + this.handler = handler; + this.hook = new GitReceiveHook(hookEventFacade, handler); + } + + @Override + public ReceivePack create(RepositoryContext repositoryContext, Repository repository) { + ReceivePack receivePack = new ReceivePack(repository); + receivePack.setAllowNonFastForwards(isNonFastForwardAllowed()); + + receivePack.setPreReceiveHook(hook); + receivePack.setPostReceiveHook(hook); + + // apply collecting listener, to be able to check which commits are new + CollectingPackParserListener.set(receivePack); + + return receivePack; + } + + private boolean isNonFastForwardAllowed() { + return ! handler.getConfig().isNonFastForwardDisallowed(); + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshUploadPackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshUploadPackFactory.java new file mode 100644 index 0000000000..9e8c46cff4 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshUploadPackFactory.java @@ -0,0 +1,13 @@ +package sonia.scm.protocolcommand.git; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.UploadPack; +import org.eclipse.jgit.transport.resolver.UploadPackFactory; +import sonia.scm.protocolcommand.RepositoryContext; + +public class SshUploadPackFactory implements UploadPackFactory { + @Override + public UploadPack create(RepositoryContext repositoryContext, Repository repository) { + return new UploadPack(repository); + } +}