From 31abc26dd0354bc2dafe27c011c2e54934a89486 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Sun, 02 Oct 2011 15:37:24 -0400
Subject: [PATCH] Fairly complete json rpc interface to view/control Gitblit data objects.

---
 src/com/gitblit/GitBlit.java                 |   27 +
 src/com/gitblit/JsonServlet.java             |   31 +
 tests/com/gitblit/tests/RpcTests.java        |  178 ++++++++++
 src/com/gitblit/FederationPullExecutor.java  |   44 +-
 src/com/gitblit/GitBlitException.java        |   30 +
 src/com/gitblit/utils/JsonUtils.java         |  166 +++++++--
 src/com/gitblit/utils/RpcUtils.java          |  290 +++++++++++++++++
 tests/com/gitblit/tests/FederationTests.java |   12 
 src/com/gitblit/models/FederationSet.java    |   58 +++
 src/com/gitblit/RpcServlet.java              |   75 ++++
 src/com/gitblit/Constants.java               |   12 
 src/com/gitblit/utils/FederationUtils.java   |   14 
 12 files changed, 821 insertions(+), 116 deletions(-)

diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java
index 3862a9d..5b4b0b6 100644
--- a/src/com/gitblit/Constants.java
+++ b/src/com/gitblit/Constants.java
@@ -52,7 +52,7 @@
 	public static final String SYNDICATION_PATH = "/feed/";
 
 	public static final String FEDERATION_PATH = "/federation/";
-	
+
 	public static final String RPC_PATH = "/rpc/";
 
 	public static final String BORDER = "***********************************************************";
@@ -202,8 +202,10 @@
 	 */
 	public static enum RpcRequest {
 		LIST_REPOSITORIES, CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY,
-		LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER;
-		
+		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;
+
 		public static RpcRequest fromName(String name) {
 			for (RpcRequest type : values()) {
 				if (type.name().equalsIgnoreCase(name)) {
@@ -212,11 +214,11 @@
 			}
 			return LIST_REPOSITORIES;
 		}
-		
+
 		public boolean exceeds(RpcRequest type) {
 			return this.ordinal() > type.ordinal();
 		}
-		
+
 		@Override
 		public String toString() {
 			return name();
diff --git a/src/com/gitblit/FederationPullExecutor.java b/src/com/gitblit/FederationPullExecutor.java
index ef089d0..b190e7b 100644
--- a/src/com/gitblit/FederationPullExecutor.java
+++ b/src/com/gitblit/FederationPullExecutor.java
@@ -19,6 +19,7 @@
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.net.InetAddress;
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -43,6 +44,7 @@
 
 import com.gitblit.Constants.FederationPullStatus;
 import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.GitBlitException.ForbiddenException;
 import com.gitblit.models.FederationModel;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
@@ -81,8 +83,8 @@
 	 * 
 	 * @param registrations
 	 * @param isDaemon
-	 *            if true, registrations are rescheduled in perpetuity. if false,
-	 *            the federation pull operation is executed once.
+	 *            if true, registrations are rescheduled in perpetuity. if
+	 *            false, the federation pull operation is executed once.
 	 */
 	public FederationPullExecutor(List<FederationModel> registrations, boolean isDaemon) {
 		this.registrations = registrations;
@@ -169,7 +171,7 @@
 			} else {
 				repositoryName = registrationFolder + "/" + repository.name;
 			}
-			
+
 			if (registration.bare) {
 				// bare repository, ensure .git suffix
 				if (!repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
@@ -178,7 +180,8 @@
 			} else {
 				// normal repository, strip .git suffix
 				if (repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
-					repositoryName = repositoryName.substring(0, repositoryName.indexOf(DOT_GIT_EXT));
+					repositoryName = repositoryName.substring(0,
+							repositoryName.indexOf(DOT_GIT_EXT));
 				}
 			}
 
@@ -190,7 +193,8 @@
 				StoredConfig config = existingRepository.getConfig();
 				config.load();
 				String origin = config.getString("remote", "origin", "url");
-				RevCommit commit = JGitUtils.getCommit(existingRepository, "refs/remotes/origin/master");
+				RevCommit commit = JGitUtils.getCommit(existingRepository,
+						"refs/remotes/origin/master");
 				if (commit != null) {
 					fetchHead = commit.getName();
 				}
@@ -209,7 +213,7 @@
 					Constants.FEDERATION_USER, registration.token);
 			logger.info(MessageFormat.format("Pulling federated repository {0} from {1} @ {2}",
 					repository.name, registration.name, registration.url));
-						
+
 			CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name,
 					cloneUrl, registration.bare, credentials);
 			Repository r = GitBlit.self().getRepository(repositoryName);
@@ -255,7 +259,7 @@
 				// preserve local settings
 				repository.isFrozen = rm.isFrozen;
 				repository.federationStrategy = rm.federationStrategy;
-				
+
 				// merge federation sets
 				Set<String> federationSets = new HashSet<String>();
 				if (rm.federationSets != null) {
@@ -317,13 +321,12 @@
 					}
 				}
 			}
-		} catch (Exception e) {
-			// a 403 error code is normal for a PULL_REPOSITORIES token
-			if (!e.getMessage().contains("403")) {
-				logger.warn(MessageFormat.format(
-						"Failed to retrieve USERS from federated gitblit ({0} @ {1})",
-						registration.name, registration.url), e);
-			}
+		} catch (ForbiddenException e) {
+			// ignore forbidden exceptions
+		} catch (IOException e) {
+			logger.warn(MessageFormat.format(
+					"Failed to retrieve USERS from federated gitblit ({0} @ {1})",
+					registration.name, registration.url), e);
 		}
 
 		try {
@@ -337,13 +340,12 @@
 				properties.store(os, null);
 				os.close();
 			}
