From 93f0b1a11d5a7f7c44cfcb8ff5300bb68b8b8188 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 1 Oct 2011 17:30:48 -0400 Subject: [PATCH] Refactored federation servlet and utils. Started rpc servlet and utils. --- distrib/gitblit.properties | 6 + src/WEB-INF/web.xml | 22 +- src/com/gitblit/Constants.java | 25 ++ src/com/gitblit/FederationServlet.java | 122 +--------- src/com/gitblit/GitBlit.java | 9 +- src/com/gitblit/JsonServlet.java | 103 ++++++++ src/com/gitblit/RpcServlet.java | 107 +++++++++ src/com/gitblit/utils/FederationUtils.java | 225 +++++------------- src/com/gitblit/utils/JsonUtils.java | 224 +++++++++++++++++ src/com/gitblit/utils/RpcUtils.java | 86 +++++++ .../wicket/panels/FederationTokensPanel.java | 8 +- tests/com/gitblit/tests/FederationTests.java | 13 +- tests/com/gitblit/tests/RpcTests.java | 40 ++++ 13 files changed, 692 insertions(+), 298 deletions(-) create mode 100644 src/com/gitblit/JsonServlet.java create mode 100644 src/com/gitblit/RpcServlet.java create mode 100644 src/com/gitblit/utils/JsonUtils.java create mode 100644 src/com/gitblit/utils/RpcUtils.java create mode 100644 tests/com/gitblit/tests/RpcTests.java diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index 5493b25b..2876382d 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -87,6 +87,12 @@ web.siteName = # SINCE 0.5.0 web.allowAdministration = true +# Allows remote clients to list repositories and administer the Gitblit instance +# if they have administrator permissions. +# +# SINCE 0.6.1 +web.enableRpcServlet = false + # Allow dynamic zip downloads. # # SINCE 0.5.0 diff --git a/src/WEB-INF/web.xml b/src/WEB-INF/web.xml index d5577259..0a6cea97 100644 --- a/src/WEB-INF/web.xml +++ b/src/WEB-INF/web.xml @@ -69,7 +69,21 @@ /federation/* - + + + + RpcServlet + com.gitblit.RpcServlet + + + RpcServlet + /rpc/* + + + wicketFilter @@ -136,8 +151,9 @@ * Zipfilter * ZipServlet * com.gitblit.Constants.ZIP_PATH - * FederationServlet --> - git/,feed/,zip/,federation/ + * FederationServlet + * RpcServlet --> + git/,feed/,zip/,federation/,rpc/ diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index f4541c9f..63a877f4 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -52,6 +52,8 @@ public class Constants { 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 = "***********************************************************"; @@ -193,4 +195,27 @@ public class Constants { return name(); } } + + /** + * Enumeration representing the possible remote procedure call requests from + * a client. + */ + public static enum RpcRequest { + LIST_REPOSITORIES, CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY, + LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER; + + public static RpcRequest fromName(String name) { + for (RpcRequest type : values()) { + if (type.name().equalsIgnoreCase(name)) { + return type; + } + } + return LIST_REPOSITORIES; + } + + @Override + public String toString() { + return name(); + } + } } diff --git a/src/com/gitblit/FederationServlet.java b/src/com/gitblit/FederationServlet.java index 7dc5d6a9..0be1066f 100644 --- a/src/com/gitblit/FederationServlet.java +++ b/src/com/gitblit/FederationServlet.java @@ -15,7 +15,6 @@ */ package com.gitblit; -import java.io.BufferedReader; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Date; @@ -23,24 +22,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.gitblit.Constants.FederationRequest; -import com.gitblit.Constants.FederationToken; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; -import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.FederationUtils; import com.gitblit.utils.HttpUtils; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; /** * Handles federation requests. @@ -48,67 +39,25 @@ import com.google.gson.GsonBuilder; * @author James Moger * */ -public class FederationServlet extends HttpServlet { +public class FederationServlet extends JsonServlet { private static final long serialVersionUID = 1L; - private transient Logger logger = LoggerFactory.getLogger(FederationServlet.class); - public FederationServlet() { super(); } /** - * Returns an url to this servlet for the specified parameters. - * - * @param sourceURL - * the url of the source gitblit instance - * @param token - * the federation token of the source gitblit instance - * @param req - * the pull type request - */ - public static String asFederationLink(String sourceURL, String token, FederationRequest req) { - return asFederationLink(sourceURL, null, token, req, null); - } - - /** - * - * @param remoteURL - * the url of the remote gitblit instance - * @param tokenType - * the type of federation token of a gitblit instance - * @param token - * the federation token of a gitblit instance - * @param req - * the pull type request - * @param myURL - * the url of this gitblit instance - * @return - */ - public static String asFederationLink(String remoteURL, FederationToken tokenType, - String token, FederationRequest req, String myURL) { - if (remoteURL.length() > 0 && remoteURL.charAt(remoteURL.length() - 1) == '/') { - remoteURL = remoteURL.substring(0, remoteURL.length() - 1); - } - if (req == null) { - req = FederationRequest.PULL_REPOSITORIES; - } - return remoteURL + Constants.FEDERATION_PATH + "?req=" + req.name().toLowerCase() - + (token == null ? "" : ("&token=" + token)) - + (tokenType == null ? "" : ("&tokenType=" + tokenType.name().toLowerCase())) - + (myURL == null ? "" : ("&url=" + StringUtils.encodeURL(myURL))); - } - - /** - * Returns the list of repositories for federation requests. + * Processes a federation request. * * @param request * @param response * @throws javax.servlet.ServletException * @throws java.io.IOException */ - private void processRequest(javax.servlet.http.HttpServletRequest request, + + @Override + protected void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { FederationRequest reqType = FederationRequest.fromName(request.getParameter("req")); @@ -137,26 +86,11 @@ public class FederationServlet extends HttpServlet { if (FederationRequest.PROPOSAL.equals(reqType)) { // Receive a gitblit federation proposal - BufferedReader reader = request.getReader(); - StringBuilder json = new StringBuilder(); - String line = null; - while ((line = reader.readLine()) != null) { - json.append(line); - } - reader.close(); - - // check to see if we have proposal data - if (json.length() == 0) { - logger.error(MessageFormat.format("Failed to receive proposal data from {0}", - request.getRemoteAddr())); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + FederationProposal proposal = deserialize(request, response, FederationProposal.class); + if (proposal == null) { return; } - - // deserialize the proposal - Gson gson = new Gson(); - FederationProposal proposal = gson.fromJson(json.toString(), FederationProposal.class); - + // reject proposal, if not receipt prohibited if (!GitBlit.getBoolean(Keys.federation.allowProposals, false)) { logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}", @@ -193,25 +127,13 @@ public class FederationServlet extends HttpServlet { String remoteId = StringUtils.decodeFromHtml(request.getParameter("url")); String identification = MessageFormat.format("{0} ({1})", remoteId, request.getRemoteAddr()); - BufferedReader reader = request.getReader(); - StringBuilder json = new StringBuilder(); - String line = null; - while ((line = reader.readLine()) != null) { - json.append(line); - } - reader.close(); - // check to see if we have repository data - if (json.length() == 0) { - logger.error(MessageFormat.format( - "Failed to receive pulled repositories list from {0}", identification)); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + // deserialize the status data + FederationModel results = deserialize(request, response, FederationModel.class); + if (results == null) { return; } - // deserialize the status data - Gson gson = new Gson(); - FederationModel results = gson.fromJson(json.toString(), FederationModel.class); // setup the last and netx pull dates results.lastPull = new Date(); int mins = TimeUtils.convertFrequencyToMinutes(results.frequency); @@ -279,25 +201,7 @@ public class FederationServlet extends HttpServlet { } } - if (result != null) { - // Send JSON response - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(result); - response.getWriter().append(json); - } - } - - @Override - protected void doPost(javax.servlet.http.HttpServletRequest request, - javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, - java.io.IOException { - processRequest(request, response); - } - - @Override - protected void doGet(javax.servlet.http.HttpServletRequest request, - javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, - java.io.IOException { - processRequest(request, response); + // send the result of the request + serialize(response, result); } } diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index c35340ac..e86fcf60 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -63,9 +63,8 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.FederationUtils; import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.JsonUtils; import com.gitblit.utils.StringUtils; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; /** * GitBlit is the servlet context listener singleton that acts as the core for @@ -978,8 +977,7 @@ public class GitBlit implements ServletContextListener { */ public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) { // convert proposal to json - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(proposal); + String json = JsonUtils.toJsonString(proposal); try { // make the proposals folder @@ -1025,10 +1023,9 @@ public class GitBlit implements ServletContextListener { && file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT); } }); - Gson gson = new Gson(); for (File file : files) { String json = com.gitblit.utils.FileUtils.readContent(file, null); - FederationProposal proposal = gson.fromJson(json, FederationProposal.class); + FederationProposal proposal = JsonUtils.fromJsonString(json, FederationProposal.class); list.add(proposal); } } diff --git a/src/com/gitblit/JsonServlet.java b/src/com/gitblit/JsonServlet.java new file mode 100644 index 00000000..b1d1053d --- /dev/null +++ b/src/com/gitblit/JsonServlet.java @@ -0,0 +1,103 @@ +/* + * 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; + +import java.io.BufferedReader; +import java.io.IOException; +import java.text.MessageFormat; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Servlet class for interpreting json requests. + * + * @author James Moger + * + */ +public abstract class JsonServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + protected final Logger logger; + + public JsonServlet() { + super(); + logger = LoggerFactory.getLogger(getClass()); + } + + /** + * Processes an gson request. + * + * @param request + * @param response + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ + protected abstract void processRequest(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException; + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, java.io.IOException { + processRequest(request, response); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + protected X deserialize(HttpServletRequest request, HttpServletResponse response, + Class clazz) throws IOException { + BufferedReader reader = request.getReader(); + StringBuilder json = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + json.append(line); + } + reader.close(); + + if (json.length() == 0) { + logger.error(MessageFormat.format("Failed to receive json data from {0}", + request.getRemoteAddr())); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + Gson gson = new Gson(); + X object = gson.fromJson(json.toString(), clazz); + return object; + } + + protected void serialize(HttpServletResponse response, Object o) throws IOException { + if (o != null) { + // Send JSON response + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(o); + response.getWriter().append(json); + } + } +} diff --git a/src/com/gitblit/RpcServlet.java b/src/com/gitblit/RpcServlet.java new file mode 100644 index 00000000..83fd7337 --- /dev/null +++ b/src/com/gitblit/RpcServlet.java @@ -0,0 +1,107 @@ +/* + * 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; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import com.gitblit.Constants.RpcRequest; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; +import com.gitblit.utils.HttpUtils; + +/** + * Handles remote procedure calls. + * + * @author James Moger + * + */ +public class RpcServlet extends JsonServlet { + + private static final long serialVersionUID = 1L; + + public RpcServlet() { + super(); + } + + /** + * Processes an rpc request. + * + * @param request + * @param response + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ + @Override + protected void processRequest(javax.servlet.http.HttpServletRequest request, + javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, + java.io.IOException { + RpcRequest reqType = RpcRequest.fromName(request.getParameter("req")); + logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType, + request.getRemoteAddr())); + + if (!GitBlit.getBoolean(Keys.web.enableRpcServlet, false)) { + logger.warn(Keys.web.enableRpcServlet + " must be set TRUE for rpc requests."); + response.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + + // TODO user authentication and authorization + UserModel user = null; + + Object result = null; + if (RpcRequest.LIST_REPOSITORIES.equals(reqType)) { + // list repositories + + // Determine the Gitblit clone url + String gitblitUrl = HttpUtils.getGitblitURL(request); + StringBuilder sb = new StringBuilder(); + sb.append(gitblitUrl); + sb.append(Constants.GIT_PATH); + sb.append("{0}"); + String cloneUrl = sb.toString(); + + List list = GitBlit.self().getRepositoryModels(user); + Map repositories = new HashMap(); + for (RepositoryModel model : list) { + String url = MessageFormat.format(cloneUrl, model.name); + repositories.put(url, model); + } + result = repositories; + } else if (RpcRequest.LIST_USERS.equals(reqType)) { + // list users + if (user == null || !user.canAdmin) { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + // user is authorized to retrieve all accounts + List names = GitBlit.self().getAllUsernames(); + List users = new ArrayList(); + for (String name : names) { + users.add(GitBlit.self().getUserModel(name)); + } + result = users; + } + + // send the result of the request + serialize(response, result); + } +} diff --git a/src/com/gitblit/utils/FederationUtils.java b/src/com/gitblit/utils/FederationUtils.java index fde95575..d04a7a31 100644 --- a/src/com/gitblit/utils/FederationUtils.java +++ b/src/com/gitblit/utils/FederationUtils.java @@ -15,17 +15,7 @@ */ package com.gitblit.utils; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; import java.lang.reflect.Type; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.security.SecureRandom; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; @@ -34,28 +24,21 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.Constants; import com.gitblit.Constants.FederationProposalResult; import com.gitblit.Constants.FederationRequest; -import com.gitblit.FederationServlet; +import com.gitblit.Constants.FederationToken; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; /** @@ -66,8 +49,6 @@ import com.google.gson.reflect.TypeToken; */ public class FederationUtils { - public static final String CHARSET; - public static final Type REPOSITORIES_TYPE = new TypeToken>() { }.getType(); @@ -80,23 +61,48 @@ public class FederationUtils { public static final Type RESULTS_TYPE = new TypeToken>() { }.getType(); - private static final SSLContext SSL_CONTEXT; - - private static final DummyHostnameVerifier HOSTNAME_VERIFIER; - private static final Logger LOGGER = LoggerFactory.getLogger(FederationUtils.class); - static { - SSLContext context = null; - try { - context = SSLContext.getInstance("SSL"); - context.init(null, new TrustManager[] { new DummyTrustManager() }, new SecureRandom()); - } catch (Throwable t) { - t.printStackTrace(); + /** + * Returns an url to this servlet for the specified parameters. + * + * @param sourceURL + * the url of the source gitblit instance + * @param token + * the federation token of the source gitblit instance + * @param req + * the pull type request + */ + public static String asLink(String sourceURL, String token, FederationRequest req) { + return asLink(sourceURL, null, token, req, null); + } + + /** + * + * @param remoteURL + * the url of the remote gitblit instance + * @param tokenType + * the type of federation token of a gitblit instance + * @param token + * the federation token of a gitblit instance + * @param req + * the pull type request + * @param myURL + * the url of this gitblit instance + * @return + */ + public static String asLink(String remoteURL, FederationToken tokenType, String token, + FederationRequest req, String myURL) { + if (remoteURL.length() > 0 && remoteURL.charAt(remoteURL.length() - 1) == '/') { + remoteURL = remoteURL.substring(0, remoteURL.length() - 1); + } + if (req == null) { + req = FederationRequest.PULL_REPOSITORIES; } - SSL_CONTEXT = context; - HOSTNAME_VERIFIER = new DummyHostnameVerifier(); - CHARSET = "UTF-8"; + return remoteURL + Constants.FEDERATION_PATH + "?req=" + req.name().toLowerCase() + + (token == null ? "" : ("&token=" + token)) + + (tokenType == null ? "" : ("&tokenType=" + tokenType.name().toLowerCase())) + + (myURL == null ? "" : ("&url=" + StringUtils.encodeURL(myURL))); } /** @@ -195,10 +201,9 @@ public class FederationUtils { * @return true if there is a route to the remoteUrl */ public static boolean poke(String remoteUrl) throws Exception { - String url = FederationServlet.asFederationLink(remoteUrl, null, FederationRequest.POKE); - Gson gson = new Gson(); - String json = gson.toJson("POKE"); - int status = writeJson(url, json); + String url = asLink(remoteUrl, null, FederationRequest.POKE); + String json = JsonUtils.toJsonString("POKE"); + int status = JsonUtils.sendJsonString(url, json); return status == HttpServletResponse.SC_OK; } @@ -213,11 +218,9 @@ public class FederationUtils { */ public static FederationProposalResult propose(String remoteUrl, FederationProposal proposal) throws Exception { - String url = FederationServlet - .asFederationLink(remoteUrl, null, FederationRequest.PROPOSAL); - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(proposal); - int status = writeJson(url, json); + String url = asLink(remoteUrl, null, FederationRequest.PROPOSAL); + String json = JsonUtils.toJsonString(proposal); + int status = JsonUtils.sendJsonString(url, json); switch (status) { case HttpServletResponse.SC_FORBIDDEN: // remote Gitblit Federation disabled @@ -251,9 +254,9 @@ public class FederationUtils { */ public static Map getRepositories(FederationModel registration, boolean checkExclusions) throws Exception { - String url = FederationServlet.asFederationLink(registration.url, registration.token, + String url = asLink(registration.url, registration.token, FederationRequest.PULL_REPOSITORIES); - Map models = readGson(url, REPOSITORIES_TYPE); + Map models = JsonUtils.retrieveJson(url, REPOSITORIES_TYPE); if (checkExclusions) { Map includedModels = new HashMap(); for (Map.Entry entry : models.entrySet()) { @@ -274,9 +277,8 @@ public class FederationUtils { * @throws Exception */ public static Collection getUsers(FederationModel registration) throws Exception { - String url = FederationServlet.asFederationLink(registration.url, registration.token, - FederationRequest.PULL_USERS); - Collection models = readGson(url, USERS_TYPE); + String url = asLink(registration.url, registration.token, FederationRequest.PULL_USERS); + Collection models = JsonUtils.retrieveJson(url, USERS_TYPE); return models; } @@ -289,9 +291,8 @@ public class FederationUtils { * @throws Exception */ public static Map getSettings(FederationModel registration) throws Exception { - String url = FederationServlet.asFederationLink(registration.url, registration.token, - FederationRequest.PULL_SETTINGS); - Map settings = readGson(url, SETTINGS_TYPE); + String url = asLink(registration.url, registration.token, FederationRequest.PULL_SETTINGS); + Map settings = JsonUtils.retrieveJson(url, SETTINGS_TYPE); return settings; } @@ -309,122 +310,10 @@ public class FederationUtils { */ public static boolean acknowledgeStatus(String identification, FederationModel registration) throws Exception { - String url = FederationServlet.asFederationLink(registration.url, null, registration.token, - FederationRequest.STATUS, identification); - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String json = gson.toJson(registration); - int status = writeJson(url, json); + String url = asLink(registration.url, null, registration.token, FederationRequest.STATUS, + identification); + String json = JsonUtils.toJsonString(registration); + int status = JsonUtils.sendJsonString(url, json); return status == HttpServletResponse.SC_OK; } - - /** - * Reads a gson object from the specified url. - * - * @param url - * @param type - * @return - * @throws Exception - */ - public static X readGson(String url, Type type) throws Exception { - String json = readJson(url); - if (StringUtils.isEmpty(json)) { - return null; - } - Gson gson = new Gson(); - return gson.fromJson(json, type); - } - - /** - * Reads a JSON response. - * - * @param url - * @return the JSON response as a string - * @throws Exception - */ - public static String readJson(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); - } - 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(); - } - - /** - * Writes a JSON message to the specified url. - * - * @param url - * the url to write to - * @param json - * the json message to send - * @return the http request result code - * @throws Exception - */ - public static int writeJson(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); - } - - // write json body - OutputStream os = conn.getOutputStream(); - os.write(jsonBytes); - os.close(); - - int status = ((HttpURLConnection) conn).getResponseCode(); - return status; - } - - /** - * DummyTrustManager trusts all certificates. - */ - private static class DummyTrustManager implements X509TrustManager { - - @Override - public void checkClientTrusted(X509Certificate[] certs, String authType) - throws CertificateException { - } - - @Override - public void checkServerTrusted(X509Certificate[] certs, String authType) - throws CertificateException { - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - } - - /** - * Trusts all hostnames from a certificate, including self-signed certs. - */ - private static class DummyHostnameVerifier implements HostnameVerifier { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - } } diff --git a/src/com/gitblit/utils/JsonUtils.java b/src/com/gitblit/utils/JsonUtils.java new file mode 100644 index 00000000..a697b7a5 --- /dev/null +++ b/src/com/gitblit/utils/JsonUtils.java @@ -0,0 +1,224 @@ +/* + * 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.utils; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Map; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +/** + * Utility methods for gson calls to a Gitblit server. + * + * @author James Moger + * + */ +public class JsonUtils { + + public static final String CHARSET; + + public static final Type REPOSITORIES_TYPE = new TypeToken>() { + }.getType(); + + public static final Type USERS_TYPE = new TypeToken>() { + }.getType(); + + private static final SSLContext SSL_CONTEXT; + + private static final DummyHostnameVerifier HOSTNAME_VERIFIER; + + static { + SSLContext context = null; + try { + context = SSLContext.getInstance("SSL"); + context.init(null, new TrustManager[] { new DummyTrustManager() }, new SecureRandom()); + } catch (Throwable t) { + t.printStackTrace(); + } + SSL_CONTEXT = context; + HOSTNAME_VERIFIER = new DummyHostnameVerifier(); + CHARSET = "UTF-8"; + } + + /** + * Creates JSON from the specified object. + * + * @param o + * @return json + */ + public static String toJsonString(Object o) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(o); + return json; + } + + /** + * Convert a json string to an object of the specified type. + * + * @param json + * @param clazz + * @return an object + */ + public static X fromJsonString(String json, Class clazz) { + Gson gson = new Gson(); + return gson.fromJson(json, clazz); + } + + /** + * Convert a json string to an object of the specified type. + * + * @param json + * @param clazz + * @return an object + */ + public static X fromJsonString(String json, Type type) { + Gson gson = new Gson(); + return gson.fromJson(json, type); + } + + /** + * Reads a gson object from the specified url. + * + * @param url + * @param type + * @return + * @throws Exception + */ + public static X retrieveJson(String url, Type type) throws Exception { + String json = retrieveJsonString(url); + if (StringUtils.isEmpty(json)) { + return null; + } + Gson gson = new Gson(); + return gson.fromJson(json, type); + } + + /** + * Retrieves a JSON message. + * + * @param url + * @return the JSON message as a string + * @throws Exception + */ + 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); + } + 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(); + } + + /** + * Sends a JSON message. + * + * @param url + * the url to write to + * @param json + * the json message to send + * @return the http request result code + * @throws Exception + */ + 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); + } + + // write json body + OutputStream os = conn.getOutputStream(); + os.write(jsonBytes); + os.close(); + + int status = ((HttpURLConnection) conn).getResponseCode(); + return status; + } + + /** + * DummyTrustManager trusts all certificates. + */ + private static class DummyTrustManager implements X509TrustManager { + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) + throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) + throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } + + /** + * Trusts all hostnames from a certificate, including self-signed certs. + */ + private static class DummyHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } +} diff --git a/src/com/gitblit/utils/RpcUtils.java b/src/com/gitblit/utils/RpcUtils.java new file mode 100644 index 00000000..919c7bb3 --- /dev/null +++ b/src/com/gitblit/utils/RpcUtils.java @@ -0,0 +1,86 @@ +/* + * 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.utils; + +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Map; + +import com.gitblit.Constants; +import com.gitblit.Constants.RpcRequest; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; +import com.google.gson.reflect.TypeToken; + +/** + * Utility methods for rpc calls. + * + * @author James Moger + * + */ +public class RpcUtils { + + public static final Type REPOSITORIES_TYPE = new TypeToken>() { + }.getType(); + + public static final Type USERS_TYPE = new TypeToken>() { + }.getType(); + + /** + * + * @param remoteURL + * the url of the remote gitblit instance + * @param req + * the rpc request type + * @return + */ + public static String asLink(String remoteURL, RpcRequest req) { + 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(); + } + + /** + * Retrieves a map of the repositories at the remote gitblit instance keyed + * by the repository clone url. + * + * @param serverUrl + * @return a map of cloneable repositories + * @throws Exception + */ + public static Map getRepositories(String serverUrl) throws Exception { + String url = asLink(serverUrl, RpcRequest.LIST_REPOSITORIES); + Map models = JsonUtils.retrieveJson(url, REPOSITORIES_TYPE); + return models; + } + + /** + * Tries to pull the gitblit user accounts from the remote gitblit instance. + * + * @param serverUrl + * @return a collection of UserModel objects + * @throws Exception + */ + public static Collection getUsers(String serverUrl) throws Exception { + String url = asLink(serverUrl, RpcRequest.LIST_USERS); + Collection models = JsonUtils.retrieveJson(url, USERS_TYPE); + return models; + } +} diff --git a/src/com/gitblit/wicket/panels/FederationTokensPanel.java b/src/com/gitblit/wicket/panels/FederationTokensPanel.java index 3a771886..3454492f 100644 --- a/src/com/gitblit/wicket/panels/FederationTokensPanel.java +++ b/src/com/gitblit/wicket/panels/FederationTokensPanel.java @@ -28,9 +28,9 @@ import org.apache.wicket.markup.repeater.data.ListDataProvider; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; -import com.gitblit.FederationServlet; import com.gitblit.GitBlit; import com.gitblit.Keys; +import com.gitblit.utils.FederationUtils; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.pages.SendProposalPage; @@ -42,11 +42,11 @@ public class FederationTokensPanel extends BasePanel { super(wicketId); final String baseUrl = WicketUtils.getGitblitURL(getRequest()); - add(new ExternalLink("federatedUsers", FederationServlet.asFederationLink(baseUrl, GitBlit.self() + add(new ExternalLink("federatedUsers", FederationUtils.asLink(baseUrl, GitBlit.self() .getFederationToken(FederationToken.USERS_AND_REPOSITORIES), FederationRequest.PULL_USERS))); - add(new ExternalLink("federatedSettings", FederationServlet.asFederationLink(baseUrl, GitBlit + add(new ExternalLink("federatedSettings", FederationUtils.asLink(baseUrl, GitBlit .self().getFederationToken(FederationToken.ALL), FederationRequest.PULL_SETTINGS))); final List data = new ArrayList(); @@ -83,7 +83,7 @@ public class FederationTokensPanel extends BasePanel { } item.add(new Label("value", entry[1])); - item.add(new ExternalLink("repositoryDefinitions", FederationServlet.asFederationLink( + item.add(new ExternalLink("repositoryDefinitions", FederationUtils.asLink( baseUrl, entry[1], FederationRequest.PULL_REPOSITORIES))); item.add(new BookmarkablePageLink("send", diff --git a/tests/com/gitblit/tests/FederationTests.java b/tests/com/gitblit/tests/FederationTests.java index c5f7e8d8..8af31671 100644 --- a/tests/com/gitblit/tests/FederationTests.java +++ b/tests/com/gitblit/tests/FederationTests.java @@ -27,12 +27,11 @@ import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.FederationProposalResult; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; -import com.gitblit.FederationServlet; import com.gitblit.GitBlitServer; import com.gitblit.models.FederationProposal; import com.gitblit.models.RepositoryModel; import com.gitblit.utils.FederationUtils; -import com.google.gson.Gson; +import com.gitblit.utils.JsonUtils; public class FederationTests extends TestCase { @@ -66,10 +65,8 @@ public class FederationTests extends TestCase { } 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}}"; - Gson gson = new Gson(); - Map models = gson - .fromJson(json, FederationUtils.REPOSITORIES_TYPE); + 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 models = JsonUtils.fromJsonString(json, FederationUtils.REPOSITORIES_TYPE); assertEquals(8, models.size()); } @@ -99,9 +96,9 @@ public class FederationTests extends TestCase { public void testPullRepositories() throws Exception { try { - String url = FederationServlet.asFederationLink("http://localhost:" + port, + String url = FederationUtils.asLink("http://localhost:" + port, "testtoken", FederationRequest.PULL_REPOSITORIES); - String json = FederationUtils.readJson(url); + String json = JsonUtils.retrieveJsonString(url); } 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 new file mode 100644 index 00000000..e140fb46 --- /dev/null +++ b/tests/com/gitblit/tests/RpcTests.java @@ -0,0 +1,40 @@ +/* + * 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.tests; + +import java.io.IOException; +import java.util.Map; + +import junit.framework.TestCase; + +import com.gitblit.models.RepositoryModel; +import com.gitblit.utils.RpcUtils; + +public class RpcTests extends TestCase { + + public void testListRepositories() throws Exception { + Map map = null; + try { + map = RpcUtils.getRepositories("https://localhost:8443"); + } catch (IOException e) { + if (!e.getMessage().contains("403")) { + throw e; + } + } + assertTrue("Repository list is null!", map != null); + assertTrue("Repository list is empty!", map.size() > 0); + } +} -- 2.39.5