James Moger
2014-03-28 a9dc74e73eea068b8cbb5c96958abccae88b4abc
src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java
@@ -15,18 +15,29 @@
 */
package com.gitblit.transport.ssh.gitblit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.kohsuke.args4j.Argument;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.manager.IGitblit;
import com.gitblit.models.RegistrantAccessPermission;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.transport.ssh.commands.CommandMetaData;
import com.gitblit.transport.ssh.commands.DispatchCommand;
import com.gitblit.transport.ssh.commands.ListFilterCommand;
import com.gitblit.transport.ssh.commands.SshCommand;
import com.gitblit.transport.ssh.commands.UsageExample;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.FlipTable;
import com.gitblit.utils.FlipTable.Borders;
import com.gitblit.utils.StringUtils;
import com.google.common.base.Joiner;
@CommandMetaData(name = "repositories", aliases = { "repos" }, description = "Repository management commands")
@@ -34,7 +45,411 @@
   @Override
   protected void setup(UserModel user) {
      // primary commands
      register(user, NewRepository.class);
      register(user, RenameRepository.class);
      register(user, RemoveRepository.class);
      register(user, ShowRepository.class);
      register(user, ListRepositories.class);
      // repository-specific commands
      register(user, SetField.class);
   }
   public static abstract class RepositoryCommand extends SshCommand {
      @Argument(index = 0, required = true, metaVar = "REPOSITORY", usage = "repository")
      protected String repository;
      protected RepositoryModel getRepository(boolean requireRepository) throws UnloggedFailure {
         IGitblit gitblit = getContext().getGitblit();
         RepositoryModel repo = gitblit.getRepositoryModel(repository);
         if (requireRepository && repo == null) {
            throw new UnloggedFailure(1, String.format("Repository %s does not exist!", repository));
         }
         return repo;
      }
      protected String sanitize(String name) throws UnloggedFailure {
         // automatically convert backslashes to forward slashes
         name = name.replace('\\', '/');
         // Automatically replace // with /
         name = name.replace("//", "/");
         // prohibit folder paths
         if (name.startsWith("/")) {
            throw new UnloggedFailure(1,  "Illegal leading slash");
         }
         if (name.startsWith("../")) {
            throw new UnloggedFailure(1,  "Illegal relative slash");
         }
         if (name.contains("/../")) {
            throw new UnloggedFailure(1,  "Illegal relative slash");
         }
         if (name.endsWith("/")) {
            name = name.substring(0, name.length() - 1);
         }
         return name;
      }
   }
   @CommandMetaData(name = "new", aliases = { "add" }, description = "Create a new repository")
   @UsageExample(syntax = "${cmd} myRepo")
   public static class NewRepository extends RepositoryCommand {
      @Override
      public void run() throws UnloggedFailure {
         UserModel user = getContext().getClient().getUser();
         String name = sanitize(repository);
         if (!user.canCreate(name)) {
            // try to prepend personal path
            String path  = StringUtils.getFirstPathElement(name);
            if ("".equals(path)) {
               name = user.getPersonalPath() + "/" + name;
            }
         }
         if (getRepository(false) != null) {
            throw new UnloggedFailure(1, String.format("Repository %s already exists!", name));
         }
         if (!user.canCreate(name)) {
            throw new UnloggedFailure(1,  String.format("Sorry, you do not have permission to create %s", name));
         }
         IGitblit gitblit = getContext().getGitblit();
         RepositoryModel repo = new RepositoryModel();
         repo.name = name;
         repo.projectPath = StringUtils.getFirstPathElement(name);
         String restriction = gitblit.getSettings().getString(Keys.git.defaultAccessRestriction, "PUSH");
         repo.accessRestriction = AccessRestrictionType.fromName(restriction);
         String authorization = gitblit.getSettings().getString(Keys.git.defaultAuthorizationControl, null);
         repo.authorizationControl = AuthorizationControl.fromName(authorization);
         if (user.isMyPersonalRepository(name)) {
            // personal repositories are private by default
            repo.addOwner(user.username);
            repo.accessRestriction = AccessRestrictionType.VIEW;
            repo.authorizationControl = AuthorizationControl.NAMED;
         }
         try {
            gitblit.updateRepositoryModel(repository,  repo, true);
            stdout.println(String.format("%s created.", repo.name));
         } catch (GitBlitException e) {
            log.error("Failed to add " + repository, e);
            throw new UnloggedFailure(1, e.getMessage());
         }
      }
   }
   @CommandMetaData(name = "rename", aliases = { "mv" }, description = "Rename a repository")
   @UsageExample(syntax = "${cmd} myRepo.git otherRepo.git", description = "Rename the repository from myRepo.git to otherRepo.git")
   public static class RenameRepository extends RepositoryCommand {
      @Argument(index = 1, required = true, metaVar = "NEWNAME", usage = "the new repository name")
      protected String newRepositoryName;
            @Override
      public void run() throws UnloggedFailure {
         RepositoryModel repo = getRepository(true);
         IGitblit gitblit = getContext().getGitblit();
         UserModel user = getContext().getClient().getUser();
         String name = sanitize(newRepositoryName);
         if (!user.canCreate(name)) {
            // try to prepend personal path
            String path  = StringUtils.getFirstPathElement(name);
            if ("".equals(path)) {
               name = user.getPersonalPath() + "/" + name;
            }
         }
         if (null != gitblit.getRepositoryModel(name)) {
            throw new UnloggedFailure(1, String.format("Repository %s already exists!", name));
         }
         if (repo.name.equalsIgnoreCase(name)) {
            throw new UnloggedFailure(1, "Repository names are identical");
         }
         if (!user.canAdmin(repo)) {
            throw new UnloggedFailure(1,  String.format("Sorry, you do not have permission to rename %s", repository));
         }
         if (!user.canCreate(name)) {
            throw new UnloggedFailure(1, String.format("Sorry, you don't have permission to move %s to %s/", repository, name));
         }
         // set the new name
         repo.name = name;
         try {
            gitblit.updateRepositoryModel(repository, repo, false);
            stdout.println(String.format("Renamed repository %s to %s.", repository, name));
         } catch (GitBlitException e) {
            String msg = String.format("Failed to rename repository from %s to %s", repository, name);
            log.error(msg, e);
            throw new UnloggedFailure(1, msg);
         }
      }
   }
   @CommandMetaData(name = "set", description = "Set the specified field of a repository")
   @UsageExample(syntax = "${cmd} myRepo description John's personal projects", description = "Set the description of a repository")
   public static class SetField extends RepositoryCommand {
      @Argument(index = 1, required = true, metaVar = "FIELD", usage = "the field to update")
      protected String fieldName;
      @Argument(index = 2, required = true, metaVar = "VALUE", usage = "the new value")
      protected List<String> fieldValues = new ArrayList<String>();
      protected enum Field {
         description;
         static Field fromString(String name) {
            for (Field field : values()) {
               if (field.name().equalsIgnoreCase(name)) {
                  return field;
               }
            }
            return null;
         }
      }
      @Override
      protected String getUsageText() {
         String fields = Joiner.on(", ").join(Field.values());
         StringBuilder sb = new StringBuilder();
         sb.append("Valid fields are:\n   ").append(fields);
         return sb.toString();
      }
      @Override
      public void run() throws UnloggedFailure {
         RepositoryModel repo = getRepository(true);
         Field field = Field.fromString(fieldName);
         if (field == null) {
            throw new UnloggedFailure(1, String.format("Unknown field %s", fieldName));
         }
         if (!getContext().getClient().getUser().canAdmin(repo)) {
            throw new UnloggedFailure(1,  String.format("Sorry, you do not have permission to administer %s", repository));
         }
         String value = Joiner.on(" ").join(fieldValues).trim();
         IGitblit gitblit = getContext().getGitblit();
         switch(field) {
         case description:
            repo.description = value;
            break;
         default:
            throw new UnloggedFailure(1,  String.format("Field %s was not properly handled by the set command.", fieldName));
         }
         try {
            gitblit.updateRepositoryModel(repo.name,  repo, false);
            stdout.println(String.format("Set %s.%s = %s", repo.name, fieldName, value));
         } catch (GitBlitException e) {
            String msg = String.format("Failed to set %s.%s = %s", repo.name, fieldName, value);
            log.error(msg, e);
            throw new UnloggedFailure(1, msg);
         }
      }
      protected boolean toBool(String value) throws UnloggedFailure {
         String v = value.toLowerCase();
         if (v.equals("t")
               || v.equals("true")
               || v.equals("yes")
               || v.equals("on")
               || v.equals("y")
               || v.equals("1")) {
            return true;
         } else if (v.equals("f")
               || v.equals("false")
               || v.equals("no")
               || v.equals("off")
               || v.equals("n")
               || v.equals("0")) {
            return false;
         }
         throw new UnloggedFailure(1,  String.format("Invalid boolean value %s", value));
      }
   }
   @CommandMetaData(name = "remove", aliases = { "rm" }, description = "Remove a repository")
   @UsageExample(syntax = "${cmd} myRepo.git", description = "Delete myRepo.git")
   public static class RemoveRepository extends RepositoryCommand {
      @Override
      public void run() throws UnloggedFailure {
         RepositoryModel repo = getRepository(true);
         if (!getContext().getClient().getUser().canAdmin(repo)) {
            throw new UnloggedFailure(1,  String.format("Sorry, you do not have permission to delete %s", repository));
         }
         IGitblit gitblit = getContext().getGitblit();
         if (gitblit.deleteRepositoryModel(repo)) {
            stdout.println(String.format("%s has been deleted.", repository));
         } else {
            throw new UnloggedFailure(1, String.format("Failed to delete %s!", repository));
         }
      }
   }
   @CommandMetaData(name = "show", description = "Show the details of a repository")
   @UsageExample(syntax = "${cmd} myRepo.git", description = "Display myRepo.git")
   public static class ShowRepository extends RepositoryCommand {
      @Override
      public void run() throws UnloggedFailure {
         RepositoryModel r = getRepository(true);
         if (!getContext().getClient().getUser().canAdmin(r)) {
            throw new UnloggedFailure(1,  String.format("Sorry, you do not have permission to see the %s settings.", repository));
         }
         IGitblit gitblit = getContext().getGitblit();
         // fields
         StringBuilder fb = new StringBuilder();
         fb.append("Description    : ").append(toString(r.description)).append('\n');
         fb.append("Origin         : ").append(toString(r.origin)).append('\n');
         fb.append("Default Branch : ").append(toString(r.HEAD)).append('\n');
         fb.append('\n');
         fb.append("GC Period    : ").append(r.gcPeriod).append('\n');
         fb.append("GC Threshold : ").append(r.gcThreshold).append('\n');
         fb.append('\n');
         fb.append("Accept Tickets   : ").append(toString(r.acceptNewTickets)).append('\n');
         fb.append("Accept Patchsets : ").append(toString(r.acceptNewPatchsets)).append('\n');
         fb.append("Require Approval : ").append(toString(r.requireApproval)).append('\n');
         fb.append("Merge To         : ").append(toString(r.mergeTo)).append('\n');
         fb.append('\n');
         fb.append("Incremental push tags    : ").append(toString(r.useIncrementalPushTags)).append('\n');
         fb.append("Show remote branches     : ").append(toString(r.showRemoteBranches)).append('\n');
         fb.append("Skip size calculations   : ").append(toString(r.skipSizeCalculation)).append('\n');
         fb.append("Skip summary metrics     : ").append(toString(r.skipSummaryMetrics)).append('\n');
         fb.append("Max activity commits     : ").append(r.maxActivityCommits).append('\n');
         fb.append("Author metric exclusions : ").append(toString(r.metricAuthorExclusions)).append('\n');
         fb.append("Commit Message Renderer  : ").append(r.commitMessageRenderer).append('\n');
         fb.append("Mailing Lists            : ").append(toString(r.mailingLists)).append('\n');
         fb.append('\n');
         fb.append("Access Restriction    : ").append(r.accessRestriction).append('\n');
         fb.append("Authorization Control : ").append(r.authorizationControl).append('\n');
         fb.append('\n');
         fb.append("Is Frozen        : ").append(toString(r.isFrozen)).append('\n');
         fb.append("Allow Forks      : ").append(toString(r.allowForks)).append('\n');
         fb.append("Verify Committer : ").append(toString(r.verifyCommitter)).append('\n');
         fb.append('\n');
         fb.append("Federation Strategy : ").append(r.federationStrategy).append('\n');
         fb.append("Federation Sets     : ").append(toString(r.federationSets)).append('\n');
         fb.append('\n');
         fb.append("Indexed Branches : ").append(toString(r.indexedBranches)).append('\n');
         fb.append('\n');
         fb.append("Pre-Receive Scripts  : ").append(toString(r.preReceiveScripts)).append('\n');
         fb.append("           inherited : ").append(toString(gitblit.getPreReceiveScriptsInherited(r))).append('\n');
         fb.append("Post-Receive Scripts : ").append(toString(r.postReceiveScripts)).append('\n');
         fb.append("           inherited : ").append(toString(gitblit.getPostReceiveScriptsInherited(r))).append('\n');
         String fields = fb.toString();
         // owners
         String owners;
         if (r.owners.isEmpty()) {
            owners = FlipTable.EMPTY;
         } else {
            String[] pheaders = { "Account", "Name" };
            Object [][] pdata = new Object[r.owners.size()][];
            for (int i = 0; i < r.owners.size(); i++) {
               String owner = r.owners.get(i);
               UserModel u = gitblit.getUserModel(owner);
               pdata[i] = new Object[] { owner, u == null ? "" : u.getDisplayName() };
            }
            owners = FlipTable.of(pheaders, pdata, Borders.COLS);
         }
         // team permissions
         List<RegistrantAccessPermission> tperms = gitblit.getTeamAccessPermissions(r);
         String tpermissions;
         if (tperms.isEmpty()) {
            tpermissions = FlipTable.EMPTY;
         } else {
            String[] pheaders = { "Team", "Permission", "Type" };
            Object [][] pdata = new Object[tperms.size()][];
            for (int i = 0; i < tperms.size(); i++) {
               RegistrantAccessPermission ap = tperms.get(i);
               pdata[i] = new Object[] { ap.registrant, ap.permission, ap.permissionType };
            }
            tpermissions = FlipTable.of(pheaders, pdata, Borders.COLS);
         }
         // user permissions
         List<RegistrantAccessPermission> uperms = gitblit.getUserAccessPermissions(r);
         String upermissions;
         if (uperms.isEmpty()) {
            upermissions = FlipTable.EMPTY;
         } else {
            String[] pheaders = { "Account", "Name", "Permission", "Type", "Source", "Mutable" };
            Object [][] pdata = new Object[uperms.size()][];
            for (int i = 0; i < uperms.size(); i++) {
               RegistrantAccessPermission ap = uperms.get(i);
               String name = "";
               try {
                  String dn = gitblit.getUserModel(ap.registrant).displayName;
                  if (dn != null) {
                     name = dn;
                  }
               } catch (Exception e) {
               }
               pdata[i] = new Object[] { ap.registrant, name, ap.permission, ap.permissionType, ap.source, ap.mutable ? "Y":"" };
            }
            upermissions = FlipTable.of(pheaders, pdata, Borders.COLS);
         }
         // assemble table
         String title = r.name;
         String [] headers = new String[] { title };
         String[][] data = new String[8][];
         data[0] = new String [] { "FIELDS" };
         data[1] = new String [] {fields };
         data[2] = new String [] { "OWNERS" };
         data[3] = new String [] { owners };
         data[4] = new String [] { "TEAM PERMISSIONS" };
         data[5] = new String [] { tpermissions };
         data[6] = new String [] { "USER PERMISSIONS" };
         data[7] = new String [] { upermissions };
         stdout.println(FlipTable.of(headers, data));
      }
      protected String toString(String val) {
         if (val == null) {
            return "";
         }
         return val;
      }
      protected String toString(Collection<?> collection) {
         if (collection == null) {
            return "";
         }
         return Joiner.on(", ").join(collection);
      }
      protected String toString(boolean val) {
         if (val) {
            return "Y";
         }
         return "";
      }
   }
   /* List repositories */