-		} catch (Exception e) {
-			// a 403 error code is normal for a PULL_REPOSITORIES token
-			if (!e.getMessage().contains("403")) {
-				logger.warn(MessageFormat.format(
-						"Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",
-						registration.name, registration.url), e);
-			}
+		} catch (ForbiddenException e) {
+			// ignore forbidden exceptions
+		} catch (IOException e) {
+			logger.warn(MessageFormat.format(
+					"Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",
+					registration.name, registration.url), e);
 		}
 	}
 
diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java
index e86fcf6..73ec29e 100644
--- a/src/com/gitblit/GitBlit.java
+++ b/src/com/gitblit/GitBlit.java
@@ -59,6 +59,7 @@
 import com.gitblit.Constants.FederationToken;
 import com.gitblit.models.FederationModel;
 import com.gitblit.models.FederationProposal;
+import com.gitblit.models.FederationSet;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.FederationUtils;
@@ -874,6 +875,29 @@
 	}
 
 	/**
+	 * Returns the list of federation sets.
+	 * 
+	 * @return list of federation sets
+	 */
+	public List<FederationSet> getFederationSets(String gitblitUrl) {
+		List<FederationSet> list = new ArrayList<FederationSet>();
+		// generate standard tokens
+		for (FederationToken type : FederationToken.values()) {
+			FederationSet fset = new FederationSet(type.toString(), type, getFederationToken(type));
+			fset.repositories = getRepositories(gitblitUrl, fset.token);
+			list.add(fset);
+		}
+		// generate tokens for federation sets
+		for (String set : settings.getStrings(Keys.federation.sets)) {
+			FederationSet fset = new FederationSet(set, FederationToken.REPOSITORIES,
+					getFederationToken(set));
+			fset.repositories = getRepositories(gitblitUrl, fset.token);
+			list.add(fset);
+		}
+		return list;
+	}
+
+	/**
 	 * Returns the list of possible federation tokens for this Gitblit instance.
 	 * 
 	 * @return list of federation tokens
@@ -1025,7 +1049,8 @@
 			});
 			for (File file : files) {
 				String json = com.gitblit.utils.FileUtils.readContent(file, null);
-				FederationProposal proposal = JsonUtils.fromJsonString(json, FederationProposal.class);
+				FederationProposal proposal = JsonUtils.fromJsonString(json,
+						FederationProposal.class);
 				list.add(proposal);
 			}
 		}
diff --git a/src/com/gitblit/GitBlitException.java b/src/com/gitblit/GitBlitException.java
index 032e41f..af32003 100644
--- a/src/com/gitblit/GitBlitException.java
+++ b/src/com/gitblit/GitBlitException.java
@@ -15,17 +15,45 @@
  */
 package com.gitblit;
 
+import java.io.IOException;
+
 /**
  * GitBlitException is a marginally useful class. :)
  * 
  * @author James Moger
  * 
  */
