From ffbf9e4fba049e89e55ebc47b8afe514bcf31b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 4 Mar 2019 13:22:40 +0100 Subject: [PATCH 01/11] Add independent classes from ssh plugin --- .../scm/protocolcommand/CommandContext.java | 20 ++++++++++++++++ .../scm/protocolcommand/CommandParser.java | 8 +++++++ .../protocolcommand/RepositoryContext.java | 23 +++++++++++++++++++ .../RepositoryContextResolver.java | 11 +++++++++ .../scm/protocolcommand/ScmSshProtocol.java | 9 ++++++++ 5 files changed, 71 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/CommandContext.java create mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java create mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContext.java create mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java create mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/ScmSshProtocol.java diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandContext.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandContext.java new file mode 100644 index 0000000000..44a1cce95a --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandContext.java @@ -0,0 +1,20 @@ +package sonia.scm.protocolcommand; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.io.InputStream; +import java.io.OutputStream; + +@Getter +@AllArgsConstructor +public class CommandContext { + + private String command; + private String[] args; + + private InputStream inputStream; + private OutputStream outputStream; + private OutputStream errorStream; + +} diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java new file mode 100644 index 0000000000..86fa9554e9 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java @@ -0,0 +1,8 @@ +package sonia.scm.protocolcommand; + +@FunctionalInterface +public interface CommandParser { + + String[] parse(String command); + +} diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContext.java b/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContext.java new file mode 100644 index 0000000000..a64d5a6047 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContext.java @@ -0,0 +1,23 @@ +package sonia.scm.protocolcommand; + +import sonia.scm.repository.Repository; + +import java.nio.file.Path; + +public class RepositoryContext { + private Repository repository; + private Path directory; + + public RepositoryContext(Repository repository, Path directory) { + this.repository = repository; + this.directory = directory; + } + + public Repository getRepository() { + return repository; + } + + public Path getDirectory() { + return directory; + } +} diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java b/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java new file mode 100644 index 0000000000..69d762109d --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java @@ -0,0 +1,11 @@ +package sonia.scm.protocolcommand; + +import sonia.scm.plugin.ExtensionPoint; + +@FunctionalInterface +@ExtensionPoint +public interface RepositoryContextResolver { + + RepositoryContext resolve(String[] args); + +} diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/ScmSshProtocol.java b/scm-core/src/main/java/sonia/scm/protocolcommand/ScmSshProtocol.java new file mode 100644 index 0000000000..0a016c3806 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/ScmSshProtocol.java @@ -0,0 +1,9 @@ +package sonia.scm.protocolcommand; + +import java.io.IOException; + +public interface ScmSshProtocol { + + void handle(CommandContext context, RepositoryContext repositoryContext) throws IOException; + +} 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 02/11] 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); + } +} From 0c46d639da6166835195f1e1061cecbe360116e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 5 Mar 2019 13:29:57 +0100 Subject: [PATCH 03/11] Extract base class for ReceivePackFactory implementations --- .../git/BaseReceivePackFactory.java | 42 +++++++++++++++++++ .../protocolcommand/git/GitSshProtocol.java | 4 ++ .../git/SshReceivePackFactory.java | 31 ++------------ .../sonia/scm/web/GitReceivePackFactory.java | 30 ++++--------- 4 files changed, 57 insertions(+), 50 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/BaseReceivePackFactory.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/BaseReceivePackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/BaseReceivePackFactory.java new file mode 100644 index 0000000000..4fc3a5415c --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/BaseReceivePackFactory.java @@ -0,0 +1,42 @@ +package sonia.scm.protocolcommand.git; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceivePack; +import org.eclipse.jgit.transport.resolver.ReceivePackFactory; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; +import sonia.scm.repository.GitRepositoryHandler; +import sonia.scm.repository.spi.HookEventFacade; +import sonia.scm.web.CollectingPackParserListener; +import sonia.scm.web.GitReceiveHook; + +public abstract class BaseReceivePackFactory implements ReceivePackFactory { + + private final GitRepositoryHandler handler; + private final GitReceiveHook hook; + + protected BaseReceivePackFactory(GitRepositoryHandler handler, HookEventFacade hookEventFacade) { + this.handler = handler; + this.hook = new GitReceiveHook(hookEventFacade, handler); + } + + @Override + public final ReceivePack create(T connection, Repository repository) throws ServiceNotAuthorizedException, ServiceNotEnabledException { + ReceivePack receivePack = createBasicReceivePack(connection, 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; + } + + protected abstract ReceivePack createBasicReceivePack(T request, Repository repository) + throws ServiceNotEnabledException, ServiceNotAuthorizedException; + + private boolean isNonFastForwardAllowed() { + return ! handler.getConfig().isNonFastForwardDisallowed(); + } +} 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 index c80127f1a7..df4ae99553 100644 --- 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 @@ -5,6 +5,8 @@ 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.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.eclipse.jgit.util.FS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +51,8 @@ public class GitSshProtocol implements ScmSshProtocol { try (Repository repository = open(repositoryContext)) { ReceivePack receivePack = receivePackFactory.create(repositoryContext, repository); receivePack.receive(commandContext.getInputStream(), commandContext.getOutputStream(), commandContext.getErrorStream()); + } catch (ServiceNotEnabledException | ServiceNotAuthorizedException e) { + throw new IOException("error creating receive pack for ssh", e); } } 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 index 819b92ca27..18bf1968d5 100644 --- 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 @@ -3,42 +3,19 @@ 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; +public class SshReceivePackFactory extends BaseReceivePackFactory { @Inject public SshReceivePackFactory(GitRepositoryHandler handler, HookEventFacade hookEventFacade) { - this.handler = handler; - this.hook = new GitReceiveHook(hookEventFacade, handler); + super(handler, hookEventFacade); } @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(); + protected ReceivePack createBasicReceivePack(RepositoryContext repositoryContext, Repository repository) { + return new ReceivePack(repository); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java index 5cb8007986..d6e7f398d4 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java @@ -43,6 +43,7 @@ import org.eclipse.jgit.transport.ReceivePack; import org.eclipse.jgit.transport.resolver.ReceivePackFactory; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; +import sonia.scm.protocolcommand.git.BaseReceivePackFactory; import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.spi.HookEventFacade; @@ -56,38 +57,21 @@ import javax.servlet.http.HttpServletRequest; * * @author Sebastian Sdorra */ -public class GitReceivePackFactory implements ReceivePackFactory +public class GitReceivePackFactory extends BaseReceivePackFactory { - private final GitRepositoryHandler handler; - - private ReceivePackFactory wrapped; - - private final GitReceiveHook hook; + private ReceivePackFactory wrapped; @Inject public GitReceivePackFactory(GitRepositoryHandler handler, HookEventFacade hookEventFacade) { - this.handler = handler; - this.hook = new GitReceiveHook(hookEventFacade, handler); - this.wrapped = new DefaultReceivePackFactory(); + super(handler, hookEventFacade); + this.wrapped = new DefaultReceivePackFactory(); } @Override - public ReceivePack create(HttpServletRequest request, Repository repository) + protected ReceivePack createBasicReceivePack(HttpServletRequest request, Repository repository) throws ServiceNotEnabledException, ServiceNotAuthorizedException { - ReceivePack receivePack = wrapped.create(request, 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(); + return wrapped.create(request, repository); } @VisibleForTesting From ebb21ee3081a3fac3e2d3f5d7ed6bdb36623675e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 5 Mar 2019 13:47:46 +0100 Subject: [PATCH 04/11] Modify test to test base, not specific class This was necessary after extracting base class. --- .../sonia/scm/web/GitReceivePackFactory.java | 6 ---- .../git/BaseReceivePackFactoryTest.java} | 29 +++++++++++-------- 2 files changed, 17 insertions(+), 18 deletions(-) rename scm-plugins/scm-git-plugin/src/test/java/sonia/scm/{web/GitReceivePackFactoryTest.java => protocolcommand/git/BaseReceivePackFactoryTest.java} (80%) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java index d6e7f398d4..b59fa7526b 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java @@ -35,7 +35,6 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.annotations.VisibleForTesting; import com.google.inject.Inject; import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory; import org.eclipse.jgit.lib.Repository; @@ -73,9 +72,4 @@ public class GitReceivePackFactory extends BaseReceivePackFactory wrappedReceivePackFactory; - private GitReceivePackFactory factory; + private BaseReceivePackFactory factory; - @Mock - private HttpServletRequest request; + private Object request = new Object(); private Repository repository; @@ -89,8 +89,12 @@ public class GitReceivePackFactoryTest { ReceivePack receivePack = new ReceivePack(repository); when(wrappedReceivePackFactory.create(request, repository)).thenReturn(receivePack); - factory = new GitReceivePackFactory(handler, null); - factory.setWrapped(wrappedReceivePackFactory); + factory = new BaseReceivePackFactory(handler, null) { + @Override + protected ReceivePack createBasicReceivePack(Object request, Repository repository) throws ServiceNotEnabledException, ServiceNotAuthorizedException { + return wrappedReceivePackFactory.create(request, repository); + } + }; } private Repository createRepositoryForTesting() throws GitAPIException, IOException { @@ -105,6 +109,7 @@ public class GitReceivePackFactoryTest { assertThat(receivePack.getPreReceiveHook(), instanceOf(GitReceiveHook.class)); assertThat(receivePack.getPostReceiveHook(), instanceOf(GitReceiveHook.class)); assertTrue(receivePack.isAllowNonFastForwards()); + verify(wrappedReceivePackFactory).create(request, repository); } @Test From dd71fb4c3b46a35dcf3f8a0ff7a239d87fd4afb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 5 Mar 2019 14:20:31 +0100 Subject: [PATCH 05/11] Expose ScmCommandProtocol as extension point This is necessary so that multiple providers (git, hg, ...) can implement this. The using class has to find the matching implementation. --- ...mSshProtocol.java => ScmCommandProtocol.java} | 7 ++++++- ...tSshProtocol.java => GitCommandProtocol.java} | 16 ++++++++++++---- .../protocolcommand/git/GitProtocolModule.java | 2 -- 3 files changed, 18 insertions(+), 7 deletions(-) rename scm-core/src/main/java/sonia/scm/protocolcommand/{ScmSshProtocol.java => ScmCommandProtocol.java} (51%) rename scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/{GitSshProtocol.java => GitCommandProtocol.java} (82%) diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/ScmSshProtocol.java b/scm-core/src/main/java/sonia/scm/protocolcommand/ScmCommandProtocol.java similarity index 51% rename from scm-core/src/main/java/sonia/scm/protocolcommand/ScmSshProtocol.java rename to scm-core/src/main/java/sonia/scm/protocolcommand/ScmCommandProtocol.java index 0a016c3806..8def99c057 100644 --- a/scm-core/src/main/java/sonia/scm/protocolcommand/ScmSshProtocol.java +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/ScmCommandProtocol.java @@ -1,8 +1,13 @@ package sonia.scm.protocolcommand; +import sonia.scm.plugin.ExtensionPoint; + import java.io.IOException; -public interface ScmSshProtocol { +@ExtensionPoint +public interface ScmCommandProtocol { + + boolean canHandle(RepositoryContext repositoryContext); void handle(CommandContext context, RepositoryContext repositoryContext) throws IOException; 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/GitCommandProtocol.java similarity index 82% rename from scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitSshProtocol.java rename to scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java index df4ae99553..02e75c2015 100644 --- 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/GitCommandProtocol.java @@ -10,27 +10,35 @@ import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.eclipse.jgit.util.FS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.plugin.Extension; import sonia.scm.protocolcommand.CommandContext; import sonia.scm.protocolcommand.RepositoryContext; -import sonia.scm.protocolcommand.ScmSshProtocol; +import sonia.scm.protocolcommand.ScmCommandProtocol; +import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.RepositoryPermissions; import javax.inject.Inject; import java.io.IOException; -public class GitSshProtocol implements ScmSshProtocol { +@Extension +public class GitCommandProtocol implements ScmCommandProtocol { - private static final Logger LOG = LoggerFactory.getLogger(GitSshProtocol.class); + private static final Logger LOG = LoggerFactory.getLogger(GitCommandProtocol.class); private SshUploadPackFactory uploadPackFactory; private SshReceivePackFactory receivePackFactory; @Inject - public GitSshProtocol(SshUploadPackFactory uploadPackFactory, SshReceivePackFactory receivePackFactory) { + public GitCommandProtocol(SshUploadPackFactory uploadPackFactory, SshReceivePackFactory receivePackFactory) { this.uploadPackFactory = uploadPackFactory; this.receivePackFactory = receivePackFactory; } + @Override + public boolean canHandle(RepositoryContext repositoryContext) { + return GitRepositoryHandler.TYPE_NAME.equals(repositoryContext.getRepository().getType()); + } + @Override public void handle(CommandContext commandContext, RepositoryContext repositoryContext) throws IOException { String subCommand = commandContext.getArgs()[0]; 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 index 897f71ad4b..8301852408 100644 --- 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 @@ -3,13 +3,11 @@ 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); } } From a16bb0d0af26dfc34258db9288b33546b78fdc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 5 Mar 2019 15:01:57 +0100 Subject: [PATCH 06/11] Wrap parser and handler into interpreter --- .../scm/protocolcommand/CommandInterpreter.java | 13 +++++++++++++ .../sonia/scm/protocolcommand/CommandParser.java | 1 - .../scm/protocolcommand/ScmCommandProtocol.java | 5 ----- .../scm/protocolcommand/git/GitCommandProtocol.java | 6 ------ 4 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java new file mode 100644 index 0000000000..1e04eb3b66 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java @@ -0,0 +1,13 @@ +package sonia.scm.protocolcommand; + +import sonia.scm.plugin.ExtensionPoint; + +@ExtensionPoint +public interface CommandInterpreter { + + boolean canHandle(String command); + + CommandParser getParser(); + + ScmCommandProtocol getProtocolHandler(); +} diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java index 86fa9554e9..1d1f0c9b34 100644 --- a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java @@ -1,6 +1,5 @@ package sonia.scm.protocolcommand; -@FunctionalInterface public interface CommandParser { String[] parse(String command); diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/ScmCommandProtocol.java b/scm-core/src/main/java/sonia/scm/protocolcommand/ScmCommandProtocol.java index 8def99c057..e7dbf65cad 100644 --- a/scm-core/src/main/java/sonia/scm/protocolcommand/ScmCommandProtocol.java +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/ScmCommandProtocol.java @@ -1,14 +1,9 @@ package sonia.scm.protocolcommand; -import sonia.scm.plugin.ExtensionPoint; - import java.io.IOException; -@ExtensionPoint public interface ScmCommandProtocol { - boolean canHandle(RepositoryContext repositoryContext); - void handle(CommandContext context, RepositoryContext repositoryContext) throws IOException; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java index 02e75c2015..b81e00a13c 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java @@ -14,7 +14,6 @@ import sonia.scm.plugin.Extension; import sonia.scm.protocolcommand.CommandContext; import sonia.scm.protocolcommand.RepositoryContext; import sonia.scm.protocolcommand.ScmCommandProtocol; -import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.RepositoryPermissions; import javax.inject.Inject; @@ -34,11 +33,6 @@ public class GitCommandProtocol implements ScmCommandProtocol { this.receivePackFactory = receivePackFactory; } - @Override - public boolean canHandle(RepositoryContext repositoryContext) { - return GitRepositoryHandler.TYPE_NAME.equals(repositoryContext.getRepository().getType()); - } - @Override public void handle(CommandContext commandContext, RepositoryContext repositoryContext) throws IOException { String subCommand = commandContext.getArgs()[0]; From a1e43ee571b7100e1ebcc304f79ef125924c9205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 5 Mar 2019 17:57:38 +0100 Subject: [PATCH 07/11] Simplify interpreter --- .../sonia/scm/protocolcommand/CommandInterpreter.java | 7 +------ .../scm/protocolcommand/CommandInterpreterFactory.java | 10 ++++++++++ .../java/sonia/scm/protocolcommand/CommandParser.java | 7 ------- 3 files changed, 11 insertions(+), 13 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreterFactory.java delete mode 100644 scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java index 1e04eb3b66..e78b2b011c 100644 --- a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java @@ -1,13 +1,8 @@ package sonia.scm.protocolcommand; -import sonia.scm.plugin.ExtensionPoint; - -@ExtensionPoint public interface CommandInterpreter { - boolean canHandle(String command); - - CommandParser getParser(); + String[] getParsedArgs(); ScmCommandProtocol getProtocolHandler(); } diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreterFactory.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreterFactory.java new file mode 100644 index 0000000000..9d6bfa1d7f --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreterFactory.java @@ -0,0 +1,10 @@ +package sonia.scm.protocolcommand; + +import sonia.scm.plugin.ExtensionPoint; + +import java.util.Optional; + +@ExtensionPoint +public interface CommandInterpreterFactory { + Optional canHandle(String command); +} diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java deleted file mode 100644 index 1d1f0c9b34..0000000000 --- a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandParser.java +++ /dev/null @@ -1,7 +0,0 @@ -package sonia.scm.protocolcommand; - -public interface CommandParser { - - String[] parse(String command); - -} From e6894490e537f4784592d564a406f237af6cdce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 6 Mar 2019 15:34:51 +0100 Subject: [PATCH 08/11] Remove ssh from class names The classes do not have anything to do with ssh. --- .../sonia/scm/protocolcommand/git/GitCommandProtocol.java | 6 +++--- ...shReceivePackFactory.java => ScmReceivePackFactory.java} | 4 ++-- ...{SshUploadPackFactory.java => ScmUploadPackFactory.java} | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/{SshReceivePackFactory.java => ScmReceivePackFactory.java} (82%) rename scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/{SshUploadPackFactory.java => ScmUploadPackFactory.java} (86%) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java index b81e00a13c..b11ea80cfe 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandProtocol.java @@ -24,11 +24,11 @@ public class GitCommandProtocol implements ScmCommandProtocol { private static final Logger LOG = LoggerFactory.getLogger(GitCommandProtocol.class); - private SshUploadPackFactory uploadPackFactory; - private SshReceivePackFactory receivePackFactory; + private ScmUploadPackFactory uploadPackFactory; + private ScmReceivePackFactory receivePackFactory; @Inject - public GitCommandProtocol(SshUploadPackFactory uploadPackFactory, SshReceivePackFactory receivePackFactory) { + public GitCommandProtocol(ScmUploadPackFactory uploadPackFactory, ScmReceivePackFactory receivePackFactory) { this.uploadPackFactory = uploadPackFactory; this.receivePackFactory = receivePackFactory; } 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/ScmReceivePackFactory.java similarity index 82% rename from scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshReceivePackFactory.java rename to scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmReceivePackFactory.java index 18bf1968d5..105a04ccdb 100644 --- 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/ScmReceivePackFactory.java @@ -7,10 +7,10 @@ import sonia.scm.protocolcommand.RepositoryContext; import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.spi.HookEventFacade; -public class SshReceivePackFactory extends BaseReceivePackFactory { +public class ScmReceivePackFactory extends BaseReceivePackFactory { @Inject - public SshReceivePackFactory(GitRepositoryHandler handler, HookEventFacade hookEventFacade) { + public ScmReceivePackFactory(GitRepositoryHandler handler, HookEventFacade hookEventFacade) { super(handler, hookEventFacade); } 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/ScmUploadPackFactory.java similarity index 86% rename from scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/SshUploadPackFactory.java rename to scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/ScmUploadPackFactory.java index 9e8c46cff4..962437a59b 100644 --- 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/ScmUploadPackFactory.java @@ -5,7 +5,7 @@ import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.resolver.UploadPackFactory; import sonia.scm.protocolcommand.RepositoryContext; -public class SshUploadPackFactory implements UploadPackFactory { +public class ScmUploadPackFactory implements UploadPackFactory { @Override public UploadPack create(RepositoryContext repositoryContext, Repository repository) { return new UploadPack(repository); From 7adbc4df518f6d16c45bf238e292c55360bf4905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 6 Mar 2019 16:01:15 +0100 Subject: [PATCH 09/11] Add some unit test --- .../git/GitRepositoryContextResolverTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java new file mode 100644 index 0000000000..6ac4cdb54b --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java @@ -0,0 +1,45 @@ +package sonia.scm.protocolcommand.git; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +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.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class GitRepositoryContextResolverTest { + + private static final Repository REPOSITORY = new Repository("id", "git", "space", "X"); + + @Mock + RepositoryManager repositoryManager; + @Mock + RepositoryLocationResolver locationResolver; + + @InjectMocks + GitRepositoryContextResolver resolver; + + @Test + void shouldResolveCorrectRepository() throws IOException { + when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(REPOSITORY); + Path repositoryPath = File.createTempFile("test", "scm").toPath(); + when(locationResolver.getPath("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")); + } +} From 77d79610d2fed5ab061af82eac7e4b4b3eda19fb Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 7 Mar 2019 10:34:13 +0100 Subject: [PATCH 10/11] moves more git specific classes from ssh-plugin to git-plugin --- .../protocolcommand/CommandInterpreter.java | 2 + .../RepositoryContextResolver.java | 3 - .../git/GitCommandInterpreter.java | 32 +++++++ .../git/GitCommandInterpreterFactory.java | 37 ++++++++ .../protocolcommand/git/GitCommandParser.java | 88 +++++++++++++++++++ .../git/GitProtocolModule.java | 13 --- 6 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreter.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreterFactory.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandParser.java delete mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitProtocolModule.java diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java index e78b2b011c..24bf7d30fa 100644 --- a/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/CommandInterpreter.java @@ -5,4 +5,6 @@ public interface CommandInterpreter { String[] getParsedArgs(); ScmCommandProtocol getProtocolHandler(); + + RepositoryContextResolver getRepositoryContextResolver(); } diff --git a/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java b/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java index 69d762109d..2c48ece185 100644 --- a/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java +++ b/scm-core/src/main/java/sonia/scm/protocolcommand/RepositoryContextResolver.java @@ -1,9 +1,6 @@ package sonia.scm.protocolcommand; -import sonia.scm.plugin.ExtensionPoint; - @FunctionalInterface -@ExtensionPoint public interface RepositoryContextResolver { RepositoryContext resolve(String[] args); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreter.java new file mode 100644 index 0000000000..d4e6f8edbb --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreter.java @@ -0,0 +1,32 @@ +package sonia.scm.protocolcommand.git; + +import sonia.scm.protocolcommand.CommandInterpreter; +import sonia.scm.protocolcommand.RepositoryContextResolver; +import sonia.scm.protocolcommand.ScmCommandProtocol; + +class GitCommandInterpreter implements CommandInterpreter { + private final GitRepositoryContextResolver gitRepositoryContextResolver; + private final GitCommandProtocol gitCommandProtocol; + private final String[] args; + + GitCommandInterpreter(GitRepositoryContextResolver gitRepositoryContextResolver, GitCommandProtocol gitCommandProtocol, String[] args) { + this.gitRepositoryContextResolver = gitRepositoryContextResolver; + this.gitCommandProtocol = gitCommandProtocol; + this.args = args; + } + + @Override + public String[] getParsedArgs() { + return args; + } + + @Override + public ScmCommandProtocol getProtocolHandler() { + return gitCommandProtocol; + } + + @Override + public RepositoryContextResolver getRepositoryContextResolver() { + return gitRepositoryContextResolver; + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreterFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreterFactory.java new file mode 100644 index 0000000000..bd38dc01db --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandInterpreterFactory.java @@ -0,0 +1,37 @@ +package sonia.scm.protocolcommand.git; + +import sonia.scm.plugin.Extension; +import sonia.scm.protocolcommand.CommandInterpreter; +import sonia.scm.protocolcommand.CommandInterpreterFactory; + +import javax.inject.Inject; +import java.util.Optional; + +import static java.util.Optional.empty; +import static java.util.Optional.of; + +@Extension +public class GitCommandInterpreterFactory implements CommandInterpreterFactory { + private final GitCommandProtocol gitCommandProtocol; + private final GitRepositoryContextResolver gitRepositoryContextResolver; + + @Inject + public GitCommandInterpreterFactory(GitCommandProtocol gitCommandProtocol, GitRepositoryContextResolver gitRepositoryContextResolver) { + this.gitCommandProtocol = gitCommandProtocol; + this.gitRepositoryContextResolver = gitRepositoryContextResolver; + } + + @Override + public Optional canHandle(String command) { + try { + String[] args = GitCommandParser.parse(command); + if (args[0].startsWith("git")) { + return of(new GitCommandInterpreter(gitRepositoryContextResolver, gitCommandProtocol, args)); + } else { + return empty(); + } + } catch (IllegalArgumentException e) { + return empty(); + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandParser.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandParser.java new file mode 100644 index 0000000000..624b951d42 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitCommandParser.java @@ -0,0 +1,88 @@ +package sonia.scm.protocolcommand.git; + +import java.util.ArrayList; +import java.util.List; + +class GitCommandParser { + + private GitCommandParser() { + } + + static String[] parse(String command) { + List strs = parseDelimitedString(command, " ", true); + String[] args = strs.toArray(new String[strs.size()]); + for (int i = 0; i < args.length; i++) { + String argVal = args[i]; + if (argVal.startsWith("'") && argVal.endsWith("'")) { + args[i] = argVal.substring(1, argVal.length() - 1); + argVal = args[i]; + } + if (argVal.startsWith("\"") && argVal.endsWith("\"")) { + args[i] = argVal.substring(1, argVal.length() - 1); + } + } + + if (args.length != 2) { + throw new IllegalArgumentException("Invalid git command line (no arguments): " + command); + } + return args; + } + + private static List parseDelimitedString(String value, String delim, boolean trim) { + if (value == null) { + value = ""; + } + + List list = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + int expecting = 7; + boolean isEscaped = false; + + for(int i = 0; i < value.length(); ++i) { + char c = value.charAt(i); + boolean isDelimiter = delim.indexOf(c) >= 0; + if (!isEscaped && c == '\\') { + isEscaped = true; + } else { + if (isEscaped) { + sb.append(c); + } else if (isDelimiter && (expecting & 2) != 0) { + if (trim) { + String str = sb.toString(); + list.add(str.trim()); + } else { + list.add(sb.toString()); + } + + sb.delete(0, sb.length()); + expecting = 7; + } else if (c == '"' && (expecting & 4) != 0) { + sb.append(c); + expecting = 9; + } else if (c == '"' && (expecting & 8) != 0) { + sb.append(c); + expecting = 7; + } else { + if ((expecting & 1) == 0) { + throw new IllegalArgumentException("Invalid delimited string: " + value); + } + + sb.append(c); + } + + isEscaped = false; + } + } + + if (sb.length() > 0) { + if (trim) { + String str = sb.toString(); + list.add(str.trim()); + } else { + list.add(sb.toString()); + } + } + + return list; + } +} 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 deleted file mode 100644 index 8301852408..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitProtocolModule.java +++ /dev/null @@ -1,13 +0,0 @@ -package sonia.scm.protocolcommand.git; - -import com.google.inject.servlet.ServletModule; -import sonia.scm.plugin.Extension; -import sonia.scm.protocolcommand.RepositoryContextResolver; - -@Extension -public class GitProtocolModule extends ServletModule { - @Override - protected void configureServlets() { - bind(RepositoryContextResolver.class).to(GitRepositoryContextResolver.class); - } -} From 9be12d8ac851324da750ab4b26943f2dcf5aee49 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 7 Mar 2019 10:24:58 +0000 Subject: [PATCH 11/11] Close branch feature/changes_for_ssh_plugin