James Moger
2014-03-14 59e621d541746ff5f2576541abc1a201afcbc15f
Move dispatcher creation to SshCommandFactory and revise permission
checks
4 files modified
188 ■■■■ changed files
src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java 73 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/SshDaemon.java 47 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java 58 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/SshCommandFactory.java
@@ -34,7 +34,21 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.git.GitblitReceivePackFactory;
import com.gitblit.git.GitblitUploadPackFactory;
import com.gitblit.git.RepositoryResolver;
import com.gitblit.manager.IGitblit;
import com.gitblit.models.UserModel;
import com.gitblit.transport.ssh.commands.AddKeyCommand;
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.RemoveKeyCommand;
import com.gitblit.transport.ssh.commands.ReviewCommand;
import com.gitblit.transport.ssh.commands.SetAccountCommand;
import com.gitblit.transport.ssh.commands.Upload;
import com.gitblit.transport.ssh.commands.VersionCommand;
import com.gitblit.utils.IdGenerator;
import com.gitblit.utils.WorkQueue;
import com.google.common.util.concurrent.Atomics;
@@ -44,18 +58,56 @@
 *
 */
public class SshCommandFactory implements CommandFactory {
  private static final Logger logger = LoggerFactory
      .getLogger(SshCommandFactory.class);
    private static final Logger logger = LoggerFactory.getLogger(SshCommandFactory.class);
    private final IGitblit gitblit;
    private final PublicKeyAuthenticator keyAuthenticator;
  private final ScheduledExecutorService startExecutor;
  private DispatchCommand dispatcher;
    public SshCommandFactory(IGitblit gitblit, PublicKeyAuthenticator keyAuthenticator, IdGenerator idGenerator) {
        this.gitblit = gitblit;
        this.keyAuthenticator = keyAuthenticator;
    public SshCommandFactory(
        WorkQueue workQueue,
        DispatchCommand d) {
        this.dispatcher = d;
        int threads = 2;//cfg.getInt("sshd","commandStartThreads", 2);
        WorkQueue workQueue = new WorkQueue(idGenerator);
        startExecutor = workQueue.createQueue(threads, "SshCommandStart");
    }
    /**
     * Creates the root dispatcher command which builds up the available commands.
     *
     * @param the client
     * @param the command line
     * @return the root dispatcher command
     */
    protected DispatchCommand createRootDispatcher(SshDaemonClient client, String cmdLine) {
        final UserModel user = client.getUser();
        DispatchCommand gitblitCmd = new DispatchCommand();
        gitblitCmd.registerCommand(user, VersionCommand.class);
        gitblitCmd.registerCommand(user, AddKeyCommand.class);
        gitblitCmd.registerCommand(user, RemoveKeyCommand.class);
        gitblitCmd.registerCommand(user, ReviewCommand.class);
        gitblitCmd.registerCommand(user, CreateRepository.class);
        gitblitCmd.registerCommand(user, SetAccountCommand.class);
        DispatchCommand gitCmd = new DispatchCommand();
        gitCmd.registerCommand(user, Upload.class);
        gitCmd.registerCommand(user, Receive.class);
        DispatchCommand root = new DispatchCommand();
        root.registerDispatcher("gitblit", gitblitCmd);
        root.registerDispatcher("git", gitCmd);
        root.setRepositoryResolver(new RepositoryResolver<SshDaemonClient>(gitblit));
        root.setUploadPackFactory(new GitblitUploadPackFactory<SshDaemonClient>(gitblit));
        root.setReceivePackFactory(new GitblitReceivePackFactory<SshDaemonClient>(gitblit));
        root.setAuthenticator(keyAuthenticator);
        root.setContext(new SshCommandContext(client, cmdLine));
        return root;
    }
    @Override
@@ -133,11 +185,10 @@
        private void onStart() throws IOException {
          synchronized (this) {
            SshCommandContext ctx = new SshCommandContext(session.getAttribute(SshDaemonClient.KEY), cmdLine);
                SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY);
            try {
              cmd = dispatcher;
                    cmd = createRootDispatcher(client, cmdLine);
              cmd.setArguments(argv);
              cmd.setContext(ctx);
              cmd.setInputStream(in);
              cmd.setOutputStream(out);
              cmd.setErrorStream(err);
@@ -156,7 +207,7 @@
              });
              cmd.start(env);
            } finally {
              ctx = null;
                    client = null;
            }
          }
        }