-public class GitBlitException extends Exception {
+public class GitBlitException extends IOException {
 
 	private static final long serialVersionUID = 1L;
 
 	public GitBlitException(String message) {
 		super(message);
 	}
+
+	/**
+	 * Exception to indicate that the client should prompt for credentials
+	 * because the requested action requires authentication.
+	 */
+	public static class UnauthorizedException extends GitBlitException {
+
+		private static final long serialVersionUID = 1L;
+
+		public UnauthorizedException(String message) {
+			super(message);
+		}
+	}
+
+	/**
+	 * Exception to indicate that the requested action can not be executed by
+	 * the specified user.
+	 */
+	public static class ForbiddenException extends GitBlitException {
+
+		private static final long serialVersionUID = 1L;
+
+		public ForbiddenException(String message) {
+			super(message);
+		}
+	}
 }
diff --git a/src/com/gitblit/JsonServlet.java b/src/com/gitblit/JsonServlet.java
index b1d1053..ad1d67b 100644
--- a/src/com/gitblit/JsonServlet.java
+++ b/src/com/gitblit/JsonServlet.java
@@ -17,6 +17,7 @@
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.lang.reflect.Type;
 import java.text.MessageFormat;
 
 import javax.servlet.ServletException;
@@ -27,6 +28,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.gitblit.utils.StringUtils;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 
@@ -72,6 +74,30 @@
 
 	protected <X> X deserialize(HttpServletRequest request, HttpServletResponse response,
 			Class<X> clazz) throws IOException {
+		String json = readJson(request, response);
+		if (StringUtils.isEmpty(json)) {
+			return null;
+		}
+
+		Gson gson = new Gson();
+		X object = gson.fromJson(json.toString(), clazz);
+		return object;
+	}
+
+	protected <X> X deserialize(HttpServletRequest request, HttpServletResponse response, Type type)
+			throws IOException {
+		String json = readJson(request, response);
+		if (StringUtils.isEmpty(json)) {
+			return null;
+		}
+
+		Gson gson = new Gson();
+		X object = gson.fromJson(json.toString(), type);
+		return object;
+	}
+
+	private String readJson(HttpServletRequest request, HttpServletResponse response)
+			throws IOException {
 		BufferedReader reader = request.getReader();
 		StringBuilder json = new StringBuilder();
 		String line = null;
@@ -86,10 +112,7 @@
 			response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
 			return null;
 		}
-
-		Gson gson = new Gson();
-		X object = gson.fromJson(json.toString(), clazz);
-		return object;
+		return json.toString();
 	}
 
 	protected void serialize(HttpServletResponse response, Object o) throws IOException {
diff --git a/src/com/gitblit/RpcServlet.java b/src/com/gitblit/RpcServlet.java
index 9d26ee0..4dee319 100644
--- a/src/com/gitblit/RpcServlet.java
+++ b/src/com/gitblit/RpcServlet.java
@@ -18,6 +18,7 @@
 import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -30,6 +31,7 @@
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.HttpUtils;
+import com.gitblit.utils.RpcUtils;
 
 /**
  * Handles remote procedure calls.
@@ -57,6 +59,7 @@
 	protected void processRequest(HttpServletRequest request, HttpServletResponse response)
 			throws ServletException, IOException {
 		RpcRequest reqType = RpcRequest.fromName(request.getParameter("req"));
+		String objectName = request.getParameter("name");
 		logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType,
 				request.getRemoteAddr()));
 
@@ -88,6 +91,78 @@
 				users.add(GitBlit.self().getUserModel(name));
 			}
 			result = users;
+		} else if (RpcRequest.CREATE_REPOSITORY.equals(reqType)) {
+			// create repository
+			RepositoryModel model = deserialize(request, response, RepositoryModel.class);
+			GitBlit.self().updateRepositoryModel(model.name, model, true);
+		} else if (RpcRequest.EDIT_REPOSITORY.equals(reqType)) {
+			// edit repository
+			RepositoryModel model = deserialize(request, response, RepositoryModel.class);
+			// name parameter specifies original repository name in event of
+			// rename
+			String repoName = objectName;
+			if (repoName == null) {
+				repoName = model.name;
+			}
+			GitBlit.self().updateRepositoryModel(repoName, model, false);
+		} else if (RpcRequest.DELETE_REPOSITORY.equals(reqType)) {
+			// delete repository
+			RepositoryModel model = deserialize(request, response, RepositoryModel.class);
+			GitBlit.self().deleteRepositoryModel(model);
+		} else if (RpcRequest.CREATE_USER.equals(reqType)) {
+			// create user
+			UserModel model = deserialize(request, response, UserModel.class);
+			GitBlit.self().updateUserModel(model.username, model, true);
+		} else if (RpcRequest.EDIT_USER.equals(reqType)) {
+			// edit user
+			UserModel model = deserialize(request, response, UserModel.class);
+			// name parameter specifies original user name in event of rename
+			String username = objectName;
+			if (username == null) {
+				username = model.username;
+			}
+			GitBlit.self().updateUserModel(username, model, false);
+		} else if (RpcRequest.DELETE_USER.equals(reqType)) {
+			// delete user
+			UserModel model = deserialize(request, response, UserModel.class);
+			GitBlit.self().deleteUser(model.username);
+		} else if (RpcRequest.LIST_REPOSITORY_MEMBERS.equals(reqType)) {
+			// get repository members
+			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
+			result = GitBlit.self().getRepositoryUsers(model);
+		} else if (RpcRequest.SET_REPOSITORY_MEMBERS.equals(reqType)) {
+			// update repository access list
+			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
+			Collection<String> names = deserialize(request, response, RpcUtils.NAMES_TYPE);
+			List<String> users = new ArrayList<String>(names);
+			if (!GitBlit.self().setRepositoryUsers(model, users)) {
+				response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+			}
+		} else if (RpcRequest.LIST_FEDERATION_REGISTRATIONS.equals(reqType)) {
+			// return the list of federation registrations
+			result = GitBlit.self().getFederationRegistrations();
+		} else if (RpcRequest.LIST_FEDERATION_RESULTS.equals(reqType)) {
+			// return the list of federation result registrations
+			if (GitBlit.canFederate()) {
+				result = GitBlit.self().getFederationResultRegistrations();
+			} else {
+				response.sendError(HttpServletResponse.SC_FORBIDDEN);
+			}
+		} else if (RpcRequest.LIST_FEDERATION_PROPOSALS.equals(reqType)) {
+			// return the list of federation proposals
+			if (GitBlit.canFederate()) {
+				result = GitBlit.self().getPendingFederationProposals();
+			} else {
+				response.sendError(HttpServletResponse.SC_FORBIDDEN);
+			}
+		} else if (RpcRequest.LIST_FEDERATION_SETS.equals(reqType)) {
+			// return the list of federation sets
+			if (GitBlit.canFederate()) {
+				String gitblitUrl = HttpUtils.getGitblitURL(request);
+				result = GitBlit.self().getFederationSets(gitblitUrl);
+			} else {
+				response.sendError(HttpServletResponse.SC_FORBIDDEN);
+			}
 		}
 
 		// send the result of the request
diff --git a/src/com/gitblit/models/FederationSet.java b/src/com/gitblit/models/FederationSet.java
new file mode 100644
index 0000000..357689c
--- /dev/null
+++ b/src/com/gitblit/models/FederationSet.java
@@ -0,0 +1,58 @@
+/*
+ * 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.models;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import com.gitblit.Constants.FederationToken;
+
+/**
+ * Represents a group of repositories.
+ */
+public class FederationSet implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	public String name;
+
+	public String token;
+
+	public FederationToken tokenType;
+
+	public Map<String, RepositoryModel> repositories;
+
+	/**
+	 * The constructor for a federation set.
+	 * 
+	 * @param name
+	 *            the name of this federation set
+	 * @param tokenType
+	 *            the type of token of this federation set
+	 * @param token
+	 *            the federation token
+	 */
+	public FederationSet(String name, FederationToken tokenType, String token) {
+		this.name = name;
+		this.tokenType = tokenType;
+		this.token = token;
+	}
+
+	@Override
+	public String toString() {
+		return "Federation Set (" + name + ")";
+	}
+}
diff --git a/src/com/gitblit/utils/FederationUtils.java b/src/com/gitblit/utils/FederationUtils.java
index d04a7a3..324aa67 100644
--- a/src/com/gitblit/utils/FederationUtils.java
+++ b/src/com/gitblit/utils/FederationUtils.java
@@ -49,16 +49,13 @@
  */
 public class FederationUtils {
 
-	public static final Type REPOSITORIES_TYPE = new TypeToken<Map<String, RepositoryModel>>() {
+	private static final Type REPOSITORIES_TYPE = new TypeToken<Map<String, RepositoryModel>>() {
 	}.getType();
 
-	public static final Type SETTINGS_TYPE = new TypeToken<Map<String, String>>() {
+	private static final Type SETTINGS_TYPE = new TypeToken<Map<String, String>>() {
 	}.getType();
 
-	public static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
-	}.getType();
-
-	public static final Type RESULTS_TYPE = new TypeToken<List<FederationModel>>() {
+	private static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
 	}.getType();
 
 	private static final Logger LOGGER = LoggerFactory.getLogger(FederationUtils.class);
@@ -276,10 +273,11 @@
 	 * @return a collection of UserModel objects
 	 * @throws Exception
 	 */
-	public static Collection<UserModel> getUsers(FederationModel registration) throws Exception {
+	public static List<UserModel> getUsers(FederationModel registration) throws Exception {
 		String url = asLink(registration.url, registration.token, FederationRequest.PULL_USERS);
 		Collection<UserModel> models = JsonUtils.retrieveJson(url, USERS_TYPE);
-		return models;
+		List<UserModel> list = new ArrayList<UserModel>(models);
+		return list;
 	}
 
 	/**
diff --git a/src/com/gitblit/utils/JsonUtils.java b/src/com/gitblit/utils/JsonUtils.java
index a697b7a..1edfc58 100644
--- a/src/com/gitblit/utils/JsonUtils.java
+++ b/src/com/gitblit/utils/JsonUtils.java
@@ -16,6 +16,7 @@
 package com.gitblit.utils;
 
 import java.io.BufferedReader;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
@@ -36,6 +37,10 @@
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
 
+import org.eclipse.jgit.util.Base64;
+
+import com.gitblit.GitBlitException.ForbiddenException;
+import com.gitblit.GitBlitException.UnauthorizedException;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.google.gson.Gson;
@@ -43,7 +48,7 @@
 import com.google.gson.reflect.TypeToken;
 
 /**
- * Utility methods for gson calls to a Gitblit server.
+ * Utility methods for json calls to a Gitblit server.
  * 
  * @author James Moger
  * 
@@ -98,7 +103,7 @@
 		Gson gson = new Gson();
 		return gson.fromJson(json, clazz);
 	}
-	
+
 	/**
 	 * Convert a json string to an object of the specified type.
 	 * 
@@ -116,11 +121,27 @@
 	 * 
 	 * @param url
 	 * @param type
-	 * @return
-	 * @throws Exception
+	 * @return the deserialized object
+	 * @throws {@link IOException}
 	 */
-	public static <X> X retrieveJson(String url, Type type) throws Exception {
-		String json = retrieveJsonString(url);
+	public static <X> X retrieveJson(String url, Type type) throws IOException,
+			UnauthorizedException {
+		return retrieveJson(url, type, null, null);
+	}
+
+	/**
+	 * Reads a gson object from the specified url.
+	 * 
+	 * @param url
+	 * @param type
+	 * @param username
+	 * @param password
+	 * @return the deserialized object
+	 * @throws {@link IOException}
+	 */
+	public static <X> X retrieveJson(String url, Type type, String username, char[] password)
+			throws IOException {
+		String json = retrieveJsonString(url, username, password);
 		if (StringUtils.isEmpty(json)) {
 			return null;
 		}
@@ -133,29 +154,42 @@
 	 * 
 	 * @param url
 	 * @return the JSON message as a string
-	 * @throws Exception
+	 * @throws {@link IOException}
 	 */
-	public static String retrieveJsonString(String url) throws Exception {
-		URL urlObject = new URL(url);
-		URLConnection conn = urlObject.openConnection();
-		conn.setRequestProperty("Accept-Charset", CHARSET);
-		conn.setUseCaches(false);
-		conn.setDoInput(true);
-		if (conn instanceof HttpsURLConnection) {
-			HttpsURLConnection secureConn = (HttpsURLConnection) conn;
-			secureConn.setSSLSocketFactory(SSL_CONTEXT.getSocketFactory());
-			secureConn.setHostnameVerifier(HOSTNAME_VERIFIER);
+	public static String retrieveJsonString(String url, String username, char[] password)
+			throws IOException {
+		try {
+			URL urlObject = new URL(url);
+			URLConnection conn = urlObject.openConnection();
+			conn.setRequestProperty("Accept-Charset", CHARSET);
+			setAuthorization(conn, username, password);
+			conn.setUseCaches(false);
+			conn.setDoInput(true);
+			if (conn instanceof HttpsURLConnection) {
+				HttpsURLConnection secureConn = (HttpsURLConnection) conn;
+				secureConn.setSSLSocketFactory(SSL_CONTEXT.getSocketFactory());
+				secureConn.setHostnameVerifier(HOSTNAME_VERIFIER);
+			}
+			InputStream is = conn.getInputStream();
+			BufferedReader reader = new BufferedReader(new InputStreamReader(is, CHARSET));
+			StringBuilder json = new StringBuilder();
+			char[] buffer = new char[4096];
+			int len = 0;
+			while ((len = reader.read(buffer)) > -1) {
+				json.append(buffer, 0, len);
+			}
+			is.close();
+			return json.toString();
+		} catch (IOException e) {
+			if (e.getMessage().indexOf("401") > -1) {
+				// unauthorized
+				throw new UnauthorizedException(url);
+			} else if (e.getMessage().indexOf("403") > -1) {
+				// requested url is forbidden by the requesting user
+				throw new ForbiddenException(url);
+			}
+			throw e;
 		}
-		InputStream is = conn.getInputStream();
-		BufferedReader reader = new BufferedReader(new InputStreamReader(is, CHARSET));
-		StringBuilder json = new StringBuilder();
-		char[] buffer = new char[4096];
-		int len = 0;
-		while ((len = reader.read(buffer)) > -1) {
-			json.append(buffer, 0, len);
-		}
-		is.close();
-		return json.toString();
 	}
 
 	/**
@@ -166,29 +200,67 @@
 	 * @param json
 	 *            the json message to send
 	 * @return the http request result code
-	 * @throws Exception
+	 * @throws {@link IOException}
 	 */
-	public static int sendJsonString(String url, String json) throws Exception {
-		byte[] jsonBytes = json.getBytes(CHARSET);
-		URL urlObject = new URL(url);
-		URLConnection conn = urlObject.openConnection();
-		conn.setRequestProperty("Content-Type", "text/plain;charset=" + CHARSET);
-		conn.setRequestProperty("Content-Length", "" + jsonBytes.length);
-		conn.setUseCaches(false);
-		conn.setDoOutput(true);
-		if (conn instanceof HttpsURLConnection) {
-			HttpsURLConnection secureConn = (HttpsURLConnection) conn;
-			secureConn.setSSLSocketFactory(SSL_CONTEXT.getSocketFactory());
-			secureConn.setHostnameVerifier(HOSTNAME_VERIFIER);
+	public static int sendJsonString(String url, String json) throws IOException {
+		return sendJsonString(url, json, null, null);
+	}
+
+	/**
+	 * Sends a JSON message.
+	 * 
+	 * @param url
+	 *            the url to write to
+	 * @param json
+	 *            the json message to send
+	 * @param username
+	 * @param password
+	 * @return the http request result code
+	 * @throws {@link IOException}
+	 */
+	public static int sendJsonString(String url, String json, String username, char[] password)
+			throws IOException {
+		try {
+			byte[] jsonBytes = json.getBytes(CHARSET);
+			URL urlObject = new URL(url);
+			URLConnection conn = urlObject.openConnection();
+			conn.setRequestProperty("Content-Type", "text/plain;charset=" + CHARSET);
+			conn.setRequestProperty("Content-Length", "" + jsonBytes.length);
+			setAuthorization(conn, username, password);
+			conn.setUseCaches(false);
+			conn.setDoOutput(true);
+			if (conn instanceof HttpsURLConnection) {
+				HttpsURLConnection secureConn = (HttpsURLConnection) conn;
+				secureConn.setSSLSocketFactory(SSL_CONTEXT.getSocketFactory());
+				secureConn.setHostnameVerifier(HOSTNAME_VERIFIER);
+			}
+
+			// write json body
+			OutputStream os = conn.getOutputStream();
+			os.write(jsonBytes);
+			os.close();
+
+			int status = ((HttpURLConnection) conn).getResponseCode();
+			return status;
+		} catch (IOException e) {
+			if (e.getMessage().indexOf("401") > -1) {
+				// unauthorized
+				throw new UnauthorizedException(url);
+			} else if (e.getMessage().indexOf("403") > -1) {
+				// requested url is forbidden by the requesting user
+				throw new ForbiddenException(url);
+			}
+			throw e;
 		}
+	}
 
-		// write json body
-		OutputStream os = conn.getOutputStream();
-		os.write(jsonBytes);
-		os.close();
-
-		int status = ((HttpURLConnection) conn).getResponseCode();
-		return status;
+	private static void setAuthorization(URLConnection conn, String username, char[] password) {
+		if (!StringUtils.isEmpty(username) && (password != null && password.length > 0)) {
+			conn.setRequestProperty(
+					"Authorization",
+					"Basic "
+							+ Base64.encodeBytes((username + ":" + new String(password)).getBytes()));
+		}
 	}
 
 	/**
diff --git a/src/com/gitblit/utils/RpcUtils.java b/src/com/gitblit/utils/RpcUtils.java
index 919c7bb..715ecb5 100644
--- a/src/com/gitblit/utils/RpcUtils.java
+++ b/src/com/gitblit/utils/RpcUtils.java
@@ -15,12 +15,18 @@
  */
 package com.gitblit.utils;
 
+import java.io.IOException;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 import com.gitblit.Constants;
 import com.gitblit.Constants.RpcRequest;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.FederationProposal;
+import com.gitblit.models.FederationSet;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.google.gson.reflect.TypeToken;
@@ -33,10 +39,22 @@
  */
 public class RpcUtils {
 
-	public static final Type REPOSITORIES_TYPE = new TypeToken<Map<String, RepositoryModel>>() {
+	public static final Type NAMES_TYPE = new TypeToken<Collection<String>>() {
 	}.getType();
 
-	public static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
+	private static final Type REPOSITORIES_TYPE = new TypeToken<Map<String, RepositoryModel>>() {
+	}.getType();
+
+	private static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
+	}.getType();
+
+	private static final Type REGISTRATIONS_TYPE = new TypeToken<Collection<FederationModel>>() {
+	}.getType();
+
+	private static final Type PROPOSALS_TYPE = new TypeToken<Collection<FederationProposal>>() {
+	}.getType();
+
+	private static final Type SETS_TYPE = new TypeToken<Collection<FederationSet>>() {
 	}.getType();
 
 	/**
@@ -48,26 +66,45 @@
 	 * @return
 	 */
 	public static String asLink(String remoteURL, RpcRequest req) {
+		return asLink(remoteURL, req, null);
+	}
+
+	/**
+	 * 
+	 * @param remoteURL
+	 *            the url of the remote gitblit instance
+	 * @param req
+	 *            the rpc request type
+	 * @param name
+	 *            the name of the actionable object
+	 * @return
+	 */
+	public static String asLink(String remoteURL, RpcRequest req, String name) {
 		if (remoteURL.length() > 0 && remoteURL.charAt(remoteURL.length() - 1) == '/') {
 			remoteURL = remoteURL.substring(0, remoteURL.length() - 1);
 		}
 		if (req == null) {
 			req = RpcRequest.LIST_REPOSITORIES;
 		}
-		return remoteURL + Constants.RPC_PATH + "?req=" + req.name().toLowerCase();
+		return remoteURL + Constants.RPC_PATH + "?req=" + req.name().toLowerCase()
+				+ (name == null ? "" : ("&name=" + name));
 	}
-	
+
 	/**
 	 * Retrieves a map of the repositories at the remote gitblit instance keyed
 	 * by the repository clone url.
 	 * 
 	 * @param serverUrl
+	 * @param account
+	 * @param password
 	 * @return a map of cloneable repositories
-	 * @throws Exception
+	 * @throws IOException
 	 */
-	public static Map<String, RepositoryModel> getRepositories(String serverUrl) throws Exception {
+	public static Map<String, RepositoryModel> getRepositories(String serverUrl, String account,
+			char[] password) throws IOException {
 		String url = asLink(serverUrl, RpcRequest.LIST_REPOSITORIES);
-		Map<String, RepositoryModel> models = JsonUtils.retrieveJson(url, REPOSITORIES_TYPE);
+		Map<String, RepositoryModel> models = JsonUtils.retrieveJson(url, REPOSITORIES_TYPE,
+				account, password);
 		return models;
 	}
 
@@ -75,12 +112,243 @@
 	 * Tries to pull the gitblit user accounts from the remote gitblit instance.
 	 * 
 	 * @param serverUrl
+	 * @param account
+	 * @param password
 	 * @return a collection of UserModel objects
-	 * @throws Exception
+	 * @throws IOException
 	 */
-	public static Collection<UserModel> getUsers(String serverUrl) throws Exception {
+	public static List<UserModel> getUsers(String serverUrl, String account, char[] password)
+			throws IOException {
 		String url = asLink(serverUrl, RpcRequest.LIST_USERS);
-		Collection<UserModel> models = JsonUtils.retrieveJson(url, USERS_TYPE);
-		return models;
+		Collection<UserModel> models = JsonUtils.retrieveJson(url, USERS_TYPE, account, password);
+		List<UserModel> list = new ArrayList<UserModel>(models);
+		return list;
+	}
+
+	/**
+	 * Create a repository on the Gitblit server.
+	 * 
+	 * @param repository
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	public static boolean createRepository(RepositoryModel repository, String serverUrl,
+			String account, char[] password) throws IOException {
+		return doAction(RpcRequest.CREATE_REPOSITORY, null, repository, serverUrl, account,
+				password);
+
+	}
+
+	/**
+	 * Send a revised version of the repository model to the Gitblit server.
+	 * 
+	 * @param repository
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	public static boolean updateRepository(String repositoryName, RepositoryModel repository,
+			String serverUrl, String account, char[] password) throws IOException {
+		return doAction(RpcRequest.EDIT_REPOSITORY, repositoryName, repository, serverUrl, account,
+				password);
+	}
+
+	/**
+	 * Delete a repository from the Gitblit server.
+	 * 
+	 * @param repository
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	public static boolean deleteRepository(RepositoryModel repository, String serverUrl,
+			String account, char[] password) throws IOException {
+		return doAction(RpcRequest.DELETE_REPOSITORY, null, repository, serverUrl, account,
+				password);
+
+	}
+
+	/**
+	 * Create a user on the Gitblit server.
+	 * 
+	 * @param user
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	public static boolean createUser(UserModel user, String serverUrl, String account,
+			char[] password) throws IOException {
+		return doAction(RpcRequest.CREATE_USER, null, user, serverUrl, account, password);
+
+	}
+
+	/**
+	 * Send a revised version of the user model to the Gitblit server.
+	 * 
+	 * @param user
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	public static boolean updateUser(String username, UserModel user, String serverUrl,
+			String account, char[] password) throws IOException {
+		return doAction(RpcRequest.EDIT_USER, username, user, serverUrl, account, password);
+
+	}
+
+	/**
+	 * Deletes a user from the Gitblit server.
+	 * 
+	 * @param user
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	public static boolean deleteUser(UserModel user, String serverUrl, String account,
+			char[] password) throws IOException {
+		return doAction(RpcRequest.DELETE_USER, null, user, serverUrl, account, password);
+	}
+
+	/**
+	 * Retrieves the list of users that can access the specified repository.
+	 * 
+	 * @param repository
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return list of members
+	 * @throws IOException
+	 */
+	public static List<String> getRepositoryMembers(RepositoryModel repository, String serverUrl,
+			String account, char[] password) throws IOException {
+		String url = asLink(serverUrl, RpcRequest.LIST_REPOSITORY_MEMBERS, repository.name);
+		Collection<String> list = JsonUtils.retrieveJson(url, NAMES_TYPE, account, password);
+		return new ArrayList<String>(list);
+	}
+
+	/**
+	 * Sets the repository membership list.
+	 * 
+	 * @param repository
+	 * @param memberships
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	public static boolean setRepositoryMembers(RepositoryModel repository,
+			List<String> memberships, String serverUrl, String account, char[] password)
+			throws IOException {
+		return doAction(RpcRequest.SET_REPOSITORY_MEMBERS, repository.name, memberships, serverUrl,
+				account, password);
+	}
+
+	/**
+	 * Retrieves the list of federation registrations. These are the list of
+	 * registrations that this Gitblit instance is pulling from.
+	 * 
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return a collection of FederationRegistration objects
+	 * @throws IOException
+	 */
+	public static List<FederationModel> getFederationRegistrations(String serverUrl,
+			String account, char[] password) throws IOException {
+		String url = asLink(serverUrl, RpcRequest.LIST_FEDERATION_REGISTRATIONS);
+		Collection<FederationModel> registrations = JsonUtils.retrieveJson(url, REGISTRATIONS_TYPE,
+				account, password);
+		List<FederationModel> list = new ArrayList<FederationModel>(registrations);
+		return list;
+	}
+
+	/**
+	 * Retrieves the list of federation result registrations. These are the
+	 * results reported back to this Gitblit instance from a federation client.
+	 * 
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return a collection of FederationRegistration objects
+	 * @throws IOException
+	 */
+	public static List<FederationModel> getFederationResultRegistrations(String serverUrl,
+			String account, char[] password) throws IOException {
+		String url = asLink(serverUrl, RpcRequest.LIST_FEDERATION_RESULTS);
+		Collection<FederationModel> registrations = JsonUtils.retrieveJson(url, REGISTRATIONS_TYPE,
+				account, password);
+		List<FederationModel> list = new ArrayList<FederationModel>(registrations);
+		return list;
+	}
+	
+	/**
+	 * Retrieves the list of federation proposals.
+	 * 
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return a collection of FederationProposal objects
+	 * @throws IOException
+	 */
+	public static List<FederationProposal> getFederationProposals(String serverUrl,
+			String account, char[] password) throws IOException {
+		String url = asLink(serverUrl, RpcRequest.LIST_FEDERATION_PROPOSALS);
+		Collection<FederationProposal> proposals = JsonUtils.retrieveJson(url, PROPOSALS_TYPE,
+				account, password);
+		List<FederationProposal> list = new ArrayList<FederationProposal>(proposals);
+		return list;
+	}
+	
+	/**
+	 * Retrieves the list of federation repository sets.
+	 * 
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return a collection of FederationSet objects
+	 * @throws IOException
+	 */
+	public static List<FederationSet> getFederationSets(String serverUrl,
+			String account, char[] password) throws IOException {
+		String url = asLink(serverUrl, RpcRequest.LIST_FEDERATION_SETS);
+		Collection<FederationSet> sets = JsonUtils.retrieveJson(url, SETS_TYPE,
+				account, password);
+		List<FederationSet> list = new ArrayList<FederationSet>(sets);
+		return list;
+	}
+
+	/**
+	 * Do the specified administrative action on the Gitblit server.
+	 * 
+	 * @param request
+	 * @param name
+	 *            the name of the object (may be null)
+	 * @param object
+	 * @param serverUrl
+	 * @param account
+	 * @param password
+	 * @return true if the action succeeded
+	 * @throws IOException
+	 */
+	protected static boolean doAction(RpcRequest request, String name, Object object,
+			String serverUrl, String account, char[] password) throws IOException {
+		String url = asLink(serverUrl, request, name);
+		String json = JsonUtils.toJsonString(object);
+		int resultCode = JsonUtils.sendJsonString(url, json, account, password);
+		return resultCode == 200;
 	}
 }
diff --git a/tests/com/gitblit/tests/FederationTests.java b/tests/com/gitblit/tests/FederationTests.java
index 8af3167..66bb949 100644
--- a/tests/com/gitblit/tests/FederationTests.java
+++ b/tests/com/gitblit/tests/FederationTests.java
@@ -64,12 +64,6 @@
 		Thread.sleep(2500);
 	}
 
-	public void testDeserialization() throws Exception {
-		String json = "{\"https://localhost:8443/git/a.b.c.orphan.git\":{\"name\":\"a.b.c.orphan.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 22, 2011 3:15:07 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/jgit.git\":{\"name\":\"test/jgit.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 13, 2011 9:42:33 AM\",\"hasCommits\":true,\"showRemoteBranches\":true,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/helloworld.git\":{\"name\":\"test/helloworld.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 15, 2008 7:26:48 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/working/ticgit\":{\"name\":\"working/ticgit\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 22, 2011 10:35:27 AM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/ticgit.git\":{\"name\":\"ticgit.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 22, 2011 10:35:27 AM\",\"hasCommits\":true,\"showRemoteBranches\":true,\"useTickets\":true,\"useDocs\":true,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/helloworld.git\":{\"name\":\"helloworld.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 15, 2008 7:26:48 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/helloworld3.git\":{\"name\":\"test/helloworld3.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 15, 2008 7:26:48 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/bluez-gnome.git\":{\"name\":\"test/bluez-gnome.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Dec 19, 2008 6:35:33 AM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false}}";		
-		Map<String, RepositoryModel> models = JsonUtils.fromJsonString(json, FederationUtils.REPOSITORIES_TYPE);
-		assertEquals(8, models.size());
-	}
-
 	public void testProposal() throws Exception {
 		// create dummy repository data
 		Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
@@ -96,9 +90,9 @@
 
 	public void testPullRepositories() throws Exception {
 		try {
-			String url = FederationUtils.asLink("http://localhost:" + port,
-					"testtoken", FederationRequest.PULL_REPOSITORIES);
-			String json = JsonUtils.retrieveJsonString(url);
+			String url = FederationUtils.asLink("http://localhost:" + port, "testtoken",
+					FederationRequest.PULL_REPOSITORIES);
+			String json = JsonUtils.retrieveJsonString(url, null, null);
 		} catch (IOException e) {
 			if (!e.getMessage().contains("403")) {
 				throw e;
diff --git a/tests/com/gitblit/tests/RpcTests.java b/tests/com/gitblit/tests/RpcTests.java
index e140fb4..cb98a56 100644
--- a/tests/com/gitblit/tests/RpcTests.java
+++ b/tests/com/gitblit/tests/RpcTests.java
@@ -16,25 +16,185 @@
 package com.gitblit.tests;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 
 import junit.framework.TestCase;
 
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.GitBlitException.UnauthorizedException;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.FederationProposal;
+import com.gitblit.models.FederationSet;
 import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
 import com.gitblit.utils.RpcUtils;
 
+/**
+ * Tests all the rpc client utility methods, the rpc filter and rpc servlet.
+ * 
+ * @author James Moger
+ * 
+ */
 public class RpcTests extends TestCase {
 
-	public void testListRepositories() throws Exception {
-		Map<String, RepositoryModel> map = null;
-		try {
-			map = RpcUtils.getRepositories("https://localhost:8443");
-		} catch (IOException e) {
-			if (!e.getMessage().contains("403")) {
-				throw e;
-			}
-		}
+	String url = "https://localhost:8443";
+	String account = "admin";
+	String password = "admin";
+
+	public void testListRepositories() throws IOException {
+		Map<String, RepositoryModel> map = RpcUtils.getRepositories(url, null, null);
 		assertTrue("Repository list is null!", map != null);
 		assertTrue("Repository list is empty!", map.size() > 0);
 	}
+
+	public void testListUsers() throws IOException {
+		List<UserModel> list = null;
+		try {
+			list = RpcUtils.getUsers(url, null, null);
+		} catch (UnauthorizedException e) {
+		}
+		assertTrue("Server allows anyone to admin!", list == null);
+
+		list = RpcUtils.getUsers(url, "admin", "admin".toCharArray());
+		assertTrue("User list is empty!", list.size() > 0);
+	}
+
+	public void testUserAdministration() throws IOException {
+		UserModel user = new UserModel("garbage");
+		user.canAdmin = true;
+		user.password = "whocares";
+
+		// create
+		assertTrue("Failed to create user!",
+				RpcUtils.createUser(user, url, account, password.toCharArray()));
+
+		UserModel retrievedUser = findUser(user.username);
+		assertTrue("Failed to find " + user.username, retrievedUser != null);
+		assertTrue("Retrieved user can not administer Gitblit", retrievedUser.canAdmin);
+
+		// rename and toggle admin permission
+		String originalName = user.username;
+		user.username = "garbage2";
+		user.canAdmin = false;
+		assertTrue("Failed to update user!",
+				RpcUtils.updateUser(originalName, user, url, account, password.toCharArray()));
+
+		retrievedUser = findUser(user.username);
+		assertTrue("Failed to find " + user.username, retrievedUser != null);
+		assertTrue("Retrieved user did not update", !retrievedUser.canAdmin);
+
+		// delete
+		assertTrue("Failed to delete " + user.username,
+				RpcUtils.deleteUser(retrievedUser, url, account, password.toCharArray()));
+
+		retrievedUser = findUser(user.username);
+		assertTrue("Failed to delete " + user.username, retrievedUser == null);
+	}
+
+	private UserModel findUser(String name) throws IOException {
+		List<UserModel> users = RpcUtils.getUsers(url, account, password.toCharArray());
+		UserModel retrievedUser = null;
+		for (UserModel model : users) {
+			if (model.username.equalsIgnoreCase(name)) {
+				retrievedUser = model;
+				break;
+			}
+		}
+		return retrievedUser;
+	}
+
+	public void testRepositoryAdministration() throws IOException {
+		RepositoryModel model = new RepositoryModel();
+		model.name = "garbagerepo.git";
+		model.description = "created by RpcUtils";
+		model.owner = "garbage";
+		model.accessRestriction = AccessRestrictionType.VIEW;
+
+		// create
+		assertTrue("Failed to create repository!",
+				RpcUtils.createRepository(model, url, account, password.toCharArray()));
+
+		RepositoryModel retrievedRepository = findRepository(model.name);
+		assertTrue("Failed to find " + model.name, retrievedRepository != null);
+		assertTrue("Access retriction type is wrong",
+				AccessRestrictionType.VIEW.equals(retrievedRepository.accessRestriction));
+
+		// rename and change access restriciton
+		String originalName = model.name;
+		model.name = "garbagerepo2.git";
+		model.accessRestriction = AccessRestrictionType.PUSH;
+		assertTrue("Failed to update repository!", RpcUtils.updateRepository(originalName, model,
+				url, account, password.toCharArray()));
+
+		retrievedRepository = findRepository(model.name);
+		assertTrue("Failed to find " + model.name, retrievedRepository != null);
+		assertTrue("Access retriction type is wrong",
+				AccessRestrictionType.PUSH.equals(retrievedRepository.accessRestriction));
+
+		// memberships
+		String testMember = "justadded";
+		List<String> members = RpcUtils.getRepositoryMembers(retrievedRepository, url, account,
+				password.toCharArray());
+		assertTrue("Membership roster is not empty!", members.size() == 0);
+		members.add(testMember);
+		assertTrue(
+				"Failed to set memberships!",
+				RpcUtils.setRepositoryMembers(retrievedRepository, members, url, account,
+						password.toCharArray()));
+		members = RpcUtils.getRepositoryMembers(retrievedRepository, url, account,
+				password.toCharArray());
+		boolean foundMember = false;
+		for (String member : members) {
+			if (member.equalsIgnoreCase(testMember)) {
+				foundMember = true;
+				break;
+			}
+		}
+		assertTrue("Failed to find member!", foundMember);
+
+		// delete
+		assertTrue("Failed to delete " + model.name, RpcUtils.deleteRepository(retrievedRepository,
+				url, account, password.toCharArray()));
+
+		retrievedRepository = findRepository(model.name);
+		assertTrue("Failed to delete " + model.name, retrievedRepository == null);
+	}
+
+	private RepositoryModel findRepository(String name) throws IOException {
+		Map<String, RepositoryModel> repositories = RpcUtils.getRepositories(url, account,
+				password.toCharArray());
+		RepositoryModel retrievedRepository = null;
+		for (RepositoryModel model : repositories.values()) {
+			if (model.name.equalsIgnoreCase(name)) {
+				retrievedRepository = model;
+				break;
+			}
+		}
+		return retrievedRepository;
+	}
+
+	public void testFederationRegistrations() throws Exception {
+		List<FederationModel> registrations = RpcUtils.getFederationRegistrations(url, account,
+				password.toCharArray());
+		assertTrue("No federation registrations wre retrieved!", registrations.size() > 0);
+	}
+
+	public void testFederationResultRegistrations() throws Exception {
+		List<FederationModel> registrations = RpcUtils.getFederationResultRegistrations(url,
+				account, password.toCharArray());
+		assertTrue("No federation result registrations were retrieved!", registrations.size() > 0);
+	}
+	
+	public void testFederationProposals() throws Exception {
+		List<FederationProposal> proposals = RpcUtils.getFederationProposals(url,
+				account, password.toCharArray());
+		assertTrue("No federation proposals were retrieved!", proposals.size() > 0);
+	}
+	
+	public void testFederationSets() throws Exception {
+		List<FederationSet> sets = RpcUtils.getFederationSets(url,
+				account, password.toCharArray());
+		assertTrue("No federation sets were retrieved!", sets.size() > 0);
+	}
 }

--
Gitblit v1.9.1