summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/gitblit
diff options
context:
space:
mode:
authorDavid Ostrovsky <david@ostrovsky.org>2014-02-22 21:17:03 +0100
committerJames Moger <james.moger@gitblit.com>2014-04-10 18:58:07 -0400
commite3b636e7fa2a823cfe90ea75e88034a60f7e59e6 (patch)
tree8341fe1dba50ec2ed4bb780c1944f91ac52d7528 /src/main/java/com/gitblit
parenta3de33e71a22268105714e01d09c1c2e28bfe2c3 (diff)
downloadgitblit-e3b636e7fa2a823cfe90ea75e88034a60f7e59e6.tar.gz
gitblit-e3b636e7fa2a823cfe90ea75e88034a60f7e59e6.zip
SSHD: Add support for git pack commands
Add git-upload-pack and git-receive-pack commands. Conflicts: src/main/java/com/gitblit/manager/ServicesManager.java src/main/java/com/gitblit/transport/ssh/CommandDispatcher.java src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java Change-Id: I8c057b41f1dfad6d004e6aa91f96c8c673be9be2
Diffstat (limited to 'src/main/java/com/gitblit')
-rw-r--r--src/main/java/com/gitblit/Constants.java2
-rw-r--r--src/main/java/com/gitblit/git/GitblitReceivePackFactory.java8
-rw-r--r--src/main/java/com/gitblit/git/RepositoryResolver.java10
-rw-r--r--src/main/java/com/gitblit/manager/AuthenticationManager.java29
-rw-r--r--src/main/java/com/gitblit/manager/GitblitManager.java7
-rw-r--r--src/main/java/com/gitblit/manager/IAuthenticationManager.java3
-rw-r--r--src/main/java/com/gitblit/manager/ServicesManager.java1
-rw-r--r--src/main/java/com/gitblit/transport/ssh/AbstractGitCommand.java108
-rw-r--r--src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java147
-rw-r--r--src/main/java/com/gitblit/transport/ssh/SshContext.java35
-rw-r--r--src/main/java/com/gitblit/transport/ssh/SshDaemon.java24
-rw-r--r--src/main/java/com/gitblit/transport/ssh/SshSession.java9
-rw-r--r--src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java38
-rw-r--r--src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java48
-rw-r--r--src/main/java/com/gitblit/transport/ssh/commands/Receive.java34
-rw-r--r--src/main/java/com/gitblit/transport/ssh/commands/Upload.java39
-rw-r--r--src/main/java/com/gitblit/transport/ssh/commands/VersionCommand.java2
17 files changed, 379 insertions, 165 deletions
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java
index 2a98b53f..889e5a30 100644
--- a/src/main/java/com/gitblit/Constants.java
+++ b/src/main/java/com/gitblit/Constants.java
@@ -501,7 +501,7 @@ public class Constants {
}
public static enum AuthenticationType {
- CREDENTIALS, COOKIE, CERTIFICATE, CONTAINER;
+ SSH, CREDENTIALS, COOKIE, CERTIFICATE, CONTAINER;
public boolean isStandard() {
return ordinal() <= COOKIE.ordinal();
diff --git a/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java b/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java
index 7976fe56..9911258c 100644
--- a/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java
+++ b/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java
@@ -31,6 +31,7 @@ import com.gitblit.Keys;
import com.gitblit.manager.IGitblit;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.SshSession;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
@@ -88,6 +89,13 @@ public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> {
// set timeout from Git daemon
timeout = client.getDaemon().getTimeout();
+ } else if (req instanceof SshSession) {
+ // SSH request is always authenticated
+ SshSession s = (SshSession) req;
+ repositoryName = s.getRepositoryName();
+ origin = s.getRemoteAddress().toString();
+ String username = s.getRemoteUser();
+ user = gitblit.getUserModel(username);
}
boolean allowAnonymousPushes = settings.getBoolean(Keys.git.allowAnonymousPushes, false);
diff --git a/src/main/java/com/gitblit/git/RepositoryResolver.java b/src/main/java/com/gitblit/git/RepositoryResolver.java
index 208c1ae1..c859f6f6 100644
--- a/src/main/java/com/gitblit/git/RepositoryResolver.java
+++ b/src/main/java/com/gitblit/git/RepositoryResolver.java
@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
import com.gitblit.manager.IGitblit;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.SshSession;
/**
* Resolves repositories and grants export access.
@@ -67,6 +68,9 @@ public class RepositoryResolver<X> extends FileResolver<X> {
// git request
GitDaemonClient client = (GitDaemonClient) req;
client.setRepositoryName(name);
+ } else if (req instanceof SshSession) {
+ SshSession s = (SshSession)req;
+ s.setRepositoryName(name);
}
return repo;
}
@@ -98,6 +102,12 @@ public class RepositoryResolver<X> extends FileResolver<X> {
if (user == null) {
user = UserModel.ANONYMOUS;
}
+ } else if (req instanceof SshSession) {
+ SshSession s = (SshSession) req;
+ user = gitblit.authenticate(s);
+ if (user == null) {
+ throw new IOException(String.format("User %s not found", s.getRemoteUser()));
+ }
}
if (user.canClone(model)) {
diff --git a/src/main/java/com/gitblit/manager/AuthenticationManager.java b/src/main/java/com/gitblit/manager/AuthenticationManager.java
index 4f3e652c..47425ce7 100644
--- a/src/main/java/com/gitblit/manager/AuthenticationManager.java
+++ b/src/main/java/com/gitblit/manager/AuthenticationManager.java
@@ -47,6 +47,7 @@ import com.gitblit.auth.SalesforceAuthProvider;
import com.gitblit.auth.WindowsAuthProvider;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.SshSession;
import com.gitblit.utils.Base64;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
@@ -290,6 +291,34 @@ public class AuthenticationManager implements IAuthenticationManager {
}
/**
+ * Authenticate a user based on SSH session.
+ *
+ * @param SshSession
+ * @return a user object or null
+ */
+ @Override
+ public UserModel authenticate(SshSession sshSession) {
+ String username = sshSession.getRemoteUser();
+ if (username != null) {
+ if (!StringUtils.isEmpty(username)) {
+ UserModel user = userManager.getUserModel(username);
+ if (user != null) {
+ // existing user
+ logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}",
+ user.username, sshSession.getRemoteAddress()));
+ return validateAuthentication(user, AuthenticationType.SSH);
+ }
+ logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted ssh authentication from {1}",
+ username, sshSession.getRemoteAddress()));
+ }
+ } else {
+ logger.warn("Empty user in SSH session");
+ }
+ return null;
+ }
+
+
+ /**
* This method allows the authentication manager to reject authentication
* attempts. It is called after the username/secret have been verified to
* ensure that the authentication technique has been logged.
diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java
index b6c2b474..a5a26379 100644
--- a/src/main/java/com/gitblit/manager/GitblitManager.java
+++ b/src/main/java/com/gitblit/manager/GitblitManager.java
@@ -68,6 +68,7 @@ import com.gitblit.models.SettingModel;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.tickets.ITicketService;
+import com.gitblit.transport.ssh.SshSession;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.JsonUtils;
@@ -651,6 +652,12 @@ public class GitblitManager implements IGitblit {
}
return user;
}
+
+ @Override
+ public UserModel authenticate(SshSession sshSession) {
+ return authenticationManager.authenticate(sshSession);
+ }
+
@Override
public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
UserModel user = authenticationManager.authenticate(httpRequest, requiresCertificate);
diff --git a/src/main/java/com/gitblit/manager/IAuthenticationManager.java b/src/main/java/com/gitblit/manager/IAuthenticationManager.java
index 3007a303..5d98d127 100644
--- a/src/main/java/com/gitblit/manager/IAuthenticationManager.java
+++ b/src/main/java/com/gitblit/manager/IAuthenticationManager.java
@@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletResponse;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.SshSession;
public interface IAuthenticationManager extends IManager {
@@ -33,6 +34,8 @@ public interface IAuthenticationManager extends IManager {
*/
UserModel authenticate(HttpServletRequest httpRequest);
+ public UserModel authenticate(SshSession sshSession);
+
/**
* Authenticate a user based on HTTP request parameters.
*
diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java
index df8918ed..11083be3 100644
--- a/src/main/java/com/gitblit/manager/ServicesManager.java
+++ b/src/main/java/com/gitblit/manager/ServicesManager.java
@@ -247,6 +247,5 @@ public class ServicesManager implements IManager {
"Next pull of {0} @ {1} scheduled for {2,date,yyyy-MM-dd HH:mm}",
registration.name, registration.url, registration.nextPull));
}
-
}
}
diff --git a/src/main/java/com/gitblit/transport/ssh/AbstractGitCommand.java b/src/main/java/com/gitblit/transport/ssh/AbstractGitCommand.java
new file mode 100644
index 00000000..bba6a402
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/AbstractGitCommand.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.transport.ssh;
+
+import java.io.IOException;
+
+import org.apache.sshd.server.Environment;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+import org.kohsuke.args4j.Argument;
+
+import com.gitblit.git.GitblitReceivePackFactory;
+import com.gitblit.git.GitblitUploadPackFactory;
+import com.gitblit.git.RepositoryResolver;
+import com.gitblit.transport.ssh.commands.BaseCommand;
+
+/**
+ * @author Eric Myhre
+ *
+ */
+public abstract class AbstractGitCommand extends BaseCommand {
+ @Argument(index = 0, metaVar = "PROJECT.git", required = true, usage = "project name")
+ protected String repository;
+
+ protected RepositoryResolver<SshSession> repositoryResolver;
+ protected ReceivePackFactory<SshSession> receivePackFactory;
+ protected UploadPackFactory<SshSession> uploadPackFactory;
+
+ protected Repository repo;
+
+ @Override
+ public void start(final Environment env) {
+ startThread(new RepositoryCommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ AbstractGitCommand.this.service();
+ }
+
+ @Override
+ public String getRepository() {
+ return repository;
+ }
+ });
+ }
+
+ private void service() throws IOException, Failure {
+ try {
+ repo = openRepository();
+ runImpl();
+ } finally {
+ if (repo != null) {
+ repo.close();
+ }
+ }
+ }
+
+ protected abstract void runImpl() throws IOException, Failure;
+
+ protected Repository openRepository() throws Failure {
+ // Assume any attempt to use \ was by a Windows client
+ // and correct to the more typical / used in Git URIs.
+ //
+ repository = repository.replace('\\', '/');
+ // ssh://git@thishost/path should always be name="/path" here
+ //
+ if (!repository.startsWith("/")) {
+ throw new Failure(1, "fatal: '" + repository
+ + "': not starts with / character");
+ }
+ repository = repository.substring(1);
+ try {
+ return repositoryResolver.open(ctx.getSession(), repository);
+ } catch (Exception e) {
+ throw new Failure(1, "fatal: '" + repository
+ + "': not a git archive", e);
+ }
+ }
+
+ public void setRepositoryResolver(
+ RepositoryResolver<SshSession> repositoryResolver) {
+ this.repositoryResolver = repositoryResolver;
+ }
+
+ public void setReceivePackFactory(
+ GitblitReceivePackFactory<SshSession> receivePackFactory) {
+ this.receivePackFactory = receivePackFactory;
+ }
+
+ public void setUploadPackFactory(
+ GitblitUploadPackFactory<SshSession> uploadPackFactory) {
+ this.uploadPackFactory = uploadPackFactory;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java b/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java
index 056938e9..0c8492f7 100644
--- a/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java
+++ b/src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java
@@ -31,20 +31,9 @@ import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.SessionAware;
import org.apache.sshd.server.session.ServerSession;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.PacketLineOut;
-import org.eclipse.jgit.transport.ReceivePack;
-import org.eclipse.jgit.transport.ServiceMayNotContinueException;
-import org.eclipse.jgit.transport.UploadPack;
-import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
-import org.eclipse.jgit.transport.resolver.UploadPackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.gitblit.git.RepositoryResolver;
import com.gitblit.transport.ssh.commands.DispatchCommand;
import com.gitblit.utils.WorkQueue;
import com.google.common.util.concurrent.Atomics;
@@ -57,23 +46,13 @@ import com.google.common.util.concurrent.Atomics;
public class SshCommandFactory implements CommandFactory {
private static final Logger logger = LoggerFactory
.getLogger(SshCommandFactory.class);
- private RepositoryResolver<SshSession> repositoryResolver;
-
- private UploadPackFactory<SshSession> uploadPackFactory;
-
- private ReceivePackFactory<SshSession> receivePackFactory;
private final ScheduledExecutorService startExecutor;
private DispatchCommand dispatcher;
- public SshCommandFactory(RepositoryResolver<SshSession> repositoryResolver,
- UploadPackFactory<SshSession> uploadPackFactory,
- ReceivePackFactory<SshSession> receivePackFactory,
+ public SshCommandFactory(
WorkQueue workQueue,
DispatchCommand d) {
- this.repositoryResolver = repositoryResolver;
- this.uploadPackFactory = uploadPackFactory;
- this.receivePackFactory = receivePackFactory;
this.dispatcher = d;
int threads = 2;//cfg.getInt("sshd","commandStartThreads", 2);
startExecutor = workQueue.createQueue(threads, "SshCommandStart");
@@ -82,35 +61,34 @@ public class SshCommandFactory implements CommandFactory {
@Override
public Command createCommand(final String commandLine) {
return new Trampoline(commandLine);
- /*
- if ("git-upload-pack".equals(command))
- return new UploadPackCommand(argument);
- if ("git-receive-pack".equals(command))
- return new ReceivePackCommand(argument);
- return new NonCommand();
- */
}
private class Trampoline implements Command, SessionAware {
private final String[] argv;
+ private ServerSession session;
private InputStream in;
private OutputStream out;
private OutputStream err;
private ExitCallback exit;
private Environment env;
+ private String cmdLine;
private DispatchCommand cmd;
private final AtomicBoolean logged;
private final AtomicReference<Future<?>> task;
- Trampoline(final String cmdLine) {
- argv = split(cmdLine);
+ Trampoline(String line) {
+ if (line.startsWith("git-")) {
+ line = "git " + line;
+ }
+ cmdLine = line;
+ argv = split(line);
logged = new AtomicBoolean();
task = Atomics.newReference();
}
@Override
public void setSession(ServerSession session) {
- // TODO Auto-generated method stub
+ this.session = session;
}
@Override
@@ -148,18 +126,18 @@ public class SshCommandFactory implements CommandFactory {
@Override
public String toString() {
- //return "start (user " + ctx.getSession().getUsername() + ")";
- return "start (user TODO)";
+ return "start (user " + session.getUsername() + ")";
}
}));
}
private void onStart() throws IOException {
synchronized (this) {
- //final Context old = sshScope.set(ctx);
+ SshContext ctx = new SshContext(session.getAttribute(SshSession.KEY), cmdLine);
try {
cmd = dispatcher;
cmd.setArguments(argv);
+ cmd.setContext(ctx);
cmd.setInputStream(in);
cmd.setOutputStream(out);
cmd.setErrorStream(err);
@@ -178,7 +156,7 @@ public class SshCommandFactory implements CommandFactory {
});
cmd.start(env);
} finally {
- //sshScope.set(old);
+ ctx = null;
}
}
}
@@ -286,101 +264,4 @@ public class SshCommandFactory implements CommandFactory {
}
return list.toArray(new String[list.size()]);
}
-
- public abstract class RepositoryCommand extends AbstractSshCommand {
- protected final String repositoryName;
-
- public RepositoryCommand(String repositoryName) {
- this.repositoryName = repositoryName;
- }
-
- @Override
- public void start(Environment env) throws IOException {
- Repository db = null;
- try {
- SshSession client = session.getAttribute(SshSession.KEY);
- db = selectRepository(client, repositoryName);
- if (db == null) return;
- run(client, db);
- exit.onExit(0);
- } catch (ServiceNotEnabledException e) {
- // Ignored. Client cannot use this repository.
- } catch (ServiceNotAuthorizedException e) {
- // Ignored. Client cannot use this repository.
- } finally {
- if (db != null)
- db.close();
- exit.onExit(1);
- }
- }
-
- protected Repository selectRepository(SshSession client, String name) throws IOException {
- try {
- return openRepository(client, name);
- } catch (ServiceMayNotContinueException e) {
- // An error when opening the repo means the client is expecting a ref
- // advertisement, so use that style of error.
- PacketLineOut pktOut = new PacketLineOut(out);
- pktOut.writeString("ERR " + e.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
- return null;
- }
- }
-
- protected Repository openRepository(SshSession client, String name)
- throws ServiceMayNotContinueException {
- // Assume any attempt to use \ was by a Windows client
- // and correct to the more typical / used in Git URIs.
- //
- name = name.replace('\\', '/');
-
- // ssh://git@thishost/path should always be name="/path" here
- //
- if (!name.startsWith("/")) //$NON-NLS-1$
- return null;
-
- try {
- return repositoryResolver.open(client, name.substring(1));
- } catch (RepositoryNotFoundException e) {
- // null signals it "wasn't found", which is all that is suitable
- // for the remote client to know.
- return null;
- } catch (ServiceNotEnabledException e) {
- // null signals it "wasn't found", which is all that is suitable
- // for the remote client to know.
- return null;
- }
- }
-
- protected abstract void run(SshSession client, Repository db)
- throws IOException, ServiceNotEnabledException, ServiceNotAuthorizedException;
- }
-
- public class UploadPackCommand extends RepositoryCommand {
- public UploadPackCommand(String repositoryName) { super(repositoryName); }
-
- @Override
- protected void run(SshSession client, Repository db)
- throws IOException, ServiceNotEnabledException, ServiceNotAuthorizedException {
- UploadPack up = uploadPackFactory.create(client, db);
- up.upload(in, out, null);
- }
- }
-
- public class ReceivePackCommand extends RepositoryCommand {
- public ReceivePackCommand(String repositoryName) { super(repositoryName); }
-
- @Override
- protected void run(SshSession client, Repository db)
- throws IOException, ServiceNotEnabledException, ServiceNotAuthorizedException {
- ReceivePack rp = receivePackFactory.create(client, db);
- rp.receive(in, out, null);
- }
- }
-
- public static class NonCommand extends AbstractSshCommand {
- @Override
- public void start(Environment env) {
- exit.onExit(127);
- }
- }
}
diff --git a/src/main/java/com/gitblit/transport/ssh/SshContext.java b/src/main/java/com/gitblit/transport/ssh/SshContext.java
new file mode 100644
index 00000000..b137cb87
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/SshContext.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.transport.ssh;
+
+public class SshContext {
+
+ private final SshSession session;
+ private final String commandLine;
+
+ public SshContext(SshSession session, String commandLine) {
+ this.session = session;
+ this.commandLine = commandLine;
+ }
+
+ public SshSession getSession() {
+ return session;
+ }
+
+ public String getCommandLine() {
+ return commandLine;
+ }
+}
diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
index dd4a2d8e..b23ddd58 100644
--- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
+++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
@@ -35,6 +35,8 @@ import com.gitblit.git.RepositoryResolver;
import com.gitblit.manager.IGitblit;
import com.gitblit.transport.ssh.commands.CreateRepository;
import com.gitblit.transport.ssh.commands.DispatchCommand;
+import com.gitblit.transport.ssh.commands.Receive;
+import com.gitblit.transport.ssh.commands.Upload;
import com.gitblit.transport.ssh.commands.VersionCommand;
import com.gitblit.utils.IdGenerator;
import com.gitblit.utils.StringUtils;
@@ -65,9 +67,6 @@ public class SshDaemon {
@SuppressWarnings("unused")
private final IGitblit gitblit;
-
- private final IdGenerator idGenerator;
-
private final SshServer sshd;
/**
@@ -77,7 +76,6 @@ public class SshDaemon {
*/
public SshDaemon(IGitblit gitblit, IdGenerator idGenerator) {
this.gitblit = gitblit;
- this.idGenerator = idGenerator;
IStoredSettings settings = gitblit.getSettings();
int port = settings.getInteger(Keys.git.sshPort, 0);
@@ -106,15 +104,21 @@ public class SshDaemon {
gitblitCmd.registerCommand(CreateRepository.class);
gitblitCmd.registerCommand(VersionCommand.class);
- DispatchCommand dispatcher = new DispatchCommand();
- dispatcher.registerDispatcher("gitblit", gitblitCmd);
+ DispatchCommand gitCmd = new DispatchCommand();
+ gitCmd.registerCommand(Upload.class);
+ gitCmd.registerCommand(Receive.class);
+
+ DispatchCommand root = new DispatchCommand();
+ root.registerDispatcher("gitblit", gitblitCmd);
+ root.registerDispatcher("git", gitCmd);
+
+ root.setRepositoryResolver(new RepositoryResolver<SshSession>(gitblit));
+ root.setUploadPackFactory(new GitblitUploadPackFactory<SshSession>(gitblit));
+ root.setReceivePackFactory(new GitblitReceivePackFactory<SshSession>(gitblit));
SshCommandFactory commandFactory = new SshCommandFactory(
- new RepositoryResolver<SshSession>(gitblit),
- new GitblitUploadPackFactory<SshSession>(gitblit),
- new GitblitReceivePackFactory<SshSession>(gitblit),
new WorkQueue(idGenerator),
- dispatcher);
+ root);
sshd.setCommandFactory(commandFactory);
diff --git a/src/main/java/com/gitblit/transport/ssh/SshSession.java b/src/main/java/com/gitblit/transport/ssh/SshSession.java
index 9f18a197..ffff8af4 100644
--- a/src/main/java/com/gitblit/transport/ssh/SshSession.java
+++ b/src/main/java/com/gitblit/transport/ssh/SshSession.java
@@ -36,6 +36,7 @@ public class SshSession {
private volatile String username;
private volatile String authError;
+ private volatile String repositoryName;
SshSession(int sessionId, SocketAddress peer) {
this.sessionId = sessionId;
@@ -78,6 +79,14 @@ public class SshSession {
authError = error;
}
+ public void setRepositoryName(String repositoryName) {
+ this.repositoryName = repositoryName;
+ }
+
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+
/** @return {@code true} if the authentication did not succeed. */
boolean isAuthenticationError() {
return authError != null;
diff --git a/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java b/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
index fd73ccfd..a04c505f 100644
--- a/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
+++ b/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
@@ -33,8 +33,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.transport.ssh.AbstractSshCommand;
+import com.gitblit.transport.ssh.SshContext;
import com.gitblit.utils.IdGenerator;
import com.gitblit.utils.WorkQueue;
+import com.gitblit.utils.WorkQueue.CancelableRunnable;
import com.gitblit.utils.cli.CmdLineParser;
import com.google.common.base.Charsets;
import com.google.common.util.concurrent.Atomics;
@@ -49,6 +51,9 @@ public abstract class BaseCommand extends AbstractSshCommand {
/** Unparsed command line options. */
private String[] argv;
+ /** Ssh context */
+ protected SshContext ctx;
+
/** The task, as scheduled on a worker thread. */
private final AtomicReference<Future<?>> task;
@@ -61,6 +66,10 @@ public abstract class BaseCommand extends AbstractSshCommand {
this.executor = w.getDefaultQueue();
}
+ public void setContext(SshContext ctx) {
+ this.ctx = ctx;
+ }
+
public void setInputStream(final InputStream in) {
this.in = in;
}
@@ -77,7 +86,10 @@ public abstract class BaseCommand extends AbstractSshCommand {
this.exit = callback;
}
- protected void provideStateTo(final Command cmd) {
+ protected void provideBaseStateTo(final Command cmd) {
+ if (cmd instanceof BaseCommand) {
+ ((BaseCommand)cmd).setContext(ctx);
+ }
cmd.setInputStream(in);
cmd.setOutputStream(out);
cmd.setErrorStream(err);
@@ -155,31 +167,25 @@ public abstract class BaseCommand extends AbstractSshCommand {
return "";
}
- private final class TaskThunk implements com.gitblit.utils.WorkQueue.CancelableRunnable {
+ private final class TaskThunk implements CancelableRunnable {
private final CommandRunnable thunk;
private final String taskName;
private TaskThunk(final CommandRunnable thunk) {
this.thunk = thunk;
- // TODO
-// StringBuilder m = new StringBuilder("foo");
-// m.append(context.getCommandLine());
-// if (userProvider.get().isIdentifiedUser()) {
-// IdentifiedUser u = (IdentifiedUser) userProvider.get();
-// m.append(" (").append(u.getAccount().getUserName()).append(")");
-// }
- this.taskName = "foo";//m.toString();
+ StringBuilder m = new StringBuilder();
+ m.append(ctx.getCommandLine());
+ this.taskName = m.toString();
}
@Override
public void cancel() {
synchronized (this) {
- //final Context old = sshScope.set(context);
try {
//onExit(/*STATUS_CANCEL*/);
} finally {
- //sshScope.set(old);
+ ctx = null;
}
}
}
@@ -190,11 +196,8 @@ public abstract class BaseCommand extends AbstractSshCommand {
final Thread thisThread = Thread.currentThread();
final String thisName = thisThread.getName();
int rc = 0;
- //final Context old = sshScope.set(context);
try {
- //context.started = TimeUtil.nowMs();
thisThread.setName("SSH " + taskName);
-
thunk.run();
out.flush();
@@ -231,6 +234,11 @@ public abstract class BaseCommand extends AbstractSshCommand {
}
+ /** Runnable function which can retrieve a project name related to the task */
+ public static interface RepositoryCommandRunnable extends CommandRunnable {
+ public String getRepository();
+ }
+
/**
* Spawn a function into its own thread.
* <p>
diff --git a/src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java b/src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java
index b6944eaf..597b9ea1 100644
--- a/src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java
+++ b/src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java
@@ -27,7 +27,12 @@ import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.kohsuke.args4j.Argument;
+import com.gitblit.git.GitblitReceivePackFactory;
+import com.gitblit.git.GitblitUploadPackFactory;
+import com.gitblit.git.RepositoryResolver;
+import com.gitblit.transport.ssh.AbstractGitCommand;
import com.gitblit.transport.ssh.CommandMetaData;
+import com.gitblit.transport.ssh.SshSession;
import com.gitblit.utils.cli.SubcommandHandler;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
@@ -95,11 +100,11 @@ public class DispatchCommand extends BaseCommand {
bc.setName(getName() + " " + commandName);
}
bc.setArguments(args.toArray(new String[args.size()]));
- } else if (!args.isEmpty()) {
- throw new UnloggedFailure(1, commandName + " does not take arguments");
}
- provideStateTo(cmd);
+ provideBaseStateTo(cmd);
+ provideGitState(cmd);
+ reset();
//atomicCmd.set(cmd);
cmd.start(env);
@@ -136,7 +141,7 @@ public class DispatchCommand extends BaseCommand {
}
@Override
-protected String usage() {
+ protected String usage() {
final StringBuilder usage = new StringBuilder();
usage.append("Available commands");
if (!getName().isEmpty()) {
@@ -173,4 +178,39 @@ protected String usage() {
usage.append("\n");
return usage.toString();
}
+
+ // This is needed because we are not using provider or
+ // clazz.newInstance() for DispatchCommand
+ private void reset() {
+ args = new ArrayList<String>();
+ }
+
+ private void provideGitState(Command cmd) {
+ if (cmd instanceof AbstractGitCommand) {
+ AbstractGitCommand a = (AbstractGitCommand) cmd;
+ a.setRepositoryResolver(repositoryResolver);
+ a.setUploadPackFactory(gitblitUploadPackFactory);
+ a.setReceivePackFactory(gitblitReceivePackFactory);
+ } else if (cmd instanceof DispatchCommand) {
+ DispatchCommand d = (DispatchCommand)cmd;
+ d.setRepositoryResolver(repositoryResolver);
+ d.setUploadPackFactory(gitblitUploadPackFactory);
+ d.setReceivePackFactory(gitblitReceivePackFactory);
+ }
+ }
+
+ private RepositoryResolver<SshSession> repositoryResolver;
+ public void setRepositoryResolver(RepositoryResolver<SshSession> repositoryResolver) {
+ this.repositoryResolver = repositoryResolver;
+ }
+
+ private GitblitUploadPackFactory<SshSession> gitblitUploadPackFactory;
+ public void setUploadPackFactory(GitblitUploadPackFactory<SshSession> gitblitUploadPackFactory) {
+ this.gitblitUploadPackFactory = gitblitUploadPackFactory;
+ }
+
+ private GitblitReceivePackFactory<SshSession> gitblitReceivePackFactory;
+ public void setReceivePackFactory(GitblitReceivePackFactory<SshSession> gitblitReceivePackFactory) {
+ this.gitblitReceivePackFactory = gitblitReceivePackFactory;
+ }
}
diff --git a/src/main/java/com/gitblit/transport/ssh/commands/Receive.java b/src/main/java/com/gitblit/transport/ssh/commands/Receive.java
new file mode 100644
index 00000000..dd1e8a06
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/commands/Receive.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.transport.ssh.commands;
+
+import org.eclipse.jgit.transport.ReceivePack;
+
+import com.gitblit.transport.ssh.AbstractGitCommand;
+import com.gitblit.transport.ssh.CommandMetaData;
+
+@CommandMetaData(name = "git-receive-pack", description = "Receive pack")
+public class Receive extends AbstractGitCommand {
+ @Override
+ protected void runImpl() throws Failure {
+ try {
+ ReceivePack rp = receivePackFactory.create(ctx.getSession(), repo);
+ rp.receive(in, out, null);
+ } catch (Exception e) {
+ throw new Failure(1, "fatal: Cannot receive pack: ", e);
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/transport/ssh/commands/Upload.java b/src/main/java/com/gitblit/transport/ssh/commands/Upload.java
new file mode 100644
index 00000000..d6c3f961
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/commands/Upload.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.transport.ssh.commands;
+
+import javax.inject.Inject;
+
+import org.eclipse.jgit.transport.UploadPack;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+
+import com.gitblit.git.RepositoryResolver;
+import com.gitblit.transport.ssh.AbstractGitCommand;
+import com.gitblit.transport.ssh.CommandMetaData;
+import com.gitblit.transport.ssh.SshSession;
+
+@CommandMetaData(name = "git-upload-pack", description = "Upload pack")
+public class Upload extends AbstractGitCommand {
+ @Override
+ protected void runImpl() throws Failure {
+ try {
+ UploadPack up = uploadPackFactory.create(ctx.getSession(), repo);
+ up.upload(in, out, null);
+ } catch (Exception e) {
+ throw new Failure(1, "fatal: Cannot upload pack: ", e);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/gitblit/transport/ssh/commands/VersionCommand.java b/src/main/java/com/gitblit/transport/ssh/commands/VersionCommand.java
index baae6a2c..fc3e01b3 100644
--- a/src/main/java/com/gitblit/transport/ssh/commands/VersionCommand.java
+++ b/src/main/java/com/gitblit/transport/ssh/commands/VersionCommand.java
@@ -29,7 +29,7 @@ public class VersionCommand extends SshCommand {
@Override
public void run() {
- stdout.println(String.format("Version: %s", Constants.getGitBlitVersion(),
+ stdout.println(String.format("Version: %s", Constants.getGitBlitVersion(),
verbose));
}
}