James Moger
2011-12-13 f08aab5c5e632431635e73b47b6096dc47243755
Teams JSON-RPC support
3 files added
13 files modified
1393 ■■■■■ changed files
docs/02_rpc.mkd 65 ●●●●● patch | view | raw | blame | history
src/com/gitblit/Constants.java 10 ●●●●● patch | view | raw | blame | history
src/com/gitblit/RpcFilter.java 2 ●●●●● patch | view | raw | blame | history
src/com/gitblit/RpcServlet.java 55 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/EditRepositoryDialog.java 29 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/EditTeamDialog.java 269 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/EditUserDialog.java 64 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitClient.java 93 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitPanel.java 44 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/RepositoriesPanel.java 22 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/TeamsPanel.java 378 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/TeamsTableModel.java 105 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/UsersPanel.java 36 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/UsersTableModel.java 13 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/RpcUtils.java 130 ●●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/RpcTests.java 78 ●●●●● patch | view | raw | blame | history
docs/02_rpc.mkd
@@ -57,32 +57,49 @@
## JSON Remote Procedure Call (RPC) Interface
### RPC Protocol Versions
<table>
<tr><th colspan='2'>url parameters</th><th rowspan='2'>required<br/>user<br/>permission</th><th colspan='2'>json</th></tr>
<tbody>
<tr><th>Release</th><th>Protocol Version</th></tr>
<tr><td>Gitblit v0.7.0</td><td>1 (inferred version)</td></tr>
<tr><td>Gitblit v0.8.0</td><td>2</td></tr>
</tbody>
</table>
### RPC Request and Response Types
<table>
<tr><th colspan='2'>url parameters</th><th rowspan='2'>required<br/>user<br/>permission</th><th rowspan='2'>protocol<br/>version</th><th colspan='2'>json</th></tr>
<tr><th>req=</th><th>name=</th><th>post body</th><th>response body</th></tr>
<tr><td colspan='5'><em>web.enableRpcServlet=true</em></td></tr>
<tr><td>LIST_REPOSITORIES</td><td>-</td><td>-</td><td>-</td><td>Map&lt;String, RepositoryModel&gt;</td></tr>
<tr><td>LIST_BRANCHES</td><td>-</td><td>-</td><td>-</td><td>Map&lt;String, List&lt;String&gt;&gt;</td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>-</em></td><td>-</td><td>ServerSettings (basic keys)</td></tr>
<tr><td colspan='5'><em>web.enableRpcManagement=true</em></td></tr>
<tr><td>CREATE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>RepositoryModel</td><td>-</td></tr>
<tr><td>EDIT_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>RepositoryModel</td><td>-</td></tr>
<tr><td>DELETE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>-</td><td>-</td></tr>
<tr><td>LIST_USERS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;UserModel&gt;</td></tr>
<tr><td>CREATE_USER</td><td>user name</td><td><em>admin</em></td><td>UserModel</td><td>-</td></tr>
<tr><td>EDIT_USER</td><td>user name</td><td><em>admin</em></td><td>UserModel</td><td>-</td></tr>
<tr><td>DELETE_USER</td><td>user name</td><td><em>admin</em></td><td>-</td><td>-</td></tr>
<tr><td>LIST_REPOSITORY_MEMBERS</td><td>repository name</td><td><em>admin</em></td><td>-</td><td>List&lt;String&gt;</td></tr>
<tr><td>SET_REPOSITORY_MEMBERS</td><td>repository name</td><td><em>admin</em></td><td>List&lt;String&gt;</td><td>-</td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>admin</em></td><td>-</td><td>ServerSettings (management keys)</td></tr>
<tr><td colspan='5'><em>web.enableRpcAdministration=true</em></td></tr>
<tr><td>LIST_FEDERATION_REGISTRATIONS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;FederationModel&gt;</td></tr>
<tr><td>LIST_FEDERATION_RESULTS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;FederationModel&gt;</td></tr>
<tr><td>LIST_FEDERATION_PROPOSALS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;FederationProposal&gt;</td></tr>
<tr><td>LIST_FEDERATION_SETS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;FederationSet&gt;</td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>admin</em></td><td>-</td><td>ServerSettings (all keys)</td></tr>
<tr><td>EDIT_SETTINGS</td><td>-</td><td><em>admin</em></td><td>Map&lt;String, String&gt;</td><td>-</td></tr>
<tr><td>LIST_STATUS</td><td>-</td><td><em>admin</em></td><td>-</td><td>ServerStatus (see example below)</td></tr>
<tr><td colspan='6'><em>web.enableRpcServlet=true</em></td></tr>
<tr><td>GET_PROTOCOL</td><td>-</td><td>-</td><td>2</td><td>-</td><td>Integer</td></tr>
<tr><td>LIST_REPOSITORIES</td><td>-</td><td>-</td><td>1</td><td>-</td><td>Map&lt;String, RepositoryModel&gt;</td></tr>
<tr><td>LIST_BRANCHES</td><td>-</td><td>-</td><td>1</td><td>-</td><td>Map&lt;String, List&lt;String&gt;&gt;</td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>-</em></td><td>1</td><td>-</td><td>ServerSettings (basic keys)</td></tr>
<tr><td colspan='6'><em>web.enableRpcManagement=true</em></td></tr>
<tr><td>CREATE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>RepositoryModel</td><td>-</td></tr>
<tr><td>EDIT_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>RepositoryModel</td><td>-</td></tr>
<tr><td>DELETE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>-</td><td>-</td></tr>
<tr><td>LIST_USERS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>List&lt;UserModel&gt;</td></tr>
<tr><td>CREATE_USER</td><td>user name</td><td><em>admin</em></td><td>1</td><td>UserModel</td><td>-</td></tr>
<tr><td>EDIT_USER</td><td>user name</td><td><em>admin</em></td><td>1</td><td>UserModel</td><td>-</td></tr>
<tr><td>DELETE_USER</td><td>user name</td><td><em>admin</em></td><td>1</td><td>-</td><td>-</td></tr>
<tr><td>LIST_TEAMS</td><td>-</td><td><em>admin</em></td><td>2</td><td>-</td><td>List&lt;TeamModel&gt;</td></tr>
<tr><td>CREATE_TEAM</td><td>team name</td><td><em>admin</em></td><td>2</td><td>TeamModel</td><td>-</td></tr>
<tr><td>EDIT_TEAM</td><td>team name</td><td><em>admin</em></td><td>2</td><td>TeamModel</td><td>-</td></tr>
<tr><td>DELETE_TEAM</td><td>team name</td><td><em>admin</em></td><td>2</td><td>-</td><td>-</td></tr>
<tr><td>LIST_REPOSITORY_MEMBERS</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>-</td><td>List&lt;String&gt;</td></tr>
<tr><td>SET_REPOSITORY_MEMBERS</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>List&lt;String&gt;</td><td>-</td></tr>
<tr><td>LIST_REPOSITORY_TEAMS</td><td>repository name</td><td><em>admin</em></td><td>2</td><td>-</td><td>List&lt;String&gt;</td></tr>
<tr><td>SET_REPOSITORY_TEAMS</td><td>repository name</td><td><em>admin</em></td><td>2</td><td>List&lt;String&gt;</td><td>-</td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>ServerSettings (management keys)</td></tr>
<tr><td colspan='6'><em>web.enableRpcAdministration=true</em></td></tr>
<tr><td>LIST_FEDERATION_REGISTRATIONS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>List&lt;FederationModel&gt;</td></tr>
<tr><td>LIST_FEDERATION_RESULTS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>List&lt;FederationModel&gt;</td></tr>
<tr><td>LIST_FEDERATION_PROPOSALS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>List&lt;FederationProposal&gt;</td></tr>
<tr><td>LIST_FEDERATION_SETS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>List&lt;FederationSet&gt;</td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>ServerSettings (all keys)</td></tr>
<tr><td>EDIT_SETTINGS</td><td>-</td><td><em>admin</em></td><td>1</td><td>Map&lt;String, String&gt;</td><td>-</td></tr>
<tr><td>LIST_STATUS</td><td>-</td><td><em>admin</em></td><td>1</td><td>-</td><td>ServerStatus (see example below)</td></tr>
</table>
### RPC/HTTP Response Codes
src/com/gitblit/Constants.java
@@ -203,10 +203,12 @@
    public static enum RpcRequest {
        // Order is important here.  anything above LIST_SETTINGS requires
        // administrator privileges and web.allowRpcManagement.
        LIST_REPOSITORIES, LIST_BRANCHES, LIST_SETTINGS, CREATE_REPOSITORY, EDIT_REPOSITORY,
        DELETE_REPOSITORY, LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER,
        LIST_REPOSITORY_MEMBERS, SET_REPOSITORY_MEMBERS, LIST_FEDERATION_REGISTRATIONS,
        LIST_FEDERATION_RESULTS, LIST_FEDERATION_PROPOSALS, LIST_FEDERATION_SETS,
        GET_PROTOCOL, LIST_REPOSITORIES, LIST_BRANCHES, LIST_SETTINGS,
        CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY,
        LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER,
        LIST_TEAMS, CREATE_TEAM, EDIT_TEAM, DELETE_TEAM,
        LIST_REPOSITORY_MEMBERS, SET_REPOSITORY_MEMBERS, LIST_REPOSITORY_TEAMS, SET_REPOSITORY_TEAMS,
        LIST_FEDERATION_REGISTRATIONS, LIST_FEDERATION_RESULTS, LIST_FEDERATION_PROPOSALS, LIST_FEDERATION_SETS,
        EDIT_SETTINGS, LIST_STATUS;
        public static RpcRequest fromName(String name) {
src/com/gitblit/RpcFilter.java
@@ -135,6 +135,8 @@
    private boolean canAccess(UserModel user, RpcRequest requestType) {
        switch (requestType) {
        case GET_PROTOCOL:
            return true;
        case LIST_REPOSITORIES:
            return true;
        default:
src/com/gitblit/RpcServlet.java
@@ -33,6 +33,7 @@
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.ServerSettings;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.JGitUtils;
@@ -47,6 +48,8 @@
public class RpcServlet extends JsonServlet {
    private static final long serialVersionUID = 1L;
    public static final int PROTOCOL_VERSION = 2;
    public RpcServlet() {
        super();
@@ -77,7 +80,10 @@
                && GitBlit.getBoolean(Keys.web.enableRpcAdministration, false);
        Object result = null;
        if (RpcRequest.LIST_REPOSITORIES.equals(reqType)) {
        if (RpcRequest.GET_PROTOCOL.equals(reqType)) {
            // Return the protocol version
            result = PROTOCOL_VERSION;
        } else if (RpcRequest.LIST_REPOSITORIES.equals(reqType)) {
            // Determine the Gitblit clone url
            String gitblitUrl = HttpUtils.getGitblitURL(request);
            StringBuilder sb = new StringBuilder();
@@ -128,6 +134,14 @@
                users.add(GitBlit.self().getUserModel(name));
            }
            result = users;
        } else if (RpcRequest.LIST_TEAMS.equals(reqType)) {
            // list teams
            List<String> names = GitBlit.self().getAllTeamnames();
            List<TeamModel> teams = new ArrayList<TeamModel>();
            for (String name : names) {
                teams.add(GitBlit.self().getTeamModel(name));
            }
            result = teams;
        } else if (RpcRequest.CREATE_REPOSITORY.equals(reqType)) {
            // create repository
            RepositoryModel model = deserialize(request, response, RepositoryModel.class);
@@ -180,6 +194,33 @@
            if (!GitBlit.self().deleteUser(model.username)) {
                response.setStatus(failureCode);
            }
        } else if (RpcRequest.CREATE_TEAM.equals(reqType)) {
            // create team
            TeamModel model = deserialize(request, response, TeamModel.class);
            try {
                GitBlit.self().updateTeamModel(model.name, model, true);
            } catch (GitBlitException e) {
                response.setStatus(failureCode);
            }
        } else if (RpcRequest.EDIT_TEAM.equals(reqType)) {
            // edit team
            TeamModel model = deserialize(request, response, TeamModel.class);
            // name parameter specifies original team name in event of rename
            String teamname = objectName;
            if (teamname == null) {
                teamname = model.name;
            }
            try {
                GitBlit.self().updateTeamModel(teamname, model, false);
            } catch (GitBlitException e) {
                response.setStatus(failureCode);
            }
        } else if (RpcRequest.DELETE_TEAM.equals(reqType)) {
            // delete team
            TeamModel model = deserialize(request, response, TeamModel.class);
            if (!GitBlit.self().deleteTeam(model.name)) {
                response.setStatus(failureCode);
            }
        } else if (RpcRequest.LIST_REPOSITORY_MEMBERS.equals(reqType)) {
            // get repository members
            RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
@@ -192,6 +233,18 @@
            if (!GitBlit.self().setRepositoryUsers(model, users)) {
                response.setStatus(failureCode);
            }
        } else if (RpcRequest.LIST_REPOSITORY_TEAMS.equals(reqType)) {
            // get repository teams
            RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
            result = GitBlit.self().getRepositoryTeams(model);
        } else if (RpcRequest.SET_REPOSITORY_TEAMS.equals(reqType)) {
            // update repository team access list
            RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
            Collection<String> names = deserialize(request, response, RpcUtils.NAMES_TYPE);
            List<String> teams = new ArrayList<String>(names);
            if (!GitBlit.self().setRepositoryTeams(model, teams)) {
                response.setStatus(failureCode);
            }
        } else if (RpcRequest.LIST_FEDERATION_REGISTRATIONS.equals(reqType)) {
            // return the list of federation registrations
            if (allowAdmin) {
src/com/gitblit/client/EditRepositoryDialog.java
@@ -52,6 +52,7 @@
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
import com.gitblit.utils.StringUtils;
/**
@@ -99,21 +100,23 @@
    private JPalette<String> setsPalette;
    private JPalette<String> teamsPalette;
    private Set<String> repositoryNames;
    public EditRepositoryDialog() {
        this(new RepositoryModel());
    public EditRepositoryDialog(int protocolVersion) {
        this(protocolVersion, new RepositoryModel());
        this.isCreate = true;
        setTitle(Translation.get("gb.newRepository"));
    }
    public EditRepositoryDialog(RepositoryModel aRepository) {
    public EditRepositoryDialog(int protocolVersion, RepositoryModel aRepository) {
        super();
        this.repositoryName = aRepository.name;
        this.repository = new RepositoryModel();
        this.repositoryNames = new HashSet<String>();
        this.isCreate = false;
        initialize(aRepository);
        initialize(protocolVersion, aRepository);
        setModal(true);
        setResizable(false);
        setTitle(Translation.get("gb.edit") + ": " + aRepository.name);
@@ -132,7 +135,7 @@
        return rootPane;
    }
    private void initialize(RepositoryModel anRepository) {
    private void initialize(int protocolVersion, RepositoryModel anRepository) {
        nameField = new JTextField(anRepository.name == null ? "" : anRepository.name, 35);
        descriptionField = new JTextField(anRepository.description == null ? ""
                : anRepository.description, 35);
@@ -195,6 +198,11 @@
        accessPanel.add(newFieldPanel(Translation.get("gb.permittedUsers"), usersPalette),
                BorderLayout.CENTER);
        teamsPalette = new JPalette<String>();
        JPanel teamsPanel = new JPanel(new BorderLayout(5, 5));
        teamsPanel.add(newFieldPanel(Translation.get("gb.permittedTeams"), teamsPalette),
                BorderLayout.CENTER);
        setsPalette = new JPalette<String>();
        JPanel federationPanel = new JPanel(new BorderLayout(5, 5));
        federationPanel.add(
@@ -206,6 +214,9 @@
        JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP);
        panel.addTab(Translation.get("gb.general"), fieldsPanel);
        panel.addTab(Translation.get("gb.accessRestriction"), accessPanel);
        if (protocolVersion >= 2) {
            panel.addTab(Translation.get("gb.teams"), teamsPanel);
        }
        panel.addTab(Translation.get("gb.federation"), federationPanel);
        JButton createButton = new JButton(Translation.get("gb.save"));
@@ -359,6 +370,10 @@
        usersPalette.setObjects(all, selected);
    }
    public void setTeams(List<String> all, List<String> selected) {
        teamsPalette.setObjects(all, selected);
    }
    public void setRepositories(List<RepositoryModel> repositories) {
        repositoryNames.clear();
        for (RepositoryModel repository : repositories) {
@@ -385,6 +400,10 @@
        return usersPalette.getSelections();
    }
    public List<String> getPermittedTeams() {
        return teamsPalette.getSelections();
    }
    /**
     * ListCellRenderer to display descriptive text about the access
     * restriction.
src/com/gitblit/client/EditTeamDialog.java
New file
@@ -0,0 +1,269 @@
/*
 * Copyright 2011 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.client;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.ServerSettings;
import com.gitblit.models.TeamModel;
import com.gitblit.utils.StringUtils;
public class EditTeamDialog extends JDialog {
    private static final long serialVersionUID = 1L;
    private final String teamname;
    private final TeamModel team;
    private final ServerSettings settings;
    private boolean isCreate;
    private boolean canceled = true;
    private JTextField teamnameField;
    private JPalette<String> repositoryPalette;
    private JPalette<String> userPalette;
    private Set<String> teamnames;
    public EditTeamDialog(int protocolVersion, ServerSettings settings) {
        this(protocolVersion, new TeamModel(""), settings);
        this.isCreate = true;
        setTitle(Translation.get("gb.newTeam"));
    }
    public EditTeamDialog(int protocolVersion, TeamModel aTeam, ServerSettings settings) {
        super();
        this.teamname = aTeam.name;
        this.team = new TeamModel("");
        this.settings = settings;
        this.teamnames = new HashSet<String>();
        this.isCreate = false;
        initialize(protocolVersion, aTeam);
        setModal(true);
        setTitle(Translation.get("gb.edit") + ": " + aTeam.name);
        setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
    }
    @Override
    protected JRootPane createRootPane() {
        KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
        JRootPane rootPane = new JRootPane();
        rootPane.registerKeyboardAction(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                setVisible(false);
            }
        }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
        return rootPane;
    }
    private void initialize(int protocolVersion, TeamModel aTeam) {
        teamnameField = new JTextField(aTeam.name == null ? "" : aTeam.name, 25);
        JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));
        fieldsPanel.add(newFieldPanel(Translation.get("gb.teamName"), teamnameField));
        final Insets _insets = new Insets(5, 5, 5, 5);
        repositoryPalette = new JPalette<String>();
        userPalette = new JPalette<String>();
        JPanel fieldsPanelTop = new JPanel(new BorderLayout());
        fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);
        JPanel repositoriesPanel = new JPanel(new BorderLayout()) {
            private static final long serialVersionUID = 1L;
            public Insets getInsets() {
                return _insets;
            }
        };
        repositoriesPanel.add(repositoryPalette, BorderLayout.CENTER);
        JPanel usersPanel = new JPanel(new BorderLayout()) {
            private static final long serialVersionUID = 1L;
            public Insets getInsets() {
                return _insets;
            }
        };
        usersPanel.add(userPalette, BorderLayout.CENTER);
        JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP);
        panel.addTab(Translation.get("gb.general"), fieldsPanelTop);
        panel.addTab(Translation.get("gb.teamMembers"), usersPanel);
        panel.addTab(Translation.get("gb.restrictedRepositories"), repositoriesPanel);
        JButton createButton = new JButton(Translation.get("gb.save"));
        createButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (validateFields()) {
                    canceled = false;
                    setVisible(false);
                }
            }
        });
        JButton cancelButton = new JButton(Translation.get("gb.cancel"));
        cancelButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                canceled = true;
                setVisible(false);
            }
        });
        JPanel controls = new JPanel();
        controls.add(cancelButton);
        controls.add(createButton);
        JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) {
            private static final long serialVersionUID = 1L;
            @Override
            public Insets getInsets() {
                return _insets;
            }
        };
        centerPanel.add(panel, BorderLayout.CENTER);
        centerPanel.add(controls, BorderLayout.SOUTH);
        getContentPane().setLayout(new BorderLayout(5, 5));
        getContentPane().add(centerPanel, BorderLayout.CENTER);
        pack();
    }
    private JPanel newFieldPanel(String label, JComponent comp) {
        JLabel fieldLabel = new JLabel(label);
        fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD));
        fieldLabel.setPreferredSize(new Dimension(150, 20));
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
        panel.add(fieldLabel);
        panel.add(comp);
        return panel;
    }
    private boolean validateFields() {
        String tname = teamnameField.getText();
        if (StringUtils.isEmpty(tname)) {
            error("Please enter a team name!");
            return false;
        }
        boolean rename = false;
        // verify teamname uniqueness on create
        if (isCreate) {
            if (teamnames.contains(tname.toLowerCase())) {
                error(MessageFormat.format("Team name ''{0}'' is unavailable.", tname));
                return false;
            }
        } else {
            // check rename collision
            rename = !StringUtils.isEmpty(teamname) && !teamname.equalsIgnoreCase(tname);
            if (rename) {
                if (teamnames.contains(tname.toLowerCase())) {
                    error(MessageFormat.format(
                            "Failed to rename ''{0}'' because ''{1}'' already exists.", teamname,
                            tname));
                    return false;
                }
            }
        }
        team.name = tname;
        team.repositories.clear();
        team.repositories.addAll(repositoryPalette.getSelections());
        team.users.clear();
        team.users.addAll(userPalette.getSelections());
        return true;
    }
    private void error(String message) {
        JOptionPane.showMessageDialog(EditTeamDialog.this, message, Translation.get("gb.error"),
                JOptionPane.ERROR_MESSAGE);
    }
    public void setTeams(List<TeamModel> teams) {
        teamnames.clear();
        for (TeamModel team : teams) {
            teamnames.add(team.name.toLowerCase());
        }
    }
    public void setRepositories(List<RepositoryModel> repositories, List<String> selected) {
        List<String> restricted = new ArrayList<String>();
        for (RepositoryModel repo : repositories) {
            if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
                restricted.add(repo.name);
            }
        }
        StringUtils.sortRepositorynames(restricted);
        if (selected != null) {
            StringUtils.sortRepositorynames(selected);
        }
        repositoryPalette.setObjects(restricted, selected);
    }
    public void setUsers(List<String> users, List<String> selected) {
        Collections.sort(users);
        if (selected != null) {
            Collections.sort(selected);
        }
        userPalette.setObjects(users, selected);
    }
    public TeamModel getTeam() {
        if (canceled) {
            return null;
        }
        return team;
    }
}
src/com/gitblit/client/EditUserDialog.java
@@ -26,6 +26,7 @@
import java.awt.event.KeyEvent;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -40,6 +41,7 @@
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRootPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
@@ -47,6 +49,7 @@
import com.gitblit.Keys;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.ServerSettings;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
@@ -76,22 +79,24 @@
    private JPalette<String> repositoryPalette;
    private JPalette<TeamModel> teamsPalette;
    private Set<String> usernames;
    public EditUserDialog(ServerSettings settings) {
        this(new UserModel(""), settings);
    public EditUserDialog(int protocolVersion, ServerSettings settings) {
        this(protocolVersion, new UserModel(""), settings);
        this.isCreate = true;
        setTitle(Translation.get("gb.newUser"));
    }
    public EditUserDialog(UserModel anUser, ServerSettings settings) {
    public EditUserDialog(int protocolVersion, UserModel anUser, ServerSettings settings) {
        super();
        this.username = anUser.username;
        this.user = new UserModel("");
        this.settings = settings;
        this.usernames = new HashSet<String>();
        this.isCreate = false;
        initialize(anUser);
        initialize(protocolVersion, anUser);
        setModal(true);
        setTitle(Translation.get("gb.edit") + ": " + anUser.username);
        setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
@@ -109,7 +114,7 @@
        return rootPane;
    }
    private void initialize(UserModel anUser) {
    private void initialize(int protocolVersion, UserModel anUser) {
        usernameField = new JTextField(anUser.username == null ? "" : anUser.username, 25);
        passwordField = new JPasswordField(anUser.password == null ? "" : anUser.password, 25);
        confirmPasswordField = new JPasswordField(anUser.password == null ? "" : anUser.password,
@@ -127,11 +132,40 @@
        fieldsPanel.add(newFieldPanel(Translation.get("gb.excludeFromFederation"),
                notFederatedCheckbox));
        final Insets _insets = new Insets(5, 5, 5, 5);
        repositoryPalette = new JPalette<String>();
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(fieldsPanel, BorderLayout.NORTH);
        panel.add(newFieldPanel(Translation.get("gb.restrictedRepositories"), repositoryPalette),
                BorderLayout.CENTER);
        teamsPalette = new JPalette<TeamModel>();
        JPanel fieldsPanelTop = new JPanel(new BorderLayout());
        fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);
        JPanel repositoriesPanel = new JPanel(new BorderLayout()) {
            private static final long serialVersionUID = 1L;
            public Insets getInsets() {
                return _insets;
            }
        };
        repositoriesPanel.add(repositoryPalette, BorderLayout.CENTER);
        JPanel teamsPanel = new JPanel(new BorderLayout()) {
            private static final long serialVersionUID = 1L;
            public Insets getInsets() {
                return _insets;
            }
        };
        teamsPanel.add(teamsPalette, BorderLayout.CENTER);
        JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP);
        panel.addTab(Translation.get("gb.general"), fieldsPanelTop);
        if (protocolVersion > 1) {
            panel.addTab(Translation.get("gb.teamMemberships"), teamsPanel);
        }
        panel.addTab(Translation.get("gb.restrictedRepositories"), repositoriesPanel);
        JButton createButton = new JButton(Translation.get("gb.save"));
        createButton.addActionListener(new ActionListener() {
@@ -155,7 +189,6 @@
        controls.add(cancelButton);
        controls.add(createButton);
        final Insets _insets = new Insets(5, 5, 5, 5);
        JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) {
            private static final long serialVersionUID = 1L;
@@ -259,6 +292,9 @@
        user.repositories.clear();
        user.repositories.addAll(repositoryPalette.getSelections());
        user.teams.clear();
        user.teams.addAll(teamsPalette.getSelections());
        return true;
    }
@@ -288,6 +324,14 @@
        repositoryPalette.setObjects(restricted, selected);
    }
    public void setTeams(List<TeamModel> teams, List<TeamModel> selected) {
        Collections.sort(teams);
        if (selected != null) {
            Collections.sort(selected);
        }
        teamsPalette.setObjects(teams, selected);
    }
    public UserModel getUser() {
        if (canceled) {
            return null;
src/com/gitblit/client/GitblitClient.java
@@ -37,6 +37,7 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.ServerSettings;
import com.gitblit.models.ServerStatus;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.RpcUtils;
import com.gitblit.utils.StringUtils;
@@ -63,6 +64,8 @@
    private final char[] password;
    private volatile int protocolVersion;
    private volatile boolean allowManagement;
    private volatile boolean allowAdministration;
@@ -72,6 +75,8 @@
    private final List<RepositoryModel> allRepositories;
    private final List<UserModel> allUsers;
    private final List<TeamModel> allTeams;
    private final List<FederationModel> federationRegistrations;
@@ -90,6 +95,7 @@
        this.password = reg.password;
        this.allUsers = new ArrayList<UserModel>();
        this.allTeams = new ArrayList<TeamModel>();
        this.allRepositories = new ArrayList<RepositoryModel>();
        this.federationRegistrations = new ArrayList<FederationModel>();
        this.availableFeeds = new ArrayList<FeedModel>();
@@ -98,6 +104,7 @@
    }
    public void login() throws IOException {
        protocolVersion = RpcUtils.getProtocolVersion(url, account, password);
        refreshSettings();
        refreshAvailableFeeds();
        refreshRepositories();
@@ -107,6 +114,9 @@
            // credentials may not have administrator access
            // or server may have disabled rpc management
            refreshUsers();
            if (protocolVersion > 1) {
                refreshTeams();
            }
            allowManagement = true;
        } catch (UnauthorizedException e) {
        } catch (ForbiddenException e) {
@@ -128,6 +138,10 @@
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public int getProtocolVersion() {
        return protocolVersion;
    }
    public boolean allowManagement() {
@@ -198,6 +212,13 @@
        return allUsers;
    }
    public List<TeamModel> refreshTeams() throws IOException {
        List<TeamModel> teams = RpcUtils.getTeams(url, account, password);
        allTeams.clear();
        allTeams.addAll(teams);
        return allTeams;
    }
    public ServerSettings refreshSettings() throws IOException {
        settings = RpcUtils.getSettings(url, account, password);
        return settings;
@@ -253,8 +274,8 @@
            for (FeedModel feed : reg.feeds) {
                feed.lastRefreshDate = feed.currentRefreshDate;
                feed.currentRefreshDate = new Date();
                List<FeedEntryModel> entries = SyndicationUtils.readFeed(url,
                        feed.repository, feed.branch, -1, page, account, password);
                List<FeedEntryModel> entries = SyndicationUtils.readFeed(url, feed.repository,
                        feed.branch, -1, page, account, password);
                allEntries.addAll(entries);
            }
        }
@@ -301,8 +322,8 @@
        return syndicatedEntries;
    }
    public List<FeedEntryModel> log(String repository, String branch, int numberOfEntries,
            int page) throws IOException {
    public List<FeedEntryModel> log(String repository, String branch, int numberOfEntries, int page)
            throws IOException {
        return SyndicationUtils.readFeed(url, repository, branch, numberOfEntries, page, account,
                password);
    }
@@ -343,6 +364,29 @@
        return usernames;
    }
    public List<TeamModel> getTeams() {
        return allTeams;
    }
    public List<String> getTeamnames() {
        List<String> teamnames = new ArrayList<String>();
        for (TeamModel team : this.allTeams) {
            teamnames.add(team.name);
        }
        Collections.sort(teamnames);
        return teamnames;
    }
    public List<String> getPermittedTeamnames(RepositoryModel repository) {
        List<String> teamnames = new ArrayList<String>();
        for (TeamModel team : this.allTeams) {
            if (team.repositories.contains(repository.name)) {
                teamnames.add(team.name);
            }
        }
        return teamnames;
    }
    public List<String> getFederationSets() {
        return settings.get(Keys.federation.sets).getStrings();
    }
@@ -353,11 +397,21 @@
    public boolean createRepository(RepositoryModel repository, List<String> permittedUsers)
            throws IOException {
        return createRepository(repository, permittedUsers, null);
    }
    public boolean createRepository(RepositoryModel repository, List<String> permittedUsers,
            List<String> permittedTeams) throws IOException {
        boolean success = true;
        success &= RpcUtils.createRepository(repository, url, account, password);
        if (permittedUsers.size() > 0) {
        if (permittedUsers != null && permittedUsers.size() > 0) {
            // if new repository has named members, set them
            success &= RpcUtils.setRepositoryMembers(repository, permittedUsers, url, account,
                    password);
        }
        if (permittedTeams != null && permittedTeams.size() > 0) {
            // if new repository has named teams, set them
            success &= RpcUtils.setRepositoryTeams(repository, permittedTeams, url, account,
                    password);
        }
        return success;
@@ -365,11 +419,22 @@
    public boolean updateRepository(String name, RepositoryModel repository,
            List<String> permittedUsers) throws IOException {
        return updateRepository(name, repository, permittedUsers, null);
    }
    public boolean updateRepository(String name, RepositoryModel repository,
            List<String> permittedUsers, List<String> permittedTeams) throws IOException {
        boolean success = true;
        success &= RpcUtils.updateRepository(name, repository, url, account, password);
        // always set the repository members
        success &= RpcUtils
                .setRepositoryMembers(repository, permittedUsers, url, account, password);
        // set the repository members
        if (permittedUsers != null) {
            success &= RpcUtils.setRepositoryMembers(repository, permittedUsers, url, account,
                    password);
        }
        if (permittedTeams != null) {
            success &= RpcUtils.setRepositoryTeams(repository, permittedTeams, url, account,
                    password);
        }
        return success;
    }
@@ -389,6 +454,18 @@
        return RpcUtils.deleteUser(user, url, account, password);
    }
    public boolean createTeam(TeamModel team) throws IOException {
        return RpcUtils.createTeam(team, url, account, password);
    }
    public boolean updateTeam(String name, TeamModel team) throws IOException {
        return RpcUtils.updateTeam(name, team, url, account, password);
    }
    public boolean deleteTeam(TeamModel team) throws IOException {
        return RpcUtils.deleteTeam(team, url, account, password);
    }
    public boolean updateSettings(Map<String, String> newSettings) throws IOException {
        return RpcUtils.updateSettings(newSettings, url, account, password);
    }
src/com/gitblit/client/GitblitPanel.java
@@ -51,6 +51,8 @@
    private UsersPanel usersPanel;
    private TeamsPanel teamsPanel;
    private SettingsPanel settingsPanel;
    private StatusPanel statusPanel;
@@ -62,6 +64,7 @@
        tabs = new JTabbedPane(JTabbedPane.BOTTOM);
        tabs.addTab(Translation.get("gb.repositories"), createRepositoriesPanel());
        tabs.addTab(Translation.get("gb.activity"), createFeedsPanel());
        tabs.addTab(Translation.get("gb.teams"), createTeamsPanel());
        tabs.addTab(Translation.get("gb.users"), createUsersPanel());
        tabs.addTab(Translation.get("gb.settings"), createSettingsPanel());
        tabs.addTab(Translation.get("gb.status"), createStatusPanel());
@@ -90,6 +93,11 @@
                usersPanel.updateTable(false);
            }
            @Override
            protected void updateTeamsTable() {
                teamsPanel.updateTable(false);
            }
        };
        return repositoriesPanel;
    }
@@ -107,8 +115,29 @@
    }
    private JPanel createUsersPanel() {
        usersPanel = new UsersPanel(gitblit);
        usersPanel = new UsersPanel(gitblit) {
            private static final long serialVersionUID = 1L;
            @Override
            protected void updateTeamsTable() {
                teamsPanel.updateTable(false);
            }
        };
        return usersPanel;
    }
    private JPanel createTeamsPanel() {
        teamsPanel = new TeamsPanel(gitblit) {
            private static final long serialVersionUID = 1L;
            @Override
            protected void updateUsersTable() {
                usersPanel.updateTable(false);
            }
        };
        return teamsPanel;
    }
    private JPanel createSettingsPanel() {
@@ -128,6 +157,19 @@
        feedsPanel.updateTable(true);
        if (gitblit.allowManagement()) {
            if (gitblit.getProtocolVersion() >= 2) {
                // refresh teams panel
                teamsPanel.updateTable(false);
            } else {
                // remove teams panel
                String teams = Translation.get("gb.teams");
                for (int i = 0; i < tabs.getTabCount(); i++) {
                    if (teams.equals(tabs.getTitleAt(i))) {
                        tabs.removeTabAt(i);
                        break;
                    }
                }
            }
            usersPanel.updateTable(false);
        } else {
            // user does not have administrator privileges
src/com/gitblit/client/RepositoriesPanel.java
@@ -278,6 +278,8 @@
    protected abstract void updateUsersTable();
    protected abstract void updateTeamsTable();
    protected void disableManagement() {
        createRepository.setVisible(false);
        editRepository.setVisible(false);
@@ -349,14 +351,16 @@
     * 
     */
    protected void createRepository() {
        EditRepositoryDialog dialog = new EditRepositoryDialog();
        EditRepositoryDialog dialog = new EditRepositoryDialog(gitblit.getProtocolVersion());
        dialog.setLocationRelativeTo(RepositoriesPanel.this);
        dialog.setUsers(null, gitblit.getUsernames(), null);
        dialog.setTeams(gitblit.getTeamnames(), null);
        dialog.setRepositories(gitblit.getRepositories());
        dialog.setFederationSets(gitblit.getFederationSets(), null);
        dialog.setVisible(true);
        final RepositoryModel newRepository = dialog.getRepository();
        final List<String> permittedUsers = dialog.getPermittedUsers();
        final List<String> permittedTeams = dialog.getPermittedTeams();
        if (newRepository == null) {
            return;
        }
@@ -365,11 +369,14 @@
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.createRepository(newRepository, permittedUsers);
                boolean success = gitblit.createRepository(newRepository, permittedUsers, permittedTeams);
                if (success) {
                    gitblit.refreshRepositories();
                    if (permittedUsers.size() > 0) {
                        gitblit.refreshUsers();
                    }
                    if (permittedTeams.size() > 0) {
                        gitblit.refreshTeams();
                    }
                }
                return success;
@@ -379,6 +386,7 @@
            protected void onSuccess() {
                updateTable(false);
                updateUsersTable();
                updateTeamsTable();
            }
            @Override
@@ -397,16 +405,18 @@
     * @param repository
     */
    protected void editRepository(final RepositoryModel repository) {
        EditRepositoryDialog dialog = new EditRepositoryDialog(repository);
        EditRepositoryDialog dialog = new EditRepositoryDialog(gitblit.getProtocolVersion(), repository);
        dialog.setLocationRelativeTo(RepositoriesPanel.this);
        List<String> usernames = gitblit.getUsernames();
        List<String> members = gitblit.getPermittedUsernames(repository);
        dialog.setUsers(repository.owner, usernames, members);
        dialog.setTeams(gitblit.getTeamnames(), gitblit.getPermittedTeamnames(repository));
        dialog.setRepositories(gitblit.getRepositories());
        dialog.setFederationSets(gitblit.getFederationSets(), repository.federationSets);
        dialog.setVisible(true);
        final RepositoryModel revisedRepository = dialog.getRepository();
        final List<String> permittedUsers = dialog.getPermittedUsers();
        final List<String> permittedTeams = dialog.getPermittedTeams();
        if (revisedRepository == null) {
            return;
        }
@@ -416,10 +426,11 @@
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateRepository(repository.name, revisedRepository,
                        permittedUsers);
                        permittedUsers, permittedTeams);
                if (success) {
                    gitblit.refreshRepositories();
                    gitblit.refreshUsers();
                    gitblit.refreshTeams();
                }
                return success;
            }
@@ -428,6 +439,7 @@
            protected void onSuccess() {
                updateTable(false);
                updateUsersTable();
                updateTeamsTable();
            }
            @Override
@@ -460,6 +472,7 @@
                    if (success) {
                        gitblit.refreshRepositories();
                        gitblit.refreshUsers();
                        gitblit.refreshTeams();
                    }
                    return success;
                }
@@ -468,6 +481,7 @@
                protected void onSuccess() {
                    updateTable(false);
                    updateUsersTable();
                    updateTeamsTable();
                }
                @Override
src/com/gitblit/client/TeamsPanel.java
New file
@@ -0,0 +1,378 @@
/*
 * Copyright 2011 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.client;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.models.TeamModel;
import com.gitblit.utils.StringUtils;
/**
 * Users panel displays a list of user accounts and allows management of those
 * accounts.
 *
 * @author James Moger
 *
 */
public abstract class TeamsPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final GitblitClient gitblit;
    private HeaderPanel header;
    private JTable table;
    private TeamsTableModel tableModel;
    private TableRowSorter<TeamsTableModel> defaultSorter;
    private JTextField filterTextfield;
    public TeamsPanel(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
        initialize();
    }
    private void initialize() {
        JButton refreshTeams = new JButton(Translation.get("gb.refresh"));
        refreshTeams.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshTeams();
            }
        });
        JButton createTeam = new JButton(Translation.get("gb.create"));
        createTeam.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                createTeam();
            }
        });
        final JButton editTeam = new JButton(Translation.get("gb.edit"));
        editTeam.setEnabled(false);
        editTeam.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editTeam(getSelectedTeams().get(0));
            }
        });
        final JButton delTeam = new JButton(Translation.get("gb.delete"));
        delTeam.setEnabled(false);
        delTeam.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                deleteTeams(getSelectedTeams());
            }
        });
        NameRenderer nameRenderer = new NameRenderer();
        tableModel = new TeamsTableModel();
        defaultSorter = new TableRowSorter<TeamsTableModel>(tableModel);
        table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
        String name = table.getColumnName(TeamsTableModel.Columns.Name.ordinal());
        table.setRowHeight(nameRenderer.getFont().getSize() + 8);
        table.getColumn(name).setCellRenderer(nameRenderer);
        int w = 125;
        name = table.getColumnName(TeamsTableModel.Columns.Members.ordinal());
        table.getColumn(name).setMinWidth(w);
        table.getColumn(name).setMaxWidth(w);
        name = table.getColumnName(TeamsTableModel.Columns.Repositories.ordinal());
        table.getColumn(name).setMinWidth(w);
        table.getColumn(name).setMaxWidth(w);
        table.setRowSorter(defaultSorter);
        table.getRowSorter().toggleSortOrder(TeamsTableModel.Columns.Name.ordinal());
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean selected = table.getSelectedRow() > -1;
                boolean singleSelection = table.getSelectedRows().length == 1;
                editTeam.setEnabled(singleSelection && selected);
                delTeam.setEnabled(selected);
            }
        });
        table.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    editTeam(getSelectedTeams().get(0));
                }
            }
        });
        filterTextfield = new JTextField();
        filterTextfield.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                filterTeams(filterTextfield.getText());
            }
        });
        filterTextfield.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                filterTeams(filterTextfield.getText());
            }
        });
        JPanel teamFilterPanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        teamFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
        teamFilterPanel.add(filterTextfield, BorderLayout.CENTER);
        JPanel teamTablePanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        teamTablePanel.add(teamFilterPanel, BorderLayout.NORTH);
        teamTablePanel.add(new JScrollPane(table), BorderLayout.CENTER);
        JPanel teamControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        teamControls.add(refreshTeams);
        teamControls.add(createTeam);
        teamControls.add(editTeam);
        teamControls.add(delTeam);
        setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        header = new HeaderPanel(Translation.get("gb.teams"), "users_16x16.png");
        add(header, BorderLayout.NORTH);
        add(teamTablePanel, BorderLayout.CENTER);
        add(teamControls, BorderLayout.SOUTH);
    }
    @Override
    public void requestFocus() {
        filterTextfield.requestFocus();
    }
    @Override
    public Insets getInsets() {
        return Utils.INSETS;
    }
    protected abstract void updateUsersTable();
    protected void updateTable(boolean pack) {
        tableModel.list.clear();
        tableModel.list.addAll(gitblit.getTeams());
        tableModel.fireTableDataChanged();
        header.setText(Translation.get("gb.teams") + " (" + gitblit.getTeams().size() + ")");
        if (pack) {
            Utils.packColumns(table, Utils.MARGIN);
        }
    }
    private void filterTeams(final String fragment) {
        if (StringUtils.isEmpty(fragment)) {
            table.setRowSorter(defaultSorter);
            return;
        }
        RowFilter<TeamsTableModel, Object> containsFilter = new RowFilter<TeamsTableModel, Object>() {
            public boolean include(Entry<? extends TeamsTableModel, ? extends Object> entry) {
                for (int i = entry.getValueCount() - 1; i >= 0; i--) {
                    if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
                        return true;
                    }
                }
                return false;
            }
        };
        TableRowSorter<TeamsTableModel> sorter = new TableRowSorter<TeamsTableModel>(tableModel);
        sorter.setRowFilter(containsFilter);
        table.setRowSorter(sorter);
    }
    private List<TeamModel> getSelectedTeams() {
        List<TeamModel> teams = new ArrayList<TeamModel>();
        for (int viewRow : table.getSelectedRows()) {
            int modelRow = table.convertRowIndexToModel(viewRow);
            TeamModel model = tableModel.list.get(modelRow);
            teams.add(model);
        }
        return teams;
    }
    protected void refreshTeams() {
        GitblitWorker worker = new GitblitWorker(TeamsPanel.this, RpcRequest.LIST_TEAMS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshTeams();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
        };
        worker.execute();
    }
    /**
     * Displays the create team dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     */
    protected void createTeam() {
        EditTeamDialog dialog = new EditTeamDialog(gitblit.getProtocolVersion(),
                gitblit.getSettings());
        dialog.setLocationRelativeTo(TeamsPanel.this);
        dialog.setTeams(gitblit.getTeams());
        dialog.setRepositories(gitblit.getRepositories(), null);
        dialog.setUsers(gitblit.getUsernames(), null);
        dialog.setVisible(true);
        final TeamModel newTeam = dialog.getTeam();
        if (newTeam == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.CREATE_TEAM) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.createTeam(newTeam);
                if (success) {
                    gitblit.refreshTeams();
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for team \"{1}\".",
                        getRequestType(), newTeam.name);
            }
        };
        worker.execute();
    }
    /**
     * Displays the edit team dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     * @param user
     */
    protected void editTeam(final TeamModel team) {
        EditTeamDialog dialog = new EditTeamDialog(gitblit.getProtocolVersion(), team,
                gitblit.getSettings());
        dialog.setLocationRelativeTo(TeamsPanel.this);
        dialog.setTeams(gitblit.getTeams());
        dialog.setRepositories(gitblit.getRepositories(), new ArrayList<String>(team.repositories));
        dialog.setUsers(gitblit.getUsernames(), team.users == null ? null : new ArrayList<String>(
                team.users));
        dialog.setVisible(true);
        final TeamModel revisedTeam = dialog.getTeam();
        if (revisedTeam == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.EDIT_TEAM) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateTeam(team.name, revisedTeam);
                if (success) {
                    gitblit.refreshTeams();
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for team \"{1}\".",
                        getRequestType(), team.name);
            }
        };
        worker.execute();
    }
    protected void deleteTeams(final List<TeamModel> teams) {
        if (teams == null || teams.size() == 0) {
            return;
        }
        StringBuilder message = new StringBuilder("Delete the following teams?\n\n");
        for (TeamModel team : teams) {
            message.append(team.name).append("\n");
        }
        int result = JOptionPane.showConfirmDialog(TeamsPanel.this, message.toString(),
                "Delete Teams?", JOptionPane.YES_NO_OPTION);
        if (result == JOptionPane.YES_OPTION) {
            GitblitWorker worker = new GitblitWorker(this, RpcRequest.DELETE_TEAM) {
                @Override
                protected Boolean doRequest() throws IOException {
                    boolean success = true;
                    for (TeamModel team : teams) {
                        success &= gitblit.deleteTeam(team);
                    }
                    if (success) {
                        gitblit.refreshTeams();
                        gitblit.refreshUsers();
                    }
                    return success;
                }
                @Override
                protected void onSuccess() {
                    updateTable(false);
                    updateUsersTable();
                }
                @Override
                protected void onFailure() {
                    showFailure("Failed to delete specified teams!");
                }
            };
            worker.execute();
        }
    }
}
src/com/gitblit/client/TeamsTableModel.java
New file
@@ -0,0 +1,105 @@
/*
 * Copyright 2011 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.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import com.gitblit.models.TeamModel;
/**
 * Table model of a list of teams.
 *
 * @author James Moger
 *
 */
