James Moger
2014-03-28 a9dc74e73eea068b8cbb5c96958abccae88b4abc
Implement management commands in repositories dispatcher
7 files modified
443 ■■■■■ changed files
src/main/java/com/gitblit/GitBlit.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/models/UserModel.java 12 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/WelcomeShell.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/BaseKeyCommand.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java 415 ●●●●● patch | view | raw | blame | history
src/site/setup_transport_ssh.mkd 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/GitBlit.java
@@ -226,7 +226,7 @@
    public boolean deleteRepositoryModel(RepositoryModel model) {
        boolean success = repositoryManager.deleteRepositoryModel(model);
        if (success && ticketService != null) {
            return ticketService.deleteAll(model);
            ticketService.deleteAll(model);
        }
        return success;
    }
src/main/java/com/gitblit/models/UserModel.java
@@ -543,7 +543,7 @@
            // admins can create any repository
            return true;
        }
        if (canCreate) {
        if (canCreate()) {
            String projectPath = StringUtils.getFirstPathElement(repository);
            if (!StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase(getPersonalPath())) {
                // personal repository
@@ -552,6 +552,16 @@
        }
        return false;
    }
    /**
     * Returns true if the user is allowed to administer the specified repository
     *
     * @param repo
     * @return true if the user can administer the repository
     */
    public boolean canAdmin(RepositoryModel repo) {
        return canAdmin() || isMyPersonalRepository(repo.name);
    }
    public boolean isAuthenticated() {
        return !UserModel.ANONYMOUS.equals(this) && isAuthenticated;
src/main/java/com/gitblit/transport/ssh/WelcomeShell.java
@@ -165,7 +165,7 @@
                msg.append(nl);
                msg.append(nl);
                msg.append(String.format("   cat ~/.ssh/id_rsa.pub | ssh -l %s -p %d %s gitblit keys add -", user.username, port, hostname));
                msg.append(String.format("   cat ~/.ssh/id_rsa.pub | ssh -l %s -p %d %s gitblit keys add", user.username, port, hostname));
                msg.append(nl);
                msg.append(nl);
src/main/java/com/gitblit/transport/ssh/gitblit/BaseKeyCommand.java
@@ -36,7 +36,7 @@
    protected List<String> readKeys(List<String> sshKeys)
            throws UnsupportedEncodingException, IOException {
        int idx = -1;
        if ((idx = sshKeys.indexOf("-")) >= 0) {
        if (sshKeys.isEmpty() || (idx = sshKeys.indexOf("-")) >= 0) {
            String sshKey = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    in, Charsets.UTF_8));
src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
@@ -59,7 +59,7 @@
        protected final Logger log = LoggerFactory.getLogger(getClass());
        @Argument(metaVar = "-|<KEY>", usage = "the key(s) to add", required = true)
        @Argument(metaVar = "<KEY>", usage = "the key(s) to add")
        private List<String> addKeys = new ArrayList<String>();
        @Override
@@ -82,7 +82,7 @@
        private final String ALL = "ALL";
        @Argument(metaVar = "-|<INDEX>|<KEY>|ALL", usage = "the key to remove", required = true)
        @Argument(metaVar = "<INDEX>|<KEY>|ALL", usage = "the key to remove", required = true)
        private List<String> removeKeys = new ArrayList<String>();
        @Override
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 */
src/site/setup_transport_ssh.mkd
@@ -23,8 +23,8 @@
Then you can upload your *public* key right from the command-line.
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add -
    cat c:\<userfolder>\.ssh\id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add -
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
    cat c:\<userfolder>\.ssh\id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
**NOTE:** It is important to note that *ssh-keygen* generates a public/private keypair (e.g. id_rsa and id_rsa.pub).  You want to upload the *public* key, which is denoted by the *.pub* file extension.
@@ -62,7 +62,7 @@
Add an SSH public key to your account.  This command accepts a public key piped to stdin.
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add -
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
##### keys list