src/main/java/com/gitblit/transport/ssh/SshDaemon.java
@@ -34,22 +34,9 @@
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.git.GitblitReceivePackFactory;
import com.gitblit.git.GitblitUploadPackFactory;
import com.gitblit.git.RepositoryResolver;
import com.gitblit.manager.IGitblit;
import com.gitblit.transport.ssh.commands.AddKeyCommand;
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.RemoveKeyCommand;
import com.gitblit.transport.ssh.commands.ReviewCommand;
import com.gitblit.transport.ssh.commands.SetAccountCommand;
import com.gitblit.transport.ssh.commands.Upload;
import com.gitblit.transport.ssh.commands.VersionCommand;
import com.gitblit.utils.IdGenerator;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.WorkQueue;
import dagger.Module;
import dagger.ObjectGraph;
@@ -117,45 +104,19 @@
            addr = new InetSocketAddress(bindInterface, port);
        }
        PublicKeyAuthenticator publickeyAuthenticator = new PublicKeyAuthenticator(
                keyManager, gitblit);
        PublicKeyAuthenticator keyAuthenticator = new PublicKeyAuthenticator(keyManager, gitblit);
        sshd = SshServer.setUpDefaultServer();
        sshd.setPort(addr.getPort());
        sshd.setHost(addr.getHostName());
        sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider(new File(
                gitblit.getBaseFolder(), HOST_KEY_STORE).getPath()));
        sshd.setPublickeyAuthenticator(publickeyAuthenticator);
        sshd.setPublickeyAuthenticator(keyAuthenticator);
        sshd.setPasswordAuthenticator(new UsernamePasswordAuthenticator(gitblit));
        sshd.setSessionFactory(new SshServerSessionFactory());
        sshd.setFileSystemFactory(new DisabledFilesystemFactory());
        sshd.setTcpipForwardingFilter(new NonForwardingFilter());
        DispatchCommand gitblitCmd = new DispatchCommand();
        gitblitCmd.registerCommand(CreateRepository.class);
        gitblitCmd.registerCommand(VersionCommand.class);
        gitblitCmd.registerCommand(AddKeyCommand.class);
        gitblitCmd.registerCommand(RemoveKeyCommand.class);
        gitblitCmd.registerCommand(SetAccountCommand.class);
        gitblitCmd.registerCommand(ReviewCommand.class);
        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<SshDaemonClient>(gitblit));
        root.setUploadPackFactory(new GitblitUploadPackFactory<SshDaemonClient>(gitblit));
        root.setReceivePackFactory(new GitblitReceivePackFactory<SshDaemonClient>(gitblit));
        root.setAuthenticator(publickeyAuthenticator);
        SshCommandFactory commandFactory = new SshCommandFactory(
                new WorkQueue(idGenerator),
                root);
        sshd.setCommandFactory(commandFactory);
        sshd.setCommandFactory(new SshCommandFactory(gitblit, keyAuthenticator, idGenerator));
        run = new AtomicBoolean(false);
    }
src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
@@ -120,16 +120,6 @@
        this.exit = callback;
    }
    protected void provideBaseStateTo(final Command cmd) {
        if (cmd instanceof BaseCommand) {
            ((BaseCommand) cmd).setContext(ctx);
        }
        cmd.setInputStream(in);
        cmd.setOutputStream(out);
        cmd.setErrorStream(err);
        cmd.setExitCallback(exit);
    }
    protected String getName() {
        return commandName;
    }