public class TeamsTableModel extends AbstractTableModel {
    private static final long serialVersionUID = 1L;
    List<TeamModel> list;
    enum Columns {
        Name, Members, Repositories;
        @Override
        public String toString() {
            return name().replace('_', ' ');
        }
    }
    public TeamsTableModel() {
        this(new ArrayList<TeamModel>());
    }
    public TeamsTableModel(List<TeamModel> teams) {
        this.list = teams;
        Collections.sort(this.list);
    }
    @Override
    public int getRowCount() {
        return list.size();
    }
    @Override
    public int getColumnCount() {
        return Columns.values().length;
    }
    @Override
    public String getColumnName(int column) {
        Columns col = Columns.values()[column];
        switch (col) {
        case Name:
            return Translation.get("gb.name");
        case Members:
            return Translation.get("gb.teamMembers");
        case Repositories:
            return Translation.get("gb.repositories");
        }
        return "";
    }
    /**
     * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
     *
     * @param columnIndex
     *            the column being queried
     * @return the Object.class
     */
    public Class<?> getColumnClass(int columnIndex) {
        return String.class;
    }
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        TeamModel model = list.get(rowIndex);
        Columns col = Columns.values()[columnIndex];
        switch (col) {
        case Name:
            return model.name;
        case Members:
            return model.users.size() == 0 ? "" : String.valueOf(model.users.size());
        case Repositories:
            return model.repositories.size() == 0 ? "" : String.valueOf(model.repositories.size());
        }
        return null;
    }
}
src/com/gitblit/client/UsersPanel.java
@@ -41,6 +41,7 @@
import javax.swing.table.TableRowSorter;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
@@ -51,7 +52,7 @@
 * @author James Moger
 * 
 */
