diff options
Diffstat (limited to 'src')
28 files changed, 3009 insertions, 503 deletions
diff --git a/src/WEB-INF/web.xml b/src/WEB-INF/web.xml index d5577259..afe4552d 100644 --- a/src/WEB-INF/web.xml +++ b/src/WEB-INF/web.xml @@ -69,7 +69,21 @@ <url-pattern>/federation/*</url-pattern>
</servlet-mapping>
-
+
+ <!-- Rpc Servlet
+ <url-pattern> MUST match:
+ * com.gitblit.Constants.RPC_PATH
+ * Wicket Filter ignorePaths parameter -->
+ <servlet>
+ <servlet-name>RpcServlet</servlet-name>
+ <servlet-class>com.gitblit.RpcServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>RpcServlet</servlet-name>
+ <url-pattern>/rpc/*</url-pattern>
+ </servlet-mapping>
+
+
<!-- Git Access Restriction Filter
<url-pattern> MUST match:
* GitServlet
@@ -113,6 +127,22 @@ <filter-name>ZipFilter</filter-name>
<url-pattern>/zip/*</url-pattern>
</filter-mapping>
+
+
+ <!-- Rpc Restriction Filter
+ <url-pattern> MUST match:
+ * RpcServlet
+ * com.gitblit.Constants.RPC_PATH
+ * Wicket Filter ignorePaths parameter -->
+ <filter>
+ <filter-name>RpcFilter</filter-name>
+ <filter-class>com.gitblit.RpcFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>RpcFilter</filter-name>
+ <url-pattern>/rpc/*</url-pattern>
+ </filter-mapping>
+
<!-- Wicket Filter -->
<filter>
@@ -136,8 +166,10 @@ * Zipfilter <url-pattern>
* ZipServlet <url-pattern>
* com.gitblit.Constants.ZIP_PATH
- * FederationServlet <url-pattern> -->
- <param-value>git/,feed/,zip/,federation/</param-value>
+ * FederationServlet <url-pattern>
+ * RpcFilter <url-pattern>
+ * RpcServlet <url-pattern> -->
+ <param-value>git/,feed/,zip/,federation/,rpc/</param-value>
</init-param>
</filter>
<filter-mapping>
diff --git a/src/com/gitblit/AccessRestrictionFilter.java b/src/com/gitblit/AccessRestrictionFilter.java index 25adc525..27e2a18a 100644 --- a/src/com/gitblit/AccessRestrictionFilter.java +++ b/src/com/gitblit/AccessRestrictionFilter.java @@ -16,34 +16,23 @@ package com.gitblit;
import java.io.IOException;
-import java.nio.charset.Charset;
-import java.security.Principal;
import java.text.MessageFormat;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import javax.servlet.Filter;
import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.eclipse.jgit.util.Base64;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import com.gitblit.AuthenticationFilter.AuthenticatedRequest;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
/**
- * The AccessRestrictionFilter is a servlet filter that preprocesses requests
- * that match its url pattern definition in the web.xml file.
+ * The AccessRestrictionFilter is an AuthenticationFilter that confirms that the
+ * requested repository can be accessed by the anonymous or named user.
*
* The filter extracts the name of the repository from the url and determines if
* the requested action for the repository requires a Basic authentication
@@ -55,19 +44,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger
*
*/
-public abstract class AccessRestrictionFilter implements Filter {
-
- private static final String BASIC = "Basic";
-
- private static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";
-
- private static final String SESSION_SECURED = "com.gitblit.secured";
-
- protected transient Logger logger;
-
- public AccessRestrictionFilter() {
- logger = LoggerFactory.getLogger(getClass());
- }
+public abstract class AccessRestrictionFilter extends AuthenticationFilter {
/**
* Extract the repository name from the url.
@@ -118,26 +95,7 @@ public abstract class AccessRestrictionFilter implements Filter { HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
- // Wrap the HttpServletRequest with the AccessRestrictionRequest which
- // overrides the servlet container user principal methods.
- // JGit requires either:
- //
- // 1. servlet container authenticated user
- // 2. http.receivepack = true in each repository's config
- //
- // Gitblit must conditionally authenticate users per-repository so just
- // enabling http.receivepack is insufficient.
-
- AccessRestrictionRequest accessRequest = new AccessRestrictionRequest(httpRequest);
-
- String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath();
- String url = httpRequest.getRequestURI().substring(servletUrl.length());
- String params = httpRequest.getQueryString();
- if (url.length() > 0 && url.charAt(0) == '/') {
- url = url.substring(1);
- }
- String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));
-
+ String fullUrl = getFullUrl(httpRequest);
String repository = extractRepositoryName(fullUrl);
// Determine if the request URL is restricted
@@ -148,145 +106,64 @@ public abstract class AccessRestrictionFilter implements Filter { RepositoryModel model = GitBlit.self().getRepositoryModel(repository);
if (model == null) {
// repository not found. send 404.
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_NOT_FOUND + ")");
+ logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,
+ HttpServletResponse.SC_NOT_FOUND));
httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
+ // Wrap the HttpServletRequest with the AccessRestrictionRequest which
+ // overrides the servlet container user principal methods.
+ // JGit requires either:
+ //
+ // 1. servlet container authenticated user
+ // 2. http.receivepack = true in each repository's config
+ //
+ // Gitblit must conditionally authenticate users per-repository so just
+ // enabling http.receivepack is insufficient.
+ AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);
+ UserModel user = getUser(httpRequest);
+ if (user != null) {
+ authenticatedRequest.setUser(user);
+ }
+
// BASIC authentication challenge and response processing
if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model)) {
- // look for client authorization credentials in header
- final String authorization = httpRequest.getHeader("Authorization");
- if (authorization != null && authorization.startsWith(BASIC)) {
- // Authorization: Basic base64credentials
- String base64Credentials = authorization.substring(BASIC.length()).trim();
- String credentials = new String(Base64.decode(base64Credentials),
- Charset.forName("UTF-8"));
- // credentials = username:password
- final String[] values = credentials.split(":");
-
- if (values.length == 2) {
- String username = values[0];
- char[] password = values[1].toCharArray();
- UserModel user = GitBlit.self().authenticate(username, password);
- if (user != null) {
- accessRequest.setUser(user);
- if (user.canAdmin || canAccess(model, user, urlRequestType)) {
- // authenticated request permitted.
- // pass processing to the restricted servlet.
- newSession(accessRequest, httpResponse);
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE
- + ") authenticated");
- chain.doFilter(accessRequest, httpResponse);
- return;
- }
- // valid user, but not for requested access. send 403.
- if (GitBlit.isDebugMode()) {
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_FORBIDDEN
- + ")");
- logger.info(MessageFormat.format("AUTH: {0} forbidden to access {1}",
- user.username, url));
- }
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
- return;
- }
+ if (user == null) {
+ // challenge client to provide credentials. send 401.
+ if (GitBlit.isDebugMode()) {
+ logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));
+ }
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ } else {
+ // check user access for request
+ if (user.canAdmin || canAccess(model, user, urlRequestType)) {
+ // authenticated request permitted.
+ // pass processing to the restricted servlet.
+ newSession(authenticatedRequest, httpResponse);
+ logger.info(MessageFormat.format("ARF: {0} ({1}) authenticated", fullUrl,
+ HttpServletResponse.SC_CONTINUE));
+ chain.doFilter(authenticatedRequest, httpResponse);
+ return;
}
+ // valid user, but not for requested access. send 403.
if (GitBlit.isDebugMode()) {
- logger.info(MessageFormat
- .format("AUTH: invalid credentials ({0})", credentials));
+ logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",
+ user.username, fullUrl));
}
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
}
-
- // challenge client to provide credentials. send 401.
- if (GitBlit.isDebugMode()) {
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_UNAUTHORIZED + ")");
- logger.info("AUTH: Challenge " + CHALLENGE);
- }
- httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
- httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
- return;
}
if (GitBlit.isDebugMode()) {
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE
- + ") unauthenticated");
+ logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,
+ HttpServletResponse.SC_CONTINUE));
}
// unauthenticated request permitted.
// pass processing to the restricted servlet.
- chain.doFilter(accessRequest, httpResponse);
- }
-
- /**
- * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()
- */
- protected void newSession(HttpServletRequest request, HttpServletResponse response) {
- HttpSession oldSession = request.getSession(false);
- if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {
- synchronized (this) {
- Map<String, Object> attributes = new HashMap<String, Object>();
- Enumeration<String> e = oldSession.getAttributeNames();
- while (e.hasMoreElements()) {
- String name = e.nextElement();
- attributes.put(name, oldSession.getAttribute(name));
- oldSession.removeAttribute(name);
- }
- oldSession.invalidate();
-
- HttpSession newSession = request.getSession(true);
- newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);
- for (Map.Entry<String, Object> entry : attributes.entrySet()) {
- newSession.setAttribute(entry.getKey(), entry.getValue());
- }
- }
- }
- }
-
- /**
- * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
- */
- @Override
- public void init(final FilterConfig config) throws ServletException {
- }
-
- /**
- * @see javax.servlet.Filter#destroy()
- */
- @Override
- public void destroy() {
- }
-
- /**
- * Wraps a standard HttpServletRequest and overrides user principal methods.
- */
- public static class AccessRestrictionRequest extends ServletRequestWrapper {
-
- private UserModel user;
-
- public AccessRestrictionRequest(HttpServletRequest req) {
- super(req);
- user = new UserModel("anonymous");
- }
-
- void setUser(UserModel user) {
- this.user = user;
- }
-
- @Override
- public String getRemoteUser() {
- return user.username;
- }
-
- @Override
- public boolean isUserInRole(String role) {
- if (role.equals(Constants.ADMIN_ROLE)) {
- return user.canAdmin;
- }
- return user.canAccessRepository(role);
- }
-
- @Override
- public Principal getUserPrincipal() {
- return user;
- }
+ chain.doFilter(authenticatedRequest, httpResponse);
}
}
\ No newline at end of file diff --git a/src/com/gitblit/AuthenticationFilter.java b/src/com/gitblit/AuthenticationFilter.java new file mode 100644 index 00000000..6b7a0af2 --- /dev/null +++ b/src/com/gitblit/AuthenticationFilter.java @@ -0,0 +1,201 @@ +/*
+ * 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.IOException;
+import java.nio.charset.Charset;
+import java.security.Principal;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jgit.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * The AuthenticationFilter is a servlet filter that preprocesses requests that
+ * match its url pattern definition in the web.xml file.
+ *
+ * http://en.wikipedia.org/wiki/Basic_access_authentication
+ *
+ * @author James Moger
+ *
+ */
+public abstract class AuthenticationFilter implements Filter {
+
+ protected static final String BASIC = "Basic";
+
+ protected static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";
+
+ protected static final String SESSION_SECURED = "com.gitblit.secured";
+
+ protected transient Logger logger = LoggerFactory.getLogger(getClass());
+
+ /**
+ * doFilter does the actual work of preprocessing the request to ensure that
+ * the user may proceed.
+ *
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse, javax.servlet.FilterChain)
+ */
+ @Override
+ public abstract void doFilter(final ServletRequest request, final ServletResponse response,
+ final FilterChain chain) throws IOException, ServletException;
+
+ /**
+ * Returns the full relative url of the request.
+ *
+ * @param httpRequest
+ * @return url
+ */
+ protected String getFullUrl(HttpServletRequest httpRequest) {
+ String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath();
+ String url = httpRequest.getRequestURI().substring(servletUrl.length());
+ String params = httpRequest.getQueryString();
+ if (url.length() > 0 && url.charAt(0) == '/') {
+ url = url.substring(1);
+ }
+ String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));
+ return fullUrl;
+ }
+
+ /**
+ * Returns the user making the request, if the user has authenticated.
+ *
+ * @param httpRequest
+ * @return user
+ */
+ protected UserModel getUser(HttpServletRequest httpRequest) {
+ UserModel user = null;
+ // look for client authorization credentials in header
+ final String authorization = httpRequest.getHeader("Authorization");
+ if (authorization != null && authorization.startsWith(BASIC)) {
+ // Authorization: Basic base64credentials
+ String base64Credentials = authorization.substring(BASIC.length()).trim();
+ String credentials = new String(Base64.decode(base64Credentials),
+ Charset.forName("UTF-8"));
+ // credentials = username:password
+ final String[] values = credentials.split(":");
+
+ if (values.length == 2) {
+ String username = values[0];
+ char[] password = values[1].toCharArray();
+ user = GitBlit.self().authenticate(username, password);
+ if (user != null) {
+ return user;
+ }
+ }
+ if (GitBlit.isDebugMode()) {
+ logger.info(MessageFormat.format("AUTH: invalid credentials ({0})", credentials));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()
+ */
+ @SuppressWarnings("unchecked")
+ protected void newSession(HttpServletRequest request, HttpServletResponse response) {
+ HttpSession oldSession = request.getSession(false);
+ if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {
+ synchronized (this) {
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ Enumeration<String> e = oldSession.getAttributeNames();
+ while (e.hasMoreElements()) {
+ String name = e.nextElement();
+ attributes.put(name, oldSession.getAttribute(name));
+ oldSession.removeAttribute(name);
+ }
+ oldSession.invalidate();
+
+ HttpSession newSession = request.getSession(true);
+ newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);
+ for (Map.Entry<String, Object> entry : attributes.entrySet()) {
+ newSession.setAttribute(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ /**
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
+ */
+ @Override
+ public void init(final FilterConfig config) throws ServletException {
+ }
+
+ /**
+ * @see javax.servlet.Filter#destroy()
+ */
+ @Override
+ public void destroy() {
+ }
+
+ /**
+ * Wraps a standard HttpServletRequest and overrides user principal methods.
+ */
+ public static class AuthenticatedRequest extends ServletRequestWrapper {
+
+ private UserModel user;
+
+ public AuthenticatedRequest(HttpServletRequest req) {
+ super(req);
+ user = new UserModel("anonymous");
+ }
+
+ UserModel getUser() {
+ return user;
+ }
+
+ void setUser(UserModel user) {
+ this.user = user;
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return user.username;
+ }
+
+ @Override
+ public boolean isUserInRole(String role) {
+ if (role.equals(Constants.ADMIN_ROLE)) {
+ return user.canAdmin;
+ }
+ return user.canAccessRepository(role);
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return user;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index a5b7c4f1..52af3e91 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -53,6 +53,8 @@ public class Constants { public static final String FEDERATION_PATH = "/federation/";
+ public static final String RPC_PATH = "/rpc/";
+
public static final String BORDER = "***********************************************************";
public static final String FEDERATION_USER = "$gitblit";
@@ -193,4 +195,33 @@ 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, 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)) {
+ return type;
+ }
+ }
+ 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 ef089d03..b190e7b5 100644 --- a/src/com/gitblit/FederationPullExecutor.java +++ b/src/com/gitblit/FederationPullExecutor.java @@ -19,6 +19,7 @@ import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT; 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 org.slf4j.LoggerFactory; 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 @@ public class FederationPullExecutor implements Runnable { *
* @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 @@ public class FederationPullExecutor implements Runnable { } else {
repositoryName = registrationFolder + "/" + repository.name;
}
-
+
if (registration.bare) {
// bare repository, ensure .git suffix
if (!repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
@@ -178,7 +180,8 @@ public class FederationPullExecutor implements Runnable { } 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 @@ public class FederationPullExecutor implements Runnable { 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 @@ public class FederationPullExecutor implements Runnable { 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 @@ public class FederationPullExecutor implements Runnable { // 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 @@ public class FederationPullExecutor implements Runnable { }
}
}
- } 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 @@ public class FederationPullExecutor implements Runnable { 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/FederationServlet.java b/src/com/gitblit/FederationServlet.java index 2726a8ac..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,14 +22,9 @@ 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.UserModel;
@@ -38,8 +32,6 @@ 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.
@@ -47,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"));
@@ -136,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}",
@@ -192,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);
@@ -278,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..1727038c 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -59,13 +59,14 @@ import com.gitblit.Constants.FederationStrategy; 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.ByteFormat;
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
@@ -530,6 +531,10 @@ public class GitBlit implements ServletContextListener { model.origin = config.getString("remote", "origin", "url");
}
r.close();
+ if (getBoolean(Keys.web.showRepositorySizes, true)) {
+ ByteFormat byteFormat = new ByteFormat();
+ model.size = byteFormat.format(calculateSize(model));
+ }
return model;
}
@@ -875,6 +880,29 @@ public class GitBlit implements ServletContextListener { }
/**
+ * 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
@@ -978,8 +1006,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 +1052,10 @@ 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/GitBlitException.java b/src/com/gitblit/GitBlitException.java index 032e41f7..af32003a 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 new file mode 100644 index 00000000..a7958969 --- /dev/null +++ b/src/com/gitblit/JsonServlet.java @@ -0,0 +1,122 @@ +/*
+ * 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.lang.reflect.Type;
+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.gitblit.utils.JsonUtils;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * 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> X deserialize(HttpServletRequest request, HttpServletResponse response,
+ Class<X> clazz) throws IOException {
+ String json = readJson(request, response);
+ if (StringUtils.isEmpty(json)) {
+ return null;
+ }
+
+ X object = JsonUtils.fromJsonString(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;
+ }
+
+ X object = JsonUtils.fromJsonString(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;
+ 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;
+ }
+ return json.toString();
+ }
+
+ protected void serialize(HttpServletResponse response, Object o) throws IOException {
+ if (o != null) {
+ // Send JSON response
+ String json = JsonUtils.toJsonString(o);
+ response.getWriter().append(json);
+ }
+ }
+}
diff --git a/src/com/gitblit/RpcFilter.java b/src/com/gitblit/RpcFilter.java new file mode 100644 index 00000000..f92dd962 --- /dev/null +++ b/src/com/gitblit/RpcFilter.java @@ -0,0 +1,139 @@ +/*
+ * 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.IOException;
+import java.text.MessageFormat;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.gitblit.Constants.RpcRequest;
+import com.gitblit.models.UserModel;
+
+/**
+ * The RpcFilter is a servlet filter that secures the RpcServlet.
+ *
+ * The filter extracts the rpc request type from the url and determines if the
+ * requested action requires a Basic authentication prompt. If authentication is
+ * required and no credentials are stored in the "Authorization" header, then a
+ * basic authentication challenge is issued.
+ *
+ * http://en.wikipedia.org/wiki/Basic_access_authentication
+ *
+ * @author James Moger
+ *
+ */
+public class RpcFilter extends AuthenticationFilter {
+
+ /**
+ * doFilter does the actual work of preprocessing the request to ensure that
+ * the user may proceed.
+ *
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse, javax.servlet.FilterChain)
+ */
+ @Override
+ public void doFilter(final ServletRequest request, final ServletResponse response,
+ final FilterChain chain) throws IOException, ServletException {
+
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+ String fullUrl = getFullUrl(httpRequest);
+ RpcRequest requestType = RpcRequest.fromName(httpRequest.getParameter("req"));
+
+ boolean adminRequest = requestType.exceeds(RpcRequest.LIST_REPOSITORIES);
+
+ // conditionally reject all rpc requests
+ if (!GitBlit.getBoolean(Keys.web.enableRpcServlet, true)) {
+ logger.warn(Keys.web.enableRpcServlet + " must be set TRUE for rpc requests.");
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, false);
+ boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
+
+ // Wrap the HttpServletRequest with the RpcServletnRequest which
+ // overrides the servlet container user principal methods.
+ AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);
+ UserModel user = getUser(httpRequest);
+ if (user != null) {
+ authenticatedRequest.setUser(user);
+ }
+
+ // conditionally reject rpc administration requests
+ if (adminRequest && !GitBlit.getBoolean(Keys.web.enableRpcAdministration, false)) {
+ logger.warn(Keys.web.enableRpcAdministration + " must be set TRUE for administrative rpc requests.");
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ // BASIC authentication challenge and response processing
+ if ((adminRequest && authenticateAdmin) || (!adminRequest && authenticateView)) {
+ if (user == null) {
+ // challenge client to provide credentials. send 401.
+ if (GitBlit.isDebugMode()) {
+ logger.info(MessageFormat.format("RPC: CHALLENGE {0}", fullUrl));
+
+ }
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ } else {
+ // check user access for request
+ if (user.canAdmin || canAccess(user, requestType)) {
+ // authenticated request permitted.
+ // pass processing to the restricted servlet.
+ newSession(authenticatedRequest, httpResponse);
+ logger.info(MessageFormat.format("RPC: {0} ({1}) authenticated", fullUrl,
+ HttpServletResponse.SC_CONTINUE));
+ chain.doFilter(authenticatedRequest, httpResponse);
+ return;
+ }
+ // valid user, but not for requested access. send 403.
+ if (GitBlit.isDebugMode()) {
+ logger.info(MessageFormat.format("RPC: {0} forbidden to access {1}",
+ user.username, fullUrl));
+ }
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+ }
+
+ if (GitBlit.isDebugMode()) {
+ logger.info(MessageFormat.format("RPC: {0} ({1}) unauthenticated", fullUrl,
+ HttpServletResponse.SC_CONTINUE));
+ }
+ // unauthenticated request permitted.
+ // pass processing to the restricted servlet.
+ chain.doFilter(authenticatedRequest, httpResponse);
+ }
+
+ private boolean canAccess(UserModel user, RpcRequest requestType) {
+ switch (requestType) {
+ case LIST_REPOSITORIES:
+ return true;
+ default:
+ return user.canAdmin;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/com/gitblit/RpcServlet.java b/src/com/gitblit/RpcServlet.java new file mode 100644 index 00000000..4dee3190 --- /dev/null +++ b/src/com/gitblit/RpcServlet.java @@ -0,0 +1,171 @@ +/*
+ * 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.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;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+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;
+import com.gitblit.utils.RpcUtils;
+
+/**
+ * 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(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()));
+
+ UserModel user = (UserModel) request.getUserPrincipal();
+
+ Object result = null;
+ if (RpcRequest.LIST_REPOSITORIES.equals(reqType)) {
+ // 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 repositories
+ List<RepositoryModel> list = GitBlit.self().getRepositoryModels(user);
+ Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
+ 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
+ List<String> names = GitBlit.self().getAllUsernames();
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (String name : names) {
+ 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
+ serialize(response, result);
+ }
+}
diff --git a/src/com/gitblit/build/Build.java b/src/com/gitblit/build/Build.java index c4f373e8..6ca8cb6b 100644 --- a/src/com/gitblit/build/Build.java +++ b/src/com/gitblit/build/Build.java @@ -48,7 +48,11 @@ import com.gitblit.utils.StringUtils; *
*/
public class Build {
-
+
+ public interface DownloadListener {
+ public void downloading(String name);
+ }
+
/**
* BuildType enumeration representing compile-time or runtime. This is used
* to download dependencies either for Gitblit GO runtime or for setting up
@@ -57,6 +61,8 @@ public class Build { public static enum BuildType {
RUNTIME, COMPILETIME;
}
+
+ private static DownloadListener downloadListener;
public static void main(String... args) {
runtime();
@@ -128,6 +134,14 @@ public class Build { downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);
}
+
+ public static void rpcClient(DownloadListener listener) {
+ downloadListener = listener;
+ downloadFromApache(MavenObject.GSON, BuildType.RUNTIME);
+ downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME);
+
+ downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);
+ }
/**
* Builds the Keys class based on the gitblit.properties file and inserts
@@ -273,6 +287,9 @@ public class Build { throw new RuntimeException("Failed to create destination folder structure!");
}
}
+ if (downloadListener != null) {
+ downloadListener.downloading(mo.name);
+ }
ByteArrayOutputStream buff = new ByteArrayOutputStream();
try {
URL url = new URL(mavenURL);
diff --git a/src/com/gitblit/client/ClosableTabComponent.java b/src/com/gitblit/client/ClosableTabComponent.java new file mode 100644 index 00000000..a121806a --- /dev/null +++ b/src/com/gitblit/client/ClosableTabComponent.java @@ -0,0 +1,149 @@ +/* + * Copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.client; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.plaf.basic.BasicButtonUI; + +/** + * Closable tab control. + */ +public class ClosableTabComponent extends JPanel { + + private static final long serialVersionUID = 1L; + + private static final MouseListener BUTTON_MOUSE_LISTENER = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + Component component = e.getComponent(); + if (component instanceof AbstractButton) { + AbstractButton button = (AbstractButton) component; + button.setBorderPainted(true); + } + } + + public void mouseExited(MouseEvent e) { + Component component = e.getComponent(); + if (component instanceof AbstractButton) { + AbstractButton button = (AbstractButton) component; + button.setBorderPainted(false); + } + } + }; + + private final JTabbedPane pane; + private final JLabel label; + private final JButton button = new TabButton(); + + private final CloseTabListener closeListener; + + public interface CloseTabListener { + void closeTab(Component c); + } + + public ClosableTabComponent(String title, ImageIcon icon, JTabbedPane pane, + CloseTabListener closeListener) { + super(new FlowLayout(FlowLayout.LEFT, 0, 0)); + this.closeListener = closeListener; + + if (pane == null) { + throw new NullPointerException("TabbedPane is null"); + } + this.pane = pane; + setOpaque(false); + label = new JLabel(title); + if (icon != null) { + label.setIcon(icon); + } + + add(label); + label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); + add(button); + setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0)); + } + + private class TabButton extends JButton implements ActionListener { + + private static final long serialVersionUID = 1L; + + public TabButton() { + int size = 17; + setPreferredSize(new Dimension(size, size)); + setToolTipText("Close"); + setUI(new BasicButtonUI()); + setContentAreaFilled(false); + setFocusable(false); + setBorder(BorderFactory.createEtchedBorder()); + setBorderPainted(false); + addMouseListener(BUTTON_MOUSE_LISTENER); + setRolloverEnabled(true); + addActionListener(this); + } + + public void actionPerformed(ActionEvent e) { + int i = pane.indexOfTabComponent(ClosableTabComponent.this); + Component c = pane.getComponentAt(i); + if (i != -1) { + pane.remove(i); + } + if (closeListener != null) { + closeListener.closeTab(c); + } + } + + public void updateUI() { + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + Stroke stroke = g2.getStroke(); + g2.setStroke(new BasicStroke(2)); + g.setColor(Color.BLACK); + if (getModel().isRollover()) { + Color highlight = new Color(0, 51, 153); + g.setColor(highlight); + } + int delta = 5; + g.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1); + g.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1); + g2.setStroke(stroke); + + int i = pane.indexOfTabComponent(ClosableTabComponent.this); + pane.setTitleAt(i, label.getText()); + } + } +} diff --git a/src/com/gitblit/client/DateCellRenderer.java b/src/com/gitblit/client/DateCellRenderer.java new file mode 100644 index 00000000..053cf52f --- /dev/null +++ b/src/com/gitblit/client/DateCellRenderer.java @@ -0,0 +1,59 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.JTable;
+import javax.swing.SwingConstants;
+import javax.swing.table.DefaultTableCellRenderer;
+
+import com.gitblit.utils.TimeUtils;
+
+/**
+ * Time ago cell renderer with real date tooltip.
+ *
+ * @author James Moger
+ *
+ */
+public class DateCellRenderer extends DefaultTableCellRenderer {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String pattern;
+
+ public DateCellRenderer(String pattern, Color foreground) {
+ this.pattern = (pattern == null ? "yyyy-MM-dd HH:mm" : pattern);
+ setForeground(foreground);
+ setHorizontalAlignment(SwingConstants.CENTER);
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
+ boolean hasFocus, int row, int column) {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ if (value instanceof Date) {
+ Date date = (Date) value;
+ String timeAgo = TimeUtils.timeAgo(date);
+ String strDate = new SimpleDateFormat(pattern).format((Date) value);
+ this.setText(timeAgo);
+ this.setToolTipText(strDate);
+ }
+ return this;
+ }
+}
\ No newline at end of file diff --git a/src/com/gitblit/client/GitblitClient.java b/src/com/gitblit/client/GitblitClient.java new file mode 100644 index 00000000..51d8e7eb --- /dev/null +++ b/src/com/gitblit/client/GitblitClient.java @@ -0,0 +1,198 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.UIManager;
+
+import com.gitblit.Constants;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Sample RPC application.
+ *
+ * @author James Moger
+ *
+ */
+public class GitblitClient extends JFrame {
+
+ private static final long serialVersionUID = 1L;
+ private JTabbedPane serverTabs;
+ private GitblitRegistration localhost = new GitblitRegistration("default",
+ "https://localhost:8443", "admin", "admin".toCharArray());
+
+ private List<GitblitRegistration> registrations = new ArrayList<GitblitRegistration>();
+ private JMenu recentMenu;
+
+ private GitblitClient() {
+ super();
+ }
+
+ private void initialize() {
+ setContentPane(getCenterPanel());
+ setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
+
+ setTitle("Gitblit RPC Client v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(950, 600);
+ }
+
+ public void setVisible(boolean value) {
+ if (value) {
+ if (registrations.size() == 0) {
+ // default prompt
+ if (loginPrompt(localhost)) {
+ pack();
+ }
+ } else if (registrations.size() == 1) {
+ // single registration prompt
+ if (loginPrompt(registrations.get(0))) {
+ pack();
+ }
+ }
+ super.setVisible(value);
+ setLocationRelativeTo(null);
+ }
+ }
+
+ private JMenuBar setupMenu() {
+ JMenuBar menuBar = new JMenuBar();
+ JMenu serversMenu = new JMenu("Servers");
+ menuBar.add(serversMenu);
+ recentMenu = new JMenu("Recent");
+ serversMenu.add(recentMenu);
+ JMenuItem login = new JMenuItem("Login...");
+ login.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, KeyEvent.CTRL_DOWN_MASK, false));
+ login.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ loginPrompt(localhost);
+ }
+ });
+ serversMenu.add(login);
+ return menuBar;
+ }
+
+ private JPanel newLabelPanel(String text, JTextField field) {
+ JLabel label = new JLabel(text);
+ label.setPreferredSize(new Dimension(75, 10));
+ JPanel jpanel = new JPanel(new BorderLayout());
+ jpanel.add(label, BorderLayout.WEST);
+ jpanel.add(field, BorderLayout.CENTER);
+ return jpanel;
+ }
+
+ private JPanel getCenterPanel() {
+ serverTabs = new JTabbedPane(JTabbedPane.TOP);
+ JMenuBar menubar = setupMenu();
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(menubar, BorderLayout.NORTH);
+ panel.add(serverTabs, BorderLayout.CENTER);
+ return panel;
+ }
+
+ private boolean loginPrompt(GitblitRegistration reg) {
+ JTextField urlField = new JTextField(reg.url, 30);
+ JTextField nameField = new JTextField(reg.name);
+ JTextField accountField = new JTextField(reg.account);
+ JPasswordField passwordField = new JPasswordField(new String(reg.password));
+
+ JPanel panel = new JPanel(new GridLayout(0, 1, 5, 5));
+ panel.add(newLabelPanel("name", nameField));
+ panel.add(newLabelPanel("url", urlField));
+ panel.add(newLabelPanel("account", accountField));
+ panel.add(newLabelPanel("password", passwordField));
+
+ int result = JOptionPane.showConfirmDialog(GitblitClient.this, panel, "Login",
+ JOptionPane.OK_CANCEL_OPTION);
+ if (result != JOptionPane.OK_OPTION) {
+ return false;
+ }
+ String url = urlField.getText();
+ if (StringUtils.isEmpty(url)) {
+ return false;
+ }
+ reg = new GitblitRegistration(nameField.getText(), url, accountField.getText(),
+ passwordField.getPassword());
+ login(reg);
+ registrations.add(0, reg);
+ rebuildRecentMenu();
+ return true;
+ }
+
+ private void login(GitblitRegistration reg) {
+ try {
+ GitblitPanel panel = new GitblitPanel(reg);
+ panel.login();
+ serverTabs.addTab(reg.name, panel);
+ int idx = serverTabs.getTabCount() - 1;
+ serverTabs.setSelectedIndex(idx);
+ serverTabs.setTabComponentAt(idx, new ClosableTabComponent(reg.name, null, serverTabs,
+ panel));
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(GitblitClient.this, e.getMessage(), "Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private void rebuildRecentMenu() {
+ recentMenu.removeAll();
+ for (final GitblitRegistration reg : registrations) {
+ JMenuItem item = new JMenuItem(reg.name);
+ item.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ login(reg);
+ }
+ });
+ recentMenu.add(item);
+ }
+ }
+
+ public static void main(String[] args) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ }
+ GitblitClient frame = new GitblitClient();
+ frame.initialize();
+ frame.setVisible(true);
+ }
+ });
+ }
+}
diff --git a/src/com/gitblit/client/GitblitClientLauncher.java b/src/com/gitblit/client/GitblitClientLauncher.java new file mode 100644 index 00000000..19e9efd8 --- /dev/null +++ b/src/com/gitblit/client/GitblitClientLauncher.java @@ -0,0 +1,105 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.SplashScreen;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import com.gitblit.Launcher;
+import com.gitblit.build.Build;
+import com.gitblit.build.Build.DownloadListener;
+
+/**
+ * Downloads dependencies and launches RPC client.
+ *
+ * @author James Moger
+ *
+ */
+public class GitblitClientLauncher {
+
+ public static void main(String[] args) {
+ final SplashScreen splash = SplashScreen.getSplashScreen();
+
+ DownloadListener downloadListener = new DownloadListener() {
+ @Override
+ public void downloading(String name) {
+ updateSplash(splash, "Downloading " + name + "...");
+ }
+ };
+
+ // download rpc client runtime dependencies
+ Build.rpcClient(downloadListener);
+
+ updateSplash(splash, "Scanning Library Folder...");
+ File libFolder = new File("ext");
+ List<File> jars = Launcher.findJars(libFolder.getAbsoluteFile());
+
+ // sort the jars by name and then reverse the order so the newer version
+ // of the library gets loaded in the event that this is an upgrade
+ Collections.sort(jars);
+ Collections.reverse(jars);
+ for (File jar : jars) {
+ try {
+ updateSplash(splash, "Loading " + jar.getName() + "...");
+ Launcher.addJarFile(jar);
+ } catch (IOException e) {
+
+ }
+ }
+
+ updateSplash(splash, "Starting Gitblit RPC Client...");
+ GitblitClient.main(args);
+ }
+
+ private static void updateSplash(final SplashScreen splash, final String string) {
+ if (splash == null) {
+ return;
+ }
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ Graphics2D g = splash.createGraphics();
+ if (g != null) {
+ // Splash is 320x120
+ FontMetrics fm = g.getFontMetrics();
+ g.setColor(Color.darkGray);
+ int h = fm.getHeight() + fm.getMaxDescent();
+ int x = 5;
+ int y = 115;
+ int w = 320 - 2 * x;
+ g.fillRect(x, y - h, w, h);
+ g.setColor(Color.lightGray);
+ g.drawRect(x, y - h, w, h);
+ g.setColor(Color.WHITE);
+ int xw = fm.stringWidth(string);
+ g.drawString(string, x + ((w - xw) / 2), y - 5);
+ g.dispose();
+ splash.update();
+ }
+ }
+ });
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/src/com/gitblit/client/GitblitPanel.java b/src/com/gitblit/client/GitblitPanel.java new file mode 100644 index 00000000..5482593d --- /dev/null +++ b/src/com/gitblit/client/GitblitPanel.java @@ -0,0 +1,468 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.net.URI;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.RowFilter;
+import javax.swing.SwingConstants;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableRowSorter;
+
+import com.gitblit.GitBlitException.ForbiddenException;
+import com.gitblit.client.ClosableTabComponent.CloseTabListener;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.RpcUtils;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * GitblitPanel performs the login, all business logic, and contains all widgets
+ * to represent the state of a repository for the given account credentials.
+ *
+ * @author James Moger
+ *
+ */
+public class GitblitPanel extends JPanel implements CloseTabListener {
+
+ private static final long serialVersionUID = 1L;
+
+ private final int margin = 5;
+
+ private final Insets insets = new Insets(margin, margin, margin, margin);
+
+ private String url;
+
+ private String account;
+
+ private char[] password;
+
+ private boolean isAdmin;
+
+ private JTabbedPane tabs;
+
+ private JTable repositoriesTable;
+
+ private RepositoriesModel repositoriesModel;
+
+ private JList usersList;
+
+ private JPanel usersPanel;
+
+ private JButton createRepository;
+
+ private JButton delRepository;
+
+ private NameRenderer nameRenderer;
+
+ private TypeRenderer typeRenderer;
+
+ private DefaultTableCellRenderer ownerRenderer;
+
+ private DefaultTableCellRenderer sizeRenderer;
+
+ private TableRowSorter<RepositoriesModel> defaultSorter;
+
+ public GitblitPanel(GitblitRegistration reg) {
+ this(reg.url, reg.account, reg.password);
+ }
+
+ public GitblitPanel(String url, String account, char[] password) {
+ this.url = url;
+ this.account = account;
+ this.password = password;
+
+ final JButton browseRepository = new JButton("Browse");
+ browseRepository.setEnabled(false);
+ browseRepository.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ RepositoryModel model = getSelectedRepositories().get(0);
+ String u = MessageFormat.format("{0}/summary/{1}", GitblitPanel.this.url,
+ StringUtils.encodeURL(model.name));
+ try {
+ Desktop.getDesktop().browse(new URI(u));
+ } catch (Exception x) {
+ x.printStackTrace();
+ }
+ }
+ });
+
+ createRepository = new JButton("Create");
+ createRepository.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ System.out.println("TODO Create Repository");
+ }
+ });
+
+ final JButton editRepository = new JButton("Edit");
+ editRepository.setEnabled(false);
+ editRepository.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ for (RepositoryModel model : getSelectedRepositories()) {
+ System.out.println("TODO Edit " + model);
+ }
+ }
+ });
+
+ delRepository = new JButton("Delete");
+ delRepository.setEnabled(false);
+ delRepository.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ for (RepositoryModel model : getSelectedRepositories()) {
+ System.out.println("TODO Delete " + model);
+ }
+ }
+ });
+
+ final JButton cloneRepository = new JButton("Clone");
+ cloneRepository.setEnabled(false);
+ cloneRepository.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ for (RepositoryModel model : getSelectedRepositories()) {
+ System.out.println("TODO Clone " + model);
+ }
+ }
+ });
+
+ nameRenderer = new NameRenderer(Color.gray, new Color(0x00, 0x69, 0xD6));
+ typeRenderer = new TypeRenderer();
+
+ sizeRenderer = new DefaultTableCellRenderer();
+ sizeRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
+ sizeRenderer.setForeground(new Color(0, 0x80, 0));
+
+ ownerRenderer = new DefaultTableCellRenderer();
+ ownerRenderer.setForeground(Color.gray);
+ ownerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
+
+ repositoriesModel = new RepositoriesModel();
+ defaultSorter = new TableRowSorter<RepositoriesModel>(repositoriesModel);
+ repositoriesTable = new JTable(repositoriesModel);
+ repositoriesTable.setRowSorter(defaultSorter);
+ repositoriesTable.getRowSorter().toggleSortOrder(RepositoriesModel.Columns.Name.ordinal());
+
+ repositoriesTable.setCellSelectionEnabled(false);
+ repositoriesTable.setRowSelectionAllowed(true);
+ repositoriesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);
+ repositoriesTable.getTableHeader().setReorderingAllowed(false);
+ repositoriesTable.setGridColor(new Color(0xd9d9d9));
+ repositoriesTable.setBackground(Color.white);
+ repositoriesTable.setDefaultRenderer(Date.class,
+ new DateCellRenderer(null, Color.orange.darker()));
+ setRenderer(RepositoriesModel.Columns.Name, nameRenderer);
+ setRenderer(RepositoriesModel.Columns.Type, typeRenderer);
+ setRenderer(RepositoriesModel.Columns.Owner, ownerRenderer);
+ setRenderer(RepositoriesModel.Columns.Size, sizeRenderer);
+
+ repositoriesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ boolean singleSelection = repositoriesTable.getSelectedRowCount() == 1;
+ boolean selected = repositoriesTable.getSelectedRow() > -1;
+ browseRepository.setEnabled(singleSelection);
+ delRepository.setEnabled(selected);
+ cloneRepository.setEnabled(selected);
+ if (selected) {
+ int viewRow = repositoriesTable.getSelectedRow();
+ int modelRow = repositoriesTable.convertRowIndexToModel(viewRow);
+ RepositoryModel model = ((RepositoriesModel) repositoriesTable.getModel()).list
+ .get(modelRow);
+ editRepository.setEnabled(singleSelection
+ && (isAdmin || model.owner.equalsIgnoreCase(GitblitPanel.this.account)));
+ } else {
+ editRepository.setEnabled(false);
+ }
+ }
+ });
+
+ final JTextField repositoryFilter = new JTextField();
+ repositoryFilter.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ filterRepositories(repositoryFilter.getText());
+ }
+ });
+
+ JPanel filterPanel = new JPanel(new BorderLayout(margin, margin));
+ filterPanel.add(new JLabel("Filter"), BorderLayout.WEST);
+ filterPanel.add(repositoryFilter, BorderLayout.CENTER);
+
+ JPanel tablePanel = new JPanel(new BorderLayout(margin, margin));
+ tablePanel.add(filterPanel, BorderLayout.NORTH);
+ tablePanel.add(new JScrollPane(repositoriesTable), BorderLayout.CENTER);
+
+ JPanel repositoryControls = new JPanel();
+ repositoryControls.add(browseRepository);
+ repositoryControls.add(cloneRepository);
+ repositoryControls.add(createRepository);
+ repositoryControls.add(editRepository);
+ repositoryControls.add(delRepository);
+
+ JPanel repositoriesPanel = new JPanel(new BorderLayout(margin, margin));
+ repositoriesPanel.add(newHeaderLabel("Repositories"), BorderLayout.NORTH);
+ repositoriesPanel.add(tablePanel, BorderLayout.CENTER);
+ repositoriesPanel.add(repositoryControls, BorderLayout.SOUTH);
+
+ JButton createUser = new JButton("Create");
+ createUser.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ System.out.println("TODO Create User");
+ }
+ });
+
+ final JButton editUser = new JButton("Edit");
+ editUser.setEnabled(false);
+ editUser.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ for (UserModel user : getSelectedUsers()) {
+ System.out.println("TODO Edit " + user);
+ }
+ }
+ });
+
+ final JButton delUser = new JButton("Delete");
+ delUser.setEnabled(false);
+ delUser.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ for (UserModel user : getSelectedUsers()) {
+ System.out.println("TODO Delete " + user);
+ }
+ }
+ });
+
+ usersList = new JList();
+ usersList.addListSelectionListener(new ListSelectionListener() {
+
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ boolean selected = usersList.getSelectedIndex() > -1;
+ boolean singleSelection = usersList.getSelectedIndices().length == 1;
+ editUser.setEnabled(singleSelection && selected);
+ delUser.setEnabled(selected);
+ }
+ });
+
+ JPanel userControls = new JPanel();
+ userControls.add(createUser);
+ userControls.add(editUser);
+ userControls.add(delUser);
+
+ usersPanel = new JPanel(new BorderLayout(margin, margin));
+ usersPanel.add(newHeaderLabel("Users"), BorderLayout.NORTH);
+ usersPanel.add(new JScrollPane(usersList), BorderLayout.CENTER);
+ usersPanel.add(userControls, BorderLayout.SOUTH);
+
+ /*
+ * Assemble the main panel
+ */
+ JPanel mainPanel = new JPanel(new BorderLayout(margin, margin));
+ mainPanel.add(repositoriesPanel, BorderLayout.CENTER);
+ mainPanel.add(usersPanel, BorderLayout.EAST);
+
+ tabs = new JTabbedPane(JTabbedPane.BOTTOM);
+ tabs.addTab("Main", mainPanel);
+ tabs.addTab("Federation", new JPanel());
+
+ setLayout(new BorderLayout());
+ add(tabs, BorderLayout.CENTER);
+ }
+
+ private JLabel newHeaderLabel(String text) {
+ JLabel label = new JLabel(text);
+ label.setOpaque(true);
+ label.setForeground(Color.white);
+ label.setBackground(Color.gray);
+ label.setFont(label.getFont().deriveFont(14f));
+ return label;
+ }
+
+ public void login() throws IOException {
+ refreshRepositoriesTable();
+
+ try {
+ refreshUsersTable();
+ isAdmin = true;
+ refreshFederationPanel();
+ } catch (ForbiddenException e) {
+ // user does not have administrator privileges
+ // hide admin repository buttons
+ createRepository.setVisible(false);
+ delRepository.setVisible(false);
+
+ // hide users panel
+ usersPanel.setVisible(false);
+
+ // remove federation tab
+ tabs.removeTabAt(1);
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+ }
+ }
+
+ private void refreshRepositoriesTable() throws IOException {
+ Map<String, RepositoryModel> repositories = RpcUtils
+ .getRepositories(url, account, password);
+ repositoriesModel.list.clear();
+ repositoriesModel.list.addAll(repositories.values());
+ repositoriesModel.fireTableDataChanged();
+ packColumns(repositoriesTable, 2);
+ }
+
+ private void setRenderer(RepositoriesModel.Columns col, TableCellRenderer renderer) {
+ String name = repositoriesTable.getColumnName(col.ordinal());
+ repositoriesTable.getColumn(name).setCellRenderer(renderer);
+ }
+
+ private void refreshUsersTable() throws IOException {
+ List<UserModel> users = RpcUtils.getUsers(url, account, password);
+ usersList.setListData(users.toArray());
+ }
+
+ private void refreshFederationPanel() throws IOException {
+ List<FederationModel> registrations = RpcUtils.getFederationRegistrations(url, account,
+ password);
+ }
+
+ private void filterRepositories(final String fragment) {
+ if (StringUtils.isEmpty(fragment)) {
+ repositoriesTable.setRowSorter(defaultSorter);
+ return;
+ }
+ RowFilter<RepositoriesModel, Object> containsFilter = new RowFilter<RepositoriesModel, Object>() {
+ public boolean include(Entry<? extends RepositoriesModel, ? extends Object> entry) {
+ for (int i = entry.getValueCount() - 1; i >= 0; i--) {
+ if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ RepositoriesModel model = (RepositoriesModel) repositoriesTable.getModel();
+ TableRowSorter<RepositoriesModel> sorter = new TableRowSorter<RepositoriesModel>(model);
+ sorter.setRowFilter(containsFilter);
+ repositoriesTable.setRowSorter(sorter);
+ }
+
+ private List<RepositoryModel> getSelectedRepositories() {
+ List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();
+ for (int viewRow : repositoriesTable.getSelectedRows()) {
+ int modelRow = repositoriesTable.convertRowIndexToModel(viewRow);
+ RepositoryModel model = ((RepositoriesModel) repositoriesTable.getModel()).list
+ .get(modelRow);
+ repositories.add(model);
+ }
+ return repositories;
+ }
+
+ private List<UserModel> getSelectedUsers() {
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (int viewRow : usersList.getSelectedIndices()) {
+ UserModel model = (UserModel) usersList.getModel().getElementAt(viewRow);
+ users.add(model);
+ }
+ return users;
+ }
+
+ private void packColumns(JTable table, int margin) {
+ for (int c = 0; c < table.getColumnCount(); c++) {
+ packColumn(table, c, 4);
+ }
+ }
+
+ // Sets the preferred width of the visible column specified by vColIndex.
+ // The column will be just wide enough to show the column head and the
+ // widest cell in the column. margin pixels are added to the left and right
+ // (resulting in an additional width of 2*margin pixels).
+ private void packColumn(JTable table, int vColIndex, int margin) {
+ DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel();
+ TableColumn col = colModel.getColumn(vColIndex);
+ int width = 0;
+
+ // Get width of column header
+ TableCellRenderer renderer = col.getHeaderRenderer();
+ if (renderer == null) {
+ renderer = table.getTableHeader().getDefaultRenderer();
+ }
+ Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false,
+ false, 0, 0);
+ width = comp.getPreferredSize().width;
+
+ // Get maximum width of column data
+ for (int r = 0; r < table.getRowCount(); r++) {
+ renderer = table.getCellRenderer(r, vColIndex);
+ comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex),
+ false, false, r, vColIndex);
+ width = Math.max(width, comp.getPreferredSize().width);
+ }
+
+ // Add margin
+ width += 2 * margin;
+
+ // Set the width
+ col.setPreferredWidth(width);
+ }
+
+ @Override
+ public Insets getInsets() {
+ return insets;
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ if (isAdmin) {
+ return new Dimension(950, 550);
+ }
+ return new Dimension(775, 450);
+ }
+
+ @Override
+ public void closeTab(Component c) {
+ }
+}
diff --git a/src/com/gitblit/client/GitblitRegistration.java b/src/com/gitblit/client/GitblitRegistration.java new file mode 100644 index 00000000..482bf8f2 --- /dev/null +++ b/src/com/gitblit/client/GitblitRegistration.java @@ -0,0 +1,47 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.io.Serializable;
+
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Simple class to encapsulate a Gitblit server registration.
+ *
+ * @author James Moger
+ *
+ */
+public class GitblitRegistration implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ String name;
+ String url;
+ String account;
+ char[] password;
+
+ public GitblitRegistration(String name, String url, String account, char[] password) {
+ this.url = url;
+ this.account = account;
+ this.password = password;
+ if (StringUtils.isEmpty(name)) {
+ this.name = url.substring(url.indexOf("//") + 2);
+ } else {
+ this.name = name;
+ }
+ }
+}
diff --git a/src/com/gitblit/client/NameRenderer.java b/src/com/gitblit/client/NameRenderer.java new file mode 100644 index 00000000..41393fb5 --- /dev/null +++ b/src/com/gitblit/client/NameRenderer.java @@ -0,0 +1,65 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.awt.Color;
+import java.awt.Component;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableCellRenderer;
+
+/**
+ * Repository name cell renderer. This renderer shows the group name in a gray
+ * color and accentuates the repository name in a cornflower blue color.
+ *
+ * @author James Moger
+ *
+ */
+public class NameRenderer extends DefaultTableCellRenderer {
+
+ private static final long serialVersionUID = 1L;
+
+ final String groupSpan;
+
+ public NameRenderer(Color group, Color repo) {
+ groupSpan = "<span style='color:" + getHexColor(group) + "'>";
+ setForeground(repo);
+ }
+
+ String getHexColor(Color c) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Integer.toHexString((c.getRGB() & 0x00FFFFFF)));
+ while (sb.length() < 6)
+ sb.insert(0, '0');
+ sb.insert(0, '#');
+ return sb.toString();
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
+ boolean hasFocus, int row, int column) {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ String name = value.toString();
+ int lastSlash = name.lastIndexOf('/');
+ if (!isSelected && lastSlash > -1) {
+ String group = name.substring(0, lastSlash + 1);
+ String repo = name.substring(lastSlash + 1);
+ setText("<html><body>" + groupSpan + group + "</span>" + repo);
+ } else {
+ this.setText(name);
+ }
+ return this;
+ }
+}
\ No newline at end of file diff --git a/src/com/gitblit/client/RepositoriesModel.java b/src/com/gitblit/client/RepositoriesModel.java new file mode 100644 index 00000000..d8e448fc --- /dev/null +++ b/src/com/gitblit/client/RepositoriesModel.java @@ -0,0 +1,112 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import com.gitblit.models.RepositoryModel;
+
+/**
+ * Table model of a list of repositories.
+ *
+ * @author James Moger
+ *
+ */
+public class RepositoriesModel extends AbstractTableModel {
+
+ private static final long serialVersionUID = 1L;
+
+ List<RepositoryModel> list;
+
+ enum Columns {
+ Name, Description, Owner, Type, Last_Change, Size;
+
+ @Override
+ public String toString() {
+ return name().replace('_', ' ');
+ }
+ }
+
+ public RepositoriesModel() {
+ this(new ArrayList<RepositoryModel>());
+ }
+
+ public RepositoriesModel(List<RepositoryModel> repositories) {
+ this.list = repositories;
+ Collections.sort(this.list);
+ }
+
+ @Override
+ public int getRowCount() {
+ return list.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return Columns.values().length;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ Columns col = Columns.values()[column];
+ return col.toString();
+ }
+
+ /**
+ * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
+ *
+ * @param columnIndex
+ * the column being queried
+ * @return the Object.class
+ */
+ public Class<?> getColumnClass(int columnIndex) {
+ Columns col = Columns.values()[columnIndex];
+ switch (col) {
+ case Name:
+ case Type:
+ return RepositoryModel.class;
+ case Last_Change:
+ return Date.class;
+ }
+ return String.class;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ RepositoryModel model = list.get(rowIndex);
+ Columns col = Columns.values()[columnIndex];
+ switch (col) {
+ case Name:
+ return model;
+ case Description:
+ return model.description;
+ case Owner:
+ return model.owner;
+ case Type:
+ return model;
+ case Last_Change:
+ return model.lastChange;
+ case Size:
+ return model.size;
+ }
+ return null;
+ }
+}
diff --git a/src/com/gitblit/client/TypeRenderer.java b/src/com/gitblit/client/TypeRenderer.java new file mode 100644 index 00000000..8f92dcf4 --- /dev/null +++ b/src/com/gitblit/client/TypeRenderer.java @@ -0,0 +1,119 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.client;
+
+import java.awt.Component;
+import java.awt.GridLayout;
+import java.io.Serializable;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.table.TableCellRenderer;
+
+import com.gitblit.models.RepositoryModel;
+
+/**
+ * Renders the type indicators (tickets, frozen, access restriction, etc) in a
+ * single cell.
+ *
+ * @author James Moger
+ *
+ */
+public class TypeRenderer extends JPanel implements TableCellRenderer, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final ImageIcon blankIcon;
+
+ private final ImageIcon pushIcon;
+
+ private final ImageIcon pullIcon;
+
+ private final ImageIcon viewIcon;
+
+ private final ImageIcon tixIcon;
+
+ private final ImageIcon doxIcon;
+
+ private final ImageIcon frozenIcon;
+
+ private final ImageIcon federatedIcon;
+
+ public TypeRenderer() {
+ super(new GridLayout(1, 0, 1, 0));
+ blankIcon = new ImageIcon(getClass().getResource("/blank.png"));
+ pushIcon = new ImageIcon(getClass().getResource("/lock_go_16x16.png"));
+ pullIcon = new ImageIcon(getClass().getResource("/lock_pull_16x16.png"));
+ viewIcon = new ImageIcon(getClass().getResource("/shield_16x16.png"));
+ tixIcon = new ImageIcon(getClass().getResource("/bug_16x16.png"));
+ doxIcon = new ImageIcon(getClass().getResource("/book_16x16.png"));
+ frozenIcon = new ImageIcon(getClass().getResource("/cold_16x16.png"));
+ federatedIcon = new ImageIcon(getClass().getResource("/federated_16x16.png"));
+ }
+
+ @Override
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
+ boolean hasFocus, int row, int column) {
+ if (isSelected)
+ setBackground(table.getSelectionBackground());
+ else
+ setBackground(table.getBackground());
+ removeAll();
+ if (value instanceof RepositoryModel) {
+ RepositoryModel model = (RepositoryModel) value;
+ if (model.useTickets) {
+ add(new JLabel(tixIcon));
+ } else {
+ add(new JLabel(blankIcon));
+ }
+ if (model.useDocs) {
+ add(new JLabel(doxIcon));
+ } else {
+ add(new JLabel(blankIcon));
+ }
+ if (model.isFrozen) {
+ add(new JLabel(frozenIcon));
+ } else {
+ add(new JLabel(blankIcon));
+ }
+ if (model.isFederated) {
+ add(new JLabel(federatedIcon));
+ } else {
+ add(new JLabel(blankIcon));
+ }
+
+ switch (model.accessRestriction) {
+ case NONE:
+ add(new JLabel(blankIcon));
+ break;
+ case PUSH:
+ add(new JLabel(pushIcon));
+ break;
+ case CLONE:
+ add(new JLabel(pullIcon));
+ break;
+ case VIEW:
+ add(new JLabel(viewIcon));
+ break;
+ default:
+ add(new JLabel(blankIcon));
+ }
+ }
+ return this;
+ }
+}
\ No newline at end of file diff --git a/src/com/gitblit/client/splash.png b/src/com/gitblit/client/splash.png Binary files differnew file mode 100644 index 00000000..d63932fb --- /dev/null +++ b/src/com/gitblit/client/splash.png diff --git a/src/com/gitblit/models/FederationSet.java b/src/com/gitblit/models/FederationSet.java new file mode 100644 index 00000000..357689c9 --- /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 fde95575..324aa67e 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,37 +49,57 @@ import com.google.gson.reflect.TypeToken; */
public class FederationUtils {
- public static final String CHARSET;
-
- public 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 REPOSITORIES_TYPE = new TypeToken<Map<String, RepositoryModel>>() {
}.getType();
- public static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
+ private static final Type SETTINGS_TYPE = new TypeToken<Map<String, String>>() {
}.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 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);
}
- SSL_CONTEXT = context;
- HOSTNAME_VERIFIER = new DummyHostnameVerifier();
- CHARSET = "UTF-8";
+ 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)));
}
/**
@@ -195,10 +198,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 +215,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 +251,9 @@ public class FederationUtils { */
public static Map<String, RepositoryModel> 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<String, RepositoryModel> models = readGson(url, REPOSITORIES_TYPE);
+ Map<String, RepositoryModel> models = JsonUtils.retrieveJson(url, REPOSITORIES_TYPE);
if (checkExclusions) {
Map<String, RepositoryModel> includedModels = new HashMap<String, RepositoryModel>();
for (Map.Entry<String, RepositoryModel> entry : models.entrySet()) {
@@ -273,11 +273,11 @@ public class FederationUtils { * @return a collection of UserModel objects
* @throws Exception
*/
- public static Collection<UserModel> getUsers(FederationModel registration) throws Exception {
- String url = FederationServlet.asFederationLink(registration.url, registration.token,
- FederationRequest.PULL_USERS);
- Collection<UserModel> models = readGson(url, USERS_TYPE);
- return models;
+ 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);
+ List<UserModel> list = new ArrayList<UserModel>(models);
+ return list;
}
/**
@@ -289,9 +289,8 @@ public class FederationUtils { * @throws Exception
*/
public static Map<String, String> getSettings(FederationModel registration) throws Exception {
- String url = FederationServlet.asFederationLink(registration.url, registration.token,
- FederationRequest.PULL_SETTINGS);
- Map<String, String> settings = readGson(url, SETTINGS_TYPE);
+ String url = asLink(registration.url, registration.token, FederationRequest.PULL_SETTINGS);
+ Map<String, String> settings = JsonUtils.retrieveJson(url, SETTINGS_TYPE);
return settings;
}
@@ -309,122 +308,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> 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..3834c8ed --- /dev/null +++ b/src/com/gitblit/utils/JsonUtils.java @@ -0,0 +1,344 @@ +/*
+ * 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.IOException;
+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.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+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 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;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Utility methods for json calls to a Gitblit server.
+ *
+ * @author James Moger
+ *
+ */
+public class JsonUtils {
+
+ public static final String CHARSET;
+
+ public static final Type REPOSITORIES_TYPE = new TypeToken<Map<String, RepositoryModel>>() {
+ }.getType();
+
+ public static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
+ }.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) {
+ 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> X fromJsonString(String json, Class<X> clazz) {
+ 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> X fromJsonString(String json, Type type) {
+ return gson().fromJson(json, type);
+ }
+
+ /**
+ * Reads a gson object from the specified url.
+ *
+ * @param url
+ * @param type
+ * @return the deserialized object
+ * @throws {@link IOException}
+ */
+ 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;
+ }
+ return gson().fromJson(json, type);
+ }
+
+ /**
+ * Retrieves a JSON message.
+ *
+ * @param url
+ * @return the JSON message as a string
+ * @throws {@link IOException}
+ */
+ 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;
+ }
+ }
+
+ /**
+ * 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 {@link IOException}
+ */
+ 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;
+ }
+ }
+
+ 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()));
+ }
+ }
+
+ // build custom gson instance with GMT date serializer/deserializer
+ // http://code.google.com/p/google-gson/issues/detail?id=281
+ private static Gson gson() {
+ GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapter(Date.class, new GmtDateTypeAdapter());
+ builder.setPrettyPrinting();
+ return builder.create();
+ }
+
+ private static class GmtDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
+ private final DateFormat dateFormat;
+
+ private GmtDateTypeAdapter() {
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ @Override
+ public synchronized JsonElement serialize(Date date, Type type,
+ JsonSerializationContext jsonSerializationContext) {
+ synchronized (dateFormat) {
+ String dateFormatAsString = dateFormat.format(date);
+ return new JsonPrimitive(dateFormatAsString);
+ }
+ }
+
+ @Override
+ public synchronized Date deserialize(JsonElement jsonElement, Type type,
+ JsonDeserializationContext jsonDeserializationContext) {
+ try {
+ synchronized (dateFormat) {
+ return dateFormat.parse(jsonElement.getAsString());
+ }
+ } catch (ParseException e) {
+ throw new JsonSyntaxException(jsonElement.getAsString(), e);
+ }
+ }
+ }
+
+ /**
+ * 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..715ecb57 --- /dev/null +++ b/src/com/gitblit/utils/RpcUtils.java @@ -0,0 +1,354 @@ +/*
+ * 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.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;
+
+/**
+ * Utility methods for rpc calls.
+ *
+ * @author James Moger
+ *
+ */
+public class RpcUtils {
+
+ public static final Type NAMES_TYPE = new TypeToken<Collection<String>>() {
+ }.getType();
+
+ 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();
+
+ /**
+ *
+ * @param remoteURL
+ * the url of the remote gitblit instance
+ * @param req
+ * the rpc request type
+ * @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()
+ + (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 IOException
+ */
+ 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,
+ account, password);
+ return models;
+ }
+
+ /**
+ * 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 IOException
+ */
+ 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, 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/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<String[]> data = new ArrayList<String[]>();
@@ -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<Void>("send",
diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/com/gitblit/wicket/panels/RepositoriesPanel.java index e5149761..d18d5bb3 100644 --- a/src/com/gitblit/wicket/panels/RepositoriesPanel.java +++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -47,7 +47,6 @@ import com.gitblit.Keys; import com.gitblit.SyndicationServlet;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
-import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.gitblit.wicket.GitBlitWebSession;
@@ -71,12 +70,6 @@ public class RepositoriesPanel extends BasePanel { if (models == null) {
linksActive = true;
models = GitBlit.self().getRepositoryModels(user);
- final ByteFormat byteFormat = new ByteFormat();
- if (showSize) {
- for (RepositoryModel model : models) {
- model.size = byteFormat.format(GitBlit.self().calculateSize(model));
- }
- }
} else {
// disable links if the repositories are already provided
// the repositories are most likely from a proposal
@@ -173,11 +166,7 @@ public class RepositoriesPanel extends BasePanel { if (entry.hasCommits) {
// Existing repository
- if (showSize) {
- row.add(new Label("repositorySize", entry.size));
- } else {
- row.add(new Label("repositorySize").setVisible(false));
- }
+ row.add(new Label("repositorySize", entry.size).setVisible(showSize));
} else {
// New repository
row.add(new Label("repositorySize", "<span class='empty'>(empty)</span>")
|