James Moger
2014-03-27 fc8487e5ade37a82e686f4168ea5702bde3cdb4b
Implemented teams dispatcher and management commands
1 files added
1 files modified
362 ■■■■■ changed files
src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java 361 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java
@@ -33,6 +33,7 @@
        register(user, KeysDispatcher.class);
        register(user, TicketsDispatcher.class);
        register(user, UsersDispatcher.class);
        register(user, TeamsDispatcher.class);
        register(user, ProjectsDispatcher.class);
        register(user, RepositoriesDispatcher.class);
    }
src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java
New file
@@ -0,0 +1,361 @@
/*
 * 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.gitblit;
import java.util.List;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import com.gitblit.Constants.AccessPermission;
import com.gitblit.manager.IGitblit;
import com.gitblit.models.RegistrantAccessPermission;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
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.transport.ssh.commands.UsageExamples;
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 = "teams", description = "Team management commands", admin = true)
public class TeamsDispatcher extends DispatchCommand {
    @Override
    protected void setup(UserModel user) {
        // primary team commands
        register(user, NewTeam.class);
        register(user, RemoveTeam.class);
        register(user, ShowTeam.class);
        register(user, ListTeams.class);
        // team-specific commands
        register(user, Permissions.class);
        register(user, Members.class);
    }
    public static abstract class TeamCommand extends SshCommand {
        @Argument(index = 0, required = true, metaVar = "TEAM", usage = "team name")
        protected String teamname;
        protected TeamModel getTeam(boolean requireTeam) throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            TeamModel team = gitblit.getTeamModel(teamname);
            if (requireTeam && team == null) {
                throw new UnloggedFailure(1, String.format("Team %s does not exist!", teamname));
            }
            return team;
        }
    }
    @CommandMetaData(name = "new", description = "Create a new team")
    @UsageExample(syntax = "${cmd} contributors --canFork --canCreate")
    public static class NewTeam extends TeamCommand {
        @Option(name = "--canAdmin", usage = "can administer the server")
        protected boolean canAdmin;
        @Option(name = "--canFork", usage = "can fork repositories")
        protected boolean canFork;
        @Option(name = "--canCreate", usage = "can create personal repositories")
        protected boolean canCreate;
        @Override
        public void run() throws UnloggedFailure {
            if (getTeam(false) != null) {
                throw new UnloggedFailure(1, String.format("Team %s already exists!", teamname));
            }
            TeamModel team = new TeamModel(teamname);
            team.canAdmin = canAdmin;
            team.canFork = canFork;
            team.canCreate = canCreate;
            IGitblit gitblit = getContext().getGitblit();
            if (gitblit.updateTeamModel(teamname, team)) {
                stdout.println(String.format("%s created.", teamname));
            } else {
                throw new UnloggedFailure(1, String.format("Failed to create %s!", teamname));
            }
        }
    }
    @CommandMetaData(name = "permissions", aliases = { "perms" }, description = "Add or remove permissions from a team")
    @UsageExample(syntax = "${cmd} contributors RW:alpha/repo.git RWC:alpha/repo2.git", description = "Add or set permissions for contributors")
    public static class Permissions extends TeamCommand {
        @Argument(index = 1, multiValued = true, metaVar = "[PERMISSION:]REPOSITORY", usage = "a repository expression")
        protected List<String> permissions;
        @Option(name = "--remove", aliases = { "-r" }, metaVar = "REPOSITORY|ALL", usage = "remove a repository permission")
        protected List<String> removals;
        @Override
        public void run() throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            TeamModel team = getTeam(true);
            boolean modified = false;
            if (!ArrayUtils.isEmpty(removals)) {
                if (removals.contains("ALL")) {
                    team.permissions.clear();
                } else {
                    for (String repo : removals) {
                        team.removeRepositoryPermission(repo);
                        log.info(String.format("Removing permission for %s from %s", repo, teamname));
                    }
                }
                modified = true;
            }
            if (!ArrayUtils.isEmpty(permissions)) {
                for (String perm : permissions) {
                    String repo = AccessPermission.repositoryFromRole(perm);
                    if (StringUtils.findInvalidCharacter(repo) == null) {
                        // explicit permision, confirm repository
                        RepositoryModel r = gitblit.getRepositoryModel(repo);
                        if (r == null) {
                            throw new UnloggedFailure(1, String.format("Repository %s does not exist!", repo));
                        }
                    }
                    AccessPermission ap = AccessPermission.permissionFromRole(perm);
                    team.setRepositoryPermission(repo, ap);
                    log.info(String.format("Setting %s:%s for %s", ap.name(), repo, teamname));
                }
                modified = true;
            }
            if (modified && gitblit.updateTeamModel(teamname, team)) {
                // reload & display new permissions
                team = gitblit.getTeamModel(teamname);
            }
            showPermissions(team);
        }
        protected void showPermissions(TeamModel team) {
            List<RegistrantAccessPermission> perms = team.getRepositoryPermissions();
            String[] pheaders = { "Repository", "Permission", "Type" };
            Object [][] pdata = new Object[perms.size()][];
            for (int i = 0; i < perms.size(); i++) {
                RegistrantAccessPermission ap = perms.get(i);
                pdata[i] = new Object[] { ap.registrant, ap.permission, ap.permissionType };
            }
            stdout.println(FlipTable.of(pheaders, pdata, Borders.BODY_HCOLS));
        }
    }
    @CommandMetaData(name = "members", aliases = { "users" }, description = "Add or remove team members")
    @UsageExample(syntax = "${cmd} contributors RW:alpha/repo.git RWC:alpha/repo2.git", description = "Add or set permissions for contributors")
    public static class Members extends TeamCommand {
        @Argument(index = 1, multiValued = true, metaVar = "USERNAME", usage = "a username")
        protected List<String> members;
        @Option(name = "--remove", aliases = { "-r" }, metaVar = "USERNAME|ALL", usage = "remove a team member")
        protected List<String> removals;
        @Override
        public void run() throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            TeamModel team = getTeam(true);
            boolean modified = false;
            if (!ArrayUtils.isEmpty(removals)) {
                if (removals.contains("ALL")) {
                    team.users.clear();
                } else {
                    for (String member : removals) {
                        team.removeUser(member);
                        log.info(String.format("Removing member %s from %s", member, teamname));
                    }
                }
                modified = true;
            }
            if (!ArrayUtils.isEmpty(members)) {
                for (String username : members) {
                    UserModel u = gitblit.getUserModel(username);
                    if (u == null) {
                        throw new UnloggedFailure(1,  String.format("Unknown user %s", username));
                    }
                    team.addUser(username);
                }
                modified = true;
            }
            if (modified && gitblit.updateTeamModel(teamname, team)) {
                // reload & display new permissions
                team = gitblit.getTeamModel(teamname);
            }
            String[] headers = { "Username", "Display Name" };
            Object [][] data = new Object[team.users.size()][];
            int i = 0;
            for (String username : team.users) {
                UserModel u = gitblit.getUserModel(username);
                data[i] = new Object[] { username, u.displayName };
                i++;
            }
            stdout.println(FlipTable.of(headers, data, Borders.BODY_HCOLS));
        }
    }
    @CommandMetaData(name = "remove", aliases = { "rm" }, description = "Remove a team")
    @UsageExample(syntax = "${cmd} contributors", description = "Delete the contributors team")
    public static class RemoveTeam extends TeamCommand {
        @Override
        public void run() throws UnloggedFailure {
            TeamModel team = getTeam(true);
            IGitblit gitblit = getContext().getGitblit();
            if (gitblit.deleteTeamModel(team)) {
                stdout.println(String.format("%s has been deleted.", teamname));
            } else {
                throw new UnloggedFailure(1, String.format("Failed to delete %s!", teamname));
            }
        }
    }
    @CommandMetaData(name = "show", description = "Show the details of a team")
    @UsageExample(syntax = "${cmd} contributors", description = "Display the 'contributors' team")
    public static class ShowTeam extends TeamCommand {
        @Override
        public void run() throws UnloggedFailure {
            TeamModel t = getTeam(true);
            // fields
            StringBuilder fb = new StringBuilder();
            fb.append("Mailing Lists : ").append(Joiner.on(", ").join(t.mailingLists)).append('\n');
            fb.append("Type          : ").append(t.accountType).append('\n');
            fb.append("Can Admin     : ").append(t.canAdmin ? "Y":"").append('\n');
            fb.append("Can Fork      : ").append(t.canFork ? "Y":"").append('\n');
            fb.append("Can Create    : ").append(t.canCreate ? "Y":"").append('\n');
            fb.append("Pre-Receive   : ").append(Joiner.on(", ").join(t.preReceiveScripts)).append('\n');
            fb.append("Post-Receive  : ").append(Joiner.on(", ").join(t.postReceiveScripts)).append('\n');
            String fields = fb.toString();
            // members
            String members;
            if (t.users.size() == 0) {
                members = FlipTable.EMPTY;
            } else {
                IGitblit gitblit = getContext().getGitblit();
                String[] headers = { "Username", "Display Name" };
                Object [][] data = new Object[t.users.size()][];
                int i = 0;
                for (String username : t.users) {
                    UserModel u = gitblit.getUserModel(username);
                    data[i] = new Object[] { username,  u == null ? null : u.displayName };
                    i++;
                }
                members = FlipTable.of(headers, data, Borders.COLS);
            }
            // permissions
            List<RegistrantAccessPermission> perms = t.getRepositoryPermissions();
            String permissions;
            if (perms.isEmpty()) {
                permissions = FlipTable.EMPTY;
            } else {
                String[] pheaders = { "Repository", "Permission", "Type" };
                Object [][] pdata = new Object[perms.size()][];
                for (int i = 0; i < perms.size(); i++) {
                    RegistrantAccessPermission ap = perms.get(i);
                    pdata[i] = new Object[] { ap.registrant, ap.permission, ap.permissionType };
                }
                permissions = FlipTable.of(pheaders, pdata, Borders.COLS);
            }
            // assemble team table
            String [] headers = new String[] { t.name };
            String[][] data = new String[6][];
            data[0] = new String [] { "FIELDS" };
            data[1] = new String [] { fields };
            data[2] = new String [] { "MEMBERS" };
            data[3] = new String [] { members };
            data[4] = new String [] { "PERMISSIONS" };
            data[5] = new String [] { permissions };
            stdout.println(FlipTable.of(headers, data));
        }
    }
    @CommandMetaData(name = "list", aliases= { "ls" }, description = "List teams")
    @UsageExamples(examples = {
        @UsageExample(syntax = "${cmd}", description = "List teams as a table"),
        @UsageExample(syntax = "${cmd} j.*", description = "List all teams that start with 'j'"),
    })
    public static class ListTeams extends ListFilterCommand<TeamModel> {
        @Override
        protected List<TeamModel> getItems() {
            IGitblit gitblit = getContext().getGitblit();
            List<TeamModel> teams = gitblit.getAllTeams();
            return teams;
        }
        @Override
        protected boolean matches(String filter, TeamModel t) {
            return t.name.matches(filter);
        }
        @Override
        protected void asTable(List<TeamModel> list) {
            String[] headers = { "Name", "Members", "Type", "Create?", "Fork?"};
            Object[][] data = new Object[list.size()][];
            for (int i = 0; i < list.size(); i++) {
                TeamModel t = list.get(i);
                data[i] = new Object[] {
                        (t.canAdmin ? "*" : " ") + t.name,
                        t.users.isEmpty() ? "" : t.users.size(),
                        t.accountType + (t.canAdmin ? ",admin":""),
                        (t.canAdmin || t.canCreate) ? "Y":"",
                        (t.canAdmin || t.canFork) ? "Y" : ""};
            }
            stdout.println(FlipTable.of(headers, data, Borders.BODY_HCOLS));
        }
        @Override
        protected void asTabbed(List<TeamModel> teams) {
            if (verbose) {
                for (TeamModel t : teams) {
                    outTabbed(
                            t.name,
                            t.users.isEmpty() ? "" : t.users.size(),
                            t.accountType + (t.canAdmin ? ",admin":""),
                            (t.canAdmin || t.canCreate) ? "Y":"",
                            (t.canAdmin || t.canFork) ? "Y" : "");
                }
            } else {
                for (TeamModel u : teams) {
                    outTabbed((u.canAdmin ? "*" : " ") + u.name);
                }
            }
        }
    }
}