public class UsersPanel extends JPanel {
public abstract class UsersPanel extends JPanel {
    private static final long serialVersionUID = 1L;
@@ -111,6 +112,18 @@
        String name = table.getColumnName(UsersTableModel.Columns.Name.ordinal());
        table.setRowHeight(nameRenderer.getFont().getSize() + 8);
        table.getColumn(name).setCellRenderer(nameRenderer);
        int w = 125;
        name = table.getColumnName(UsersTableModel.Columns.AccessLevel.ordinal());
        table.getColumn(name).setMinWidth(w);
        table.getColumn(name).setMaxWidth(w);
        name = table.getColumnName(UsersTableModel.Columns.Teams.ordinal());
        table.getColumn(name).setMinWidth(w);
        table.getColumn(name).setMaxWidth(w);
        name = table.getColumnName(UsersTableModel.Columns.Repositories.ordinal());
        table.getColumn(name).setMinWidth(w);
        table.getColumn(name).setMaxWidth(w);
        table.setRowSorter(defaultSorter);
        table.getRowSorter().toggleSortOrder(UsersTableModel.Columns.Name.ordinal());
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@@ -178,6 +191,8 @@
        return Utils.INSETS;
    }
    protected abstract void updateTeamsTable();
    protected void updateTable(boolean pack) {
        tableModel.list.clear();
        tableModel.list.addAll(gitblit.getUsers());
@@ -240,10 +255,12 @@
     * 
     */
    protected void createUser() {
        EditUserDialog dialog = new EditUserDialog(gitblit.getSettings());
        EditUserDialog dialog = new EditUserDialog(gitblit.getProtocolVersion(),
                gitblit.getSettings());
        dialog.setLocationRelativeTo(UsersPanel.this);
        dialog.setUsers(gitblit.getUsers());
        dialog.setRepositories(gitblit.getRepositories(), null);
        dialog.setTeams(gitblit.getTeams(), null);
        dialog.setVisible(true);
        final UserModel newUser = dialog.getUser();
        if (newUser == null) {
@@ -257,6 +274,9 @@
                boolean success = gitblit.createUser(newUser);
                if (success) {
                    gitblit.refreshUsers();
                    if (newUser.teams.size() > 0) {
                        gitblit.refreshTeams();
                    }
                }
                return success;
            }
@@ -264,6 +284,9 @@
            @Override
            protected void onSuccess() {
                updateTable(false);
                if (newUser.teams.size() > 0) {
                    updateTeamsTable();
                }
            }
            @Override
@@ -282,10 +305,13 @@
     * @param user
     */
    protected void editUser(final UserModel user) {
        EditUserDialog dialog = new EditUserDialog(user, gitblit.getSettings());
        EditUserDialog dialog = new EditUserDialog(gitblit.getProtocolVersion(), user,
                gitblit.getSettings());
        dialog.setLocationRelativeTo(UsersPanel.this);
        dialog.setUsers(gitblit.getUsers());
        dialog.setRepositories(gitblit.getRepositories(), new ArrayList<String>(user.repositories));
        dialog.setTeams(gitblit.getTeams(), user.teams == null ? null : new ArrayList<TeamModel>(
                user.teams));
        dialog.setVisible(true);
        final UserModel revisedUser = dialog.getUser();
        if (revisedUser == null) {
@@ -298,6 +324,7 @@
                boolean success = gitblit.updateUser(user.username, revisedUser);
                if (success) {
                    gitblit.refreshUsers();
                    gitblit.refreshTeams();
                }
                return success;
            }
@@ -305,6 +332,7 @@
            @Override
            protected void onSuccess() {
                updateTable(false);
                updateTeamsTable();
            }
            @Override
@@ -336,6 +364,7 @@
                    }
                    if (success) {
                        gitblit.refreshUsers();
                        gitblit.refreshTeams();
                    }
                    return success;
                }
@@ -343,6 +372,7 @@
                @Override
                protected void onSuccess() {
                    updateTable(false);
                    updateTeamsTable();
                }
                @Override
src/com/gitblit/client/UsersTableModel.java
@@ -36,7 +36,7 @@
    List<UserModel> list;
    enum Columns {
        Name, AccessLevel;
        Name, AccessLevel, Teams, Repositories;
        @Override
        public String toString() {
@@ -71,6 +71,10 @@
            return Translation.get("gb.name");
        case AccessLevel:
            return Translation.get("gb.accessLevel");
        case Teams:
            return Translation.get("gb.teamMemberships");
        case Repositories:
            return Translation.get("gb.repositories");
        }
        return "";
    }
@@ -97,6 +101,13 @@
            if (model.canAdmin) {
                return "administrator";
            }
            return "";
        case Teams:
            return (model.teams == null || model.teams.size() == 0) ? "" : String
                    .valueOf(model.teams.size());
        case Repositories:
            return (model.repositories == null || model.repositories.size() == 0) ? "" : String
                    .valueOf(model.repositories.size());
        }
        return null;
    }