src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java
@@ -26,10 +26,13 @@
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.kohsuke.args4j.Argument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.git.GitblitReceivePackFactory;
import com.gitblit.git.GitblitUploadPackFactory;
import com.gitblit.git.RepositoryResolver;
import com.gitblit.models.UserModel;
import com.gitblit.transport.ssh.CommandMetaData;
import com.gitblit.transport.ssh.PublicKeyAuthenticator;
import com.gitblit.transport.ssh.SshDaemonClient;
@@ -40,6 +43,8 @@
import com.google.common.collect.Sets;
public class DispatchCommand extends BaseCommand {
    private Logger log = LoggerFactory.getLogger(getClass());
  @Argument(index = 0, required = false, metaVar = "COMMAND", handler = SubcommandHandler.class)
  private String commandName;
@@ -62,10 +67,21 @@
      root.put(name, cmd);
  }
  public void registerCommand(Class<? extends Command> cmd) {
    /**
     * Registers a command as long as the user is permitted to execute it.
     *
     * @param user
     * @param cmd
     */
    public void registerCommand(UserModel user, Class<? extends Command> cmd) {
      if (!cmd.isAnnotationPresent(CommandMetaData.class)) {
          throw new RuntimeException(MessageFormat.format("{0} must be annotated with {1}!",
                  cmd.getName(), CommandMetaData.class.getName()));
            throw new RuntimeException(MessageFormat.format("{0} must be annotated with {1}!", cmd.getName(),
                    CommandMetaData.class.getName()));
        }
        CommandMetaData meta = cmd.getAnnotation(CommandMetaData.class);
        if (meta.admin() && user.canAdmin()) {
            log.debug(MessageFormat.format("excluding admin command {} for {}", meta.name(), user.username));
            return;
      }
      commands.add(cmd);
  }
@@ -92,12 +108,6 @@
      }
      Command cmd = getCommand();
      if (cmd.getClass().isAnnotationPresent(CommandMetaData.class)) {
          CommandMetaData meta = cmd.getClass().getAnnotation(CommandMetaData.class);
          if (meta.admin() && !ctx.getClient().getUser().canAdmin()) {
              throw new UnloggedFailure(1, MessageFormat.format("{0} requires admin permissions", commandName));
          }
      }
      if (cmd instanceof BaseCommand) {
        BaseCommand bc = (BaseCommand) cmd;
        if (getName().isEmpty()) {
@@ -108,9 +118,7 @@
        bc.setArguments(args.toArray(new String[args.size()]));
      }
      provideBaseStateTo(cmd);
      provideGitState(cmd);
      reset();
            provideStateTo(cmd);
      //atomicCmd.set(cmd);
      cmd.start(env);
@@ -131,9 +139,7 @@
    }
    final Class<? extends Command> c = getMap().get(commandName);
      if (c == null) {
        String msg =
            (getName().isEmpty() ? "Gitblit" : getName()) + ": "
                + commandName + ": not found";
            String msg = (getName().isEmpty() ? "Gitblit" : getName()) + ": " + commandName + ": not found";
        throw new UnloggedFailure(1, msg);
      }
@@ -167,15 +173,11 @@
      final Class<? extends Command> c = m.get(name);
      CommandMetaData meta = c.getAnnotation(CommandMetaData.class);
      if (meta != null) {
        if (meta.admin() && !ctx.getClient().getUser().canAdmin()) {
            continue;
        }
        if (meta.hidden()) {
            continue;
        }
        usage.append("   ");
        usage.append(String.format(format, name,
            Strings.nullToEmpty(meta.description())));
                usage.append(String.format(format, name, Strings.nullToEmpty(meta.description())));
      }
      usage.append("\n");
    }
@@ -191,13 +193,15 @@
    return usage.toString();
  }
  // This is needed because we are not using provider or
  // clazz.newInstance() for DispatchCommand
  private void reset() {
      args = new ArrayList<String>();
    protected void provideStateTo(final Command cmd) {
        if (cmd instanceof BaseCommand) {
            ((BaseCommand) cmd).setContext(ctx);
  }
        cmd.setInputStream(in);
        cmd.setOutputStream(out);
        cmd.setErrorStream(err);
        cmd.setExitCallback(exit);
  private void provideGitState(Command cmd) {
      if (cmd instanceof BaseGitCommand) {
        BaseGitCommand a = (BaseGitCommand) cmd;
        a.setRepositoryResolver(repositoryResolver);
@@ -216,21 +220,25 @@
  }
  private RepositoryResolver<SshDaemonClient> repositoryResolver;
  public void setRepositoryResolver(RepositoryResolver<SshDaemonClient> repositoryResolver) {
      this.repositoryResolver = repositoryResolver;
  }
  private GitblitUploadPackFactory<SshDaemonClient> gitblitUploadPackFactory;
  public void setUploadPackFactory(GitblitUploadPackFactory<SshDaemonClient> gitblitUploadPackFactory) {
      this.gitblitUploadPackFactory = gitblitUploadPackFactory;
  }
  private GitblitReceivePackFactory<SshDaemonClient> gitblitReceivePackFactory;
  public void setReceivePackFactory(GitblitReceivePackFactory<SshDaemonClient> gitblitReceivePackFactory) {
      this.gitblitReceivePackFactory = gitblitReceivePackFactory;
  }
  private PublicKeyAuthenticator authenticator;
  public void setAuthenticator(PublicKeyAuthenticator authenticator) {
    this.authenticator = authenticator;
  }