src/com/gitblit/utils/RpcUtils.java
@@ -24,6 +24,7 @@
import com.gitblit.Constants;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.GitBlitException.UnknownRequestException;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.FederationSet;
@@ -31,6 +32,7 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.ServerSettings;
import com.gitblit.models.ServerStatus;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.google.gson.reflect.TypeToken;
@@ -52,6 +54,9 @@
    }.getType();
    private static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
    }.getType();
    private static final Type TEAMS_TYPE = new TypeToken<Collection<TeamModel>>() {
    }.getType();
    private static final Type REGISTRATIONS_TYPE = new TypeToken<Collection<FederationModel>>() {
@@ -96,7 +101,28 @@
            req = RpcRequest.LIST_REPOSITORIES;
        }
        return remoteURL + Constants.RPC_PATH + "?req=" + req.name().toLowerCase()
                + (name == null ? "" : ("&name=" + name));
                + (name == null ? "" : ("&name=" + StringUtils.encodeURL(name)));
    }
    /**
     * Returns the version of the RPC protocol on the server.
     *
     * @param serverUrl
     * @param account
     * @param password
     * @return the protocol version
     * @throws IOException
     */
    public static int getProtocolVersion(String serverUrl, String account, char[] password)
            throws IOException {
        String url = asLink(serverUrl, RpcRequest.GET_PROTOCOL);
        int protocol = 1;
        try {
            protocol = JsonUtils.retrieveJson(url, Integer.class, account, password);
        } catch (UnknownRequestException e) {
            // v0.7.0 (protocol 1) did not have this request type
        }
        return protocol;
    }
    /**
@@ -131,6 +157,24 @@
        String url = asLink(serverUrl, RpcRequest.LIST_USERS);
        Collection<UserModel> models = JsonUtils.retrieveJson(url, USERS_TYPE, account, password);
        List<UserModel> list = new ArrayList<UserModel>(models);
        return list;
    }
    /**
     * Tries to pull the gitblit team definitions from the remote gitblit
     * instance.
     *
     * @param serverUrl
     * @param account
     * @param password
     * @return a collection of UserModel objects
     * @throws IOException
     */
    public static List<TeamModel> getTeams(String serverUrl, String account, char[] password)
            throws IOException {
        String url = asLink(serverUrl, RpcRequest.LIST_TEAMS);
        Collection<TeamModel> models = JsonUtils.retrieveJson(url, TEAMS_TYPE, account, password);
        List<TeamModel> list = new ArrayList<TeamModel>(models);
        return list;
    }
@@ -236,6 +280,53 @@
    }
    /**
     * Create a team on the Gitblit server.
     *
     * @param team
     * @param serverUrl
     * @param account
     * @param password
     * @return true if the action succeeded
     * @throws IOException
     */
    public static boolean createTeam(TeamModel team, String serverUrl, String account,
            char[] password) throws IOException {
        return doAction(RpcRequest.CREATE_TEAM, null, team, serverUrl, account, password);
    }
    /**
     * Send a revised version of the team model to the Gitblit server.
     *
     * @param team
     * @param serverUrl
     * @param account
     * @param password
     * @return true if the action succeeded
     * @throws IOException
     */
    public static boolean updateTeam(String teamname, TeamModel team, String serverUrl,
            String account, char[] password) throws IOException {
        return doAction(RpcRequest.EDIT_TEAM, teamname, team, serverUrl, account, password);
    }
    /**
     * Deletes a team from the Gitblit server.
     *
     * @param team
     * @param serverUrl
     * @param account
     * @param password
     * @return true if the action succeeded
     * @throws IOException
     */
    public static boolean deleteTeam(TeamModel team, String serverUrl, String account,
            char[] password) throws IOException {
        return doAction(RpcRequest.DELETE_TEAM, null, team, serverUrl, account, password);
    }
    /**
     * Retrieves the list of users that can access the specified repository.
     * 
     * @param repository
@@ -253,7 +344,7 @@
    }
    /**
     * Sets the repository membership list.
     * Sets the repository user membership list.
     * 
     * @param repository
     * @param memberships
@@ -271,6 +362,41 @@
    }
    /**
     * Retrieves the list of teams that can access the specified repository.
     *
     * @param repository
     * @param serverUrl
     * @param account
     * @param password
     * @return list of teams
     * @throws IOException
     */
    public static List<String> getRepositoryTeams(RepositoryModel repository, String serverUrl,
            String account, char[] password) throws IOException {
        String url = asLink(serverUrl, RpcRequest.LIST_REPOSITORY_TEAMS, repository.name);
        Collection<String> list = JsonUtils.retrieveJson(url, NAMES_TYPE, account, password);
        return new ArrayList<String>(list);
    }
    /**
     * Sets the repository team membership list.
     *
     * @param repository
     * @param teams
     * @param serverUrl
     * @param account
     * @param password
     * @return true if the action succeeded
     * @throws IOException
     */
    public static boolean setRepositoryTeams(RepositoryModel repository,
            List<String> teams, String serverUrl, String account, char[] password)
            throws IOException {
        return doAction(RpcRequest.SET_REPOSITORY_TEAMS, repository.name, teams, serverUrl,
                account, password);
    }
    /**
     * Retrieves the list of federation registrations. These are the list of
     * registrations that this Gitblit instance is pulling from.
     * 
tests/com/gitblit/tests/RpcTests.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -34,12 +35,14 @@
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.GitBlitException.UnauthorizedException;
import com.gitblit.Keys;
import com.gitblit.RpcServlet;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.FederationSet;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.ServerSettings;
import com.gitblit.models.ServerStatus;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.RpcUtils;
@@ -70,6 +73,12 @@
    }
    @Test
    public void testGetProtocolVersion() throws IOException {
        int protocol = RpcUtils.getProtocolVersion(url, null, null);
        assertEquals(RpcServlet.PROTOCOL_VERSION, protocol);
    }
    @Test
    public void testListRepositories() throws IOException {
        Map<String, RepositoryModel> map = RpcUtils.getRepositories(url, null, null);
        assertNotNull("Repository list is null!", map);
@@ -87,6 +96,20 @@
        list = RpcUtils.getUsers(url, "admin", "admin".toCharArray());
        assertTrue("User list is empty!", list.size() > 0);
    }
    @Test
    public void testListTeams() throws IOException {
        List<TeamModel> list = null;
        try {
            list = RpcUtils.getTeams(url, null, null);
        } catch (UnauthorizedException e) {
        }
        assertNull("Server allows anyone to admin!", list);
        list = RpcUtils.getTeams(url, "admin", "admin".toCharArray());
        assertTrue("Team list is empty!", list.size() > 0);
        assertEquals("admins", list.get(0).name);
    }
    @Test
@@ -214,6 +237,61 @@
    }
    @Test
    public void testTeamAdministration() throws IOException {
        List<TeamModel> teams = RpcUtils.getTeams(url, account, password.toCharArray());
        assertEquals(1, teams.size());
        // Create the A-Team
        TeamModel aTeam = new TeamModel("A-Team");
        aTeam.users.add("admin");
        aTeam.repositories.add("helloworld.git");
        assertTrue(RpcUtils.createTeam(aTeam, url, account, password.toCharArray()));
        aTeam = null;
        teams = RpcUtils.getTeams(url, account, password.toCharArray());
        assertEquals(2, teams.size());
        for (TeamModel team : teams) {
            if (team.name.equals("A-Team")) {
                aTeam = team;
                break;
            }
        }
        assertNotNull(aTeam);
        assertTrue(aTeam.hasUser("admin"));
        assertTrue(aTeam.hasRepository("helloworld.git"));
        RepositoryModel helloworld = null;
        Map<String, RepositoryModel> repositories = RpcUtils.getRepositories(url, account,
                password.toCharArray());
        for (RepositoryModel repository : repositories.values()) {
            if (repository.name.equals("helloworld.git")) {
                helloworld = repository;
                break;
            }
        }
        assertNotNull(helloworld);
        // Confirm that we have added the team
        List<String> helloworldTeams = RpcUtils.getRepositoryTeams(helloworld, url, account,
                password.toCharArray());
        assertEquals(1, helloworldTeams.size());
        assertTrue(helloworldTeams.contains(aTeam.name));
        // set no teams
        assertTrue(RpcUtils.setRepositoryTeams(helloworld, new ArrayList<String>(), url, account,
                password.toCharArray()));
        helloworldTeams = RpcUtils.getRepositoryTeams(helloworld, url, account,
                password.toCharArray());
        assertEquals(0, helloworldTeams.size());
        // delete the A-Team
        assertTrue(RpcUtils.deleteTeam(aTeam, url, account, password.toCharArray()));
        teams = RpcUtils.getTeams(url, account, password.toCharArray());
        assertEquals(1, teams.size());
    }
    @Test
    public void testFederationRegistrations() throws Exception {
        List<FederationModel> registrations = RpcUtils.getFederationRegistrations(url, account,
                password.toCharArray());