From 892570d96cdfaf4779c1e92c89d76dabf78be361 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 1 Jul 2011 17:47:28 -0400 Subject: [PATCH] Documentation. Adding JavaDoc comments. Adjustments to method names. --- src/com/gitblit/AccessRestrictionFilter.java | 56 ++- src/com/gitblit/Constants.java | 6 + src/com/gitblit/DownloadZipServlet.java | 31 +- src/com/gitblit/FileSettings.java | 22 +- src/com/gitblit/FileUserService.java | 123 ++++++- src/com/gitblit/GitBlit.java | 340 ++++++++++++++++-- src/com/gitblit/GitBlitException.java | 6 + src/com/gitblit/GitBlitServer.java | 85 +++-- src/com/gitblit/GitFilter.java | 45 ++- src/com/gitblit/GitServlet.java | 28 ++ src/com/gitblit/IStoredSettings.java | 60 ++++ src/com/gitblit/IUserService.java | 112 +++++- src/com/gitblit/Launcher.java | 12 +- src/com/gitblit/MakeCertificate.java | 6 + src/com/gitblit/ServletRequestWrapper.java | 16 + src/com/gitblit/SyndicationFilter.java | 41 ++- src/com/gitblit/SyndicationServlet.java | 35 ++ src/com/gitblit/WebXmlSettings.java | 10 +- src/com/gitblit/build/Build.java | 16 + src/com/gitblit/build/BuildSite.java | 48 ++- src/com/gitblit/build/BuildThumbnails.java | 37 +- src/com/gitblit/build/BuildWebXml.java | 11 +- src/com/gitblit/utils/FileUtils.java | 59 +++ .../wicket/pages/EditRepositoryPage.java | 2 +- .../gitblit/wicket/pages/EditUserPage.java | 2 +- tests/com/gitblit/tests/GitBlitSuite.java | 6 +- 26 files changed, 1077 insertions(+), 138 deletions(-) create mode 100644 src/com/gitblit/utils/FileUtils.java diff --git a/src/com/gitblit/AccessRestrictionFilter.java b/src/com/gitblit/AccessRestrictionFilter.java index e3dcc62e..74571789 100644 --- a/src/com/gitblit/AccessRestrictionFilter.java +++ b/src/com/gitblit/AccessRestrictionFilter.java @@ -42,8 +42,18 @@ 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 filter extracts the name of the repository from the url and determines if + * the requested action for the repository 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 abstract class AccessRestrictionFilter implements Filter { @@ -59,15 +69,47 @@ public abstract class AccessRestrictionFilter implements Filter { logger = LoggerFactory.getLogger(getClass()); } + /** + * Extract the repository name from the url. + * + * @param url + * @return repository name + */ protected abstract String extractRepositoryName(String url); - protected abstract String getUrlRequestType(String url); + /** + * Analyze the url and returns the action of the request. + * + * @param url + * @return action of the request + */ + protected abstract String getUrlRequestAction(String url); + /** + * Determine if the repository requires authentication. + * + * @param repository + * @return true if authentication required + */ protected abstract boolean requiresAuthentication(RepositoryModel repository); - protected abstract boolean canAccess(RepositoryModel repository, UserModel user, - String restrictedUrl); + /** + * Determine if the user can access the repository and perform the specified + * action. + * + * @param repository + * @param user + * @param action + * @return true if user may execute the action on the repository + */ + protected abstract boolean canAccess(RepositoryModel repository, UserModel user, String action); + /** + * 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 { @@ -98,7 +140,7 @@ public abstract class AccessRestrictionFilter implements Filter { // Determine if the request URL is restricted String fullSuffix = fullUrl.substring(repository.length()); - String urlRequestType = getUrlRequestType(fullSuffix); + String urlRequestType = getUrlRequestAction(fullSuffix); // Load the repository model RepositoryModel model = GitBlit.self().getRepositoryModel(repository); @@ -197,10 +239,16 @@ public abstract class AccessRestrictionFilter implements Filter { } } + /** + * @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() { } diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index 69f5786a..08ffb9ba 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -15,6 +15,12 @@ */ package com.gitblit; +/** + * Constant values used by Gitblit. + * + * @author James Moger + * + */ public class Constants { public static final String NAME = "Gitblit"; diff --git a/src/com/gitblit/DownloadZipServlet.java b/src/com/gitblit/DownloadZipServlet.java index d36b94d2..32369ae1 100644 --- a/src/com/gitblit/DownloadZipServlet.java +++ b/src/com/gitblit/DownloadZipServlet.java @@ -30,6 +30,19 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.StringUtils; +/** + * Streams out a zip file from the specified repository for any tree path at any + * revision. + * + * Unlike the GitServlet and the SyndicationServlet, this servlet is not + * protected by an AccessRestrictionFilter. It performs its own authorization + * check, but it does not perform any authentication. The assumption is that + * requests to this servlet are made via the web ui and not by direct url + * access. Unauthorized requests fail with a standard 403 (FORBIDDEN) code. + * + * @author James Moger + * + */ public class DownloadZipServlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -40,6 +53,15 @@ public class DownloadZipServlet extends HttpServlet { super(); } + /** + * Returns an url to this servlet for the specified parameters. + * + * @param baseURL + * @param repository + * @param objectId + * @param path + * @return an url + */ public static String asLink(String baseURL, String repository, String objectId, String path) { if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { baseURL = baseURL.substring(0, baseURL.length() - 1); @@ -49,6 +71,14 @@ public class DownloadZipServlet extends HttpServlet { + (objectId == null ? "" : ("&h=" + objectId)); } + /** + * Performs the authorization and zip streaming of the specified elements. + * + * @param request + * @param response + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { @@ -92,7 +122,6 @@ public class DownloadZipServlet extends HttpServlet { Date date = JGitUtils.getCommitDate(commit); String contentType = "application/octet-stream"; response.setContentType(contentType + "; charset=" + response.getCharacterEncoding()); - // response.setContentLength(attachment.getFileSize()); response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip" + "\""); response.setDateHeader("Last-Modified", date.getTime()); diff --git a/src/com/gitblit/FileSettings.java b/src/com/gitblit/FileSettings.java index 1e654222..56aac8b7 100644 --- a/src/com/gitblit/FileSettings.java +++ b/src/com/gitblit/FileSettings.java @@ -21,7 +21,10 @@ import java.io.FileNotFoundException; import java.util.Properties; /** - * Reads GitBlit settings file. + * Dynamically loads and reloads a properties file by keeping track of the last + * modification date. + * + * @author James Moger * */ public class FileSettings extends IStoredSettings { @@ -30,16 +33,20 @@ public class FileSettings extends IStoredSettings { private final Properties properties = new Properties(); - private volatile long lastread; + private volatile long lastModified; public FileSettings(String file) { super(FileSettings.class); this.propertiesFile = new File(file); } + /** + * Returns a properties object which contains the most recent contents of + * the properties file. + */ @Override protected synchronized Properties read() { - if (propertiesFile.exists() && (propertiesFile.lastModified() > lastread)) { + if (propertiesFile.exists() && (propertiesFile.lastModified() > lastModified)) { FileInputStream is = null; try { Properties props = new Properties(); @@ -49,7 +56,7 @@ public class FileSettings extends IStoredSettings { // load properties after we have successfully read file properties.clear(); properties.putAll(props); - lastread = propertiesFile.lastModified(); + lastModified = propertiesFile.lastModified(); } catch (FileNotFoundException f) { // IGNORE - won't happen because file.exists() check above } catch (Throwable t) { @@ -67,8 +74,11 @@ public class FileSettings extends IStoredSettings { return properties; } - protected long lastRead() { - return lastread; + /** + * @return the last modification date of the properties file + */ + protected long lastModified() { + return lastModified; } @Override diff --git a/src/com/gitblit/FileUserService.java b/src/com/gitblit/FileUserService.java index 01a50be1..12164ce6 100644 --- a/src/com/gitblit/FileUserService.java +++ b/src/com/gitblit/FileUserService.java @@ -33,6 +33,15 @@ import org.slf4j.LoggerFactory; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; +/** + * FileUserService is Gitblit's default user service implementation. + * + * Users and their repository memberships are stored in a simple properties file + * which is cached and dynamically reloaded when modified. + * + * @author James Moger + * + */ public class FileUserService extends FileSettings implements IUserService { private final Logger logger = LoggerFactory.getLogger(FileUserService.class); @@ -43,11 +52,22 @@ public class FileUserService extends FileSettings implements IUserService { super(realmFile.getAbsolutePath()); } + /** + * Does the user service support cookie authentication? + * + * @return true or false + */ @Override public boolean supportsCookies() { return true; } + /** + * Returns the cookie value for the specified user. + * + * @param model + * @return cookie value + */ @Override public char[] getCookie(UserModel model) { Properties allUsers = super.read(); @@ -58,6 +78,12 @@ public class FileUserService extends FileSettings implements IUserService { return cookie.toCharArray(); } + /** + * Authenticate a user based on their cookie. + * + * @param cookie + * @return a user object or null + */ @Override public UserModel authenticate(char[] cookie) { String hash = new String(cookie); @@ -73,6 +99,13 @@ public class FileUserService extends FileSettings implements IUserService { return model; } + /** + * Authenticate a user based on a username and password. + * + * @param username + * @param password + * @return a user object or null + */ @Override public UserModel authenticate(String username, char[] password) { Properties allUsers = read(); @@ -93,6 +126,12 @@ public class FileUserService extends FileSettings implements IUserService { return returnedUser; } + /** + * Retrieve the user object for the specified username. + * + * @param username + * @return a user object or null + */ @Override public UserModel getUserModel(String username) { Properties allUsers = read(); @@ -119,11 +158,27 @@ public class FileUserService extends FileSettings implements IUserService { return model; } + /** + * Updates/writes a complete user object. + * + * @param model + * @return true if update is successful + */ @Override public boolean updateUserModel(UserModel model) { return updateUserModel(model.username, model); } + /** + * Updates/writes and replaces a complete user object keyed by username. + * This method allows for renaming a user. + * + * @param username + * the old username + * @param model + * the user object to use for username + * @return true if update is successful + */ @Override public boolean updateUserModel(String username, UserModel model) { try { @@ -156,11 +211,23 @@ public class FileUserService extends FileSettings implements IUserService { return false; } + /** + * Deletes the user object from the user service. + * + * @param model + * @return true if successful + */ @Override public boolean deleteUserModel(UserModel model) { return deleteUser(model.username); } + /** + * Delete the user object with the specified username + * + * @param username + * @return true if successful + */ @Override public boolean deleteUser(String username) { try { @@ -175,6 +242,11 @@ public class FileUserService extends FileSettings implements IUserService { return false; } + /** + * Returns the list of all users available to the login service. + * + * @return list of all usernames + */ @Override public List getAllUsernames() { Properties allUsers = read(); @@ -182,8 +254,16 @@ public class FileUserService extends FileSettings implements IUserService { return list; } + /** + * Returns the list of all users who are allowed to bypass the access + * restriction placed on the specified repository. + * + * @param role + * the repository name + * @return list of all usernames that can bypass the access restriction + */ @Override - public List getUsernamesForRepository(String role) { + public List getUsernamesForRepositoryRole(String role) { List list = new ArrayList(); try { Properties allUsers = read(); @@ -205,8 +285,17 @@ public class FileUserService extends FileSettings implements IUserService { return list; } + /** + * Sets the list of all uses who are allowed to bypass the access + * restriction placed on the specified repository. + * + * @param role + * the repository name + * @param usernames + * @return true if successful + */ @Override - public boolean setUsernamesForRepository(String role, List usernames) { + public boolean setUsernamesForRepositoryRole(String role, List usernames) { try { Set specifiedUsers = new HashSet(usernames); Set needsAddRole = new HashSet(specifiedUsers); @@ -272,6 +361,13 @@ public class FileUserService extends FileSettings implements IUserService { return false; } + /** + * Renames a repository role. + * + * @param oldRole + * @param newRole + * @return true if successful + */ @Override public boolean renameRepositoryRole(String oldRole, String newRole) { try { @@ -327,6 +423,12 @@ public class FileUserService extends FileSettings implements IUserService { return false; } + /** + * Removes a repository role from all users. + * + * @param role + * @return true if successful + */ @Override public boolean deleteRepositoryRole(String role) { try { @@ -380,14 +482,22 @@ public class FileUserService extends FileSettings implements IUserService { return false; } + /** + * Writes the properties file. + * + * @param properties + * @throws IOException + */ private void write(Properties properties) throws IOException { - // Update realm file + // Write a temporary copy of the users file File realmFileCopy = new File(propertiesFile.getAbsolutePath() + ".tmp"); FileWriter writer = new FileWriter(realmFileCopy); properties .store(writer, "# Gitblit realm file format: username=password,\\#permission,repository1,repository2..."); writer.close(); + // If the write is successful, delete the current file and rename + // the temporary copy to the original filename. if (realmFileCopy.exists() && realmFileCopy.length() > 0) { if (propertiesFile.delete()) { if (!realmFileCopy.renameTo(propertiesFile)) { @@ -404,11 +514,14 @@ public class FileUserService extends FileSettings implements IUserService { } } + /** + * Reads the properties file and rebuilds the in-memory cookie lookup table. + */ @Override protected synchronized Properties read() { - long lastRead = lastRead(); + long lastRead = lastModified(); Properties allUsers = super.read(); - if (lastRead != lastRead()) { + if (lastRead != lastModified()) { // reload hash cache cookies.clear(); for (String username : allUsers.stringPropertyNames()) { diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 9b661171..467d6620 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -34,6 +34,8 @@ import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.transport.resolver.FileResolver; +import org.eclipse.jgit.transport.resolver.RepositoryResolver; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.eclipse.jgit.util.FileUtils; import org.slf4j.Logger; @@ -45,13 +47,29 @@ import com.gitblit.models.UserModel; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.StringUtils; +/** + * GitBlit is the servlet context listener singleton that acts as the core for + * the web ui and the servlets. This class is either directly instantiated by + * the GitBlitServer class (Gitblit GO) or is reflectively instantiated from the + * definition in the web.xml file (Gitblit WAR). + * + * This class is the central logic processor for Gitblit. All settings, user + * object, and repository object operations pass through this class. + * + * Repository Resolution. There are two pathways for finding repositories. One + * pathway, for web ui display and repository authentication & authorization, is + * within this class. The other pathway is through the standard GitServlet. + * + * @author James Moger + * + */ public class GitBlit implements ServletContextListener { private static GitBlit gitblit; private final Logger logger = LoggerFactory.getLogger(GitBlit.class); - private FileResolver repositoryResolver; + private RepositoryResolver repositoryResolver; private File repositoriesFolder; @@ -59,59 +77,136 @@ public class GitBlit implements ServletContextListener { private IUserService userService; - private IStoredSettings storedSettings; + private IStoredSettings settings; public GitBlit() { if (gitblit == null) { - // Singleton reference when running in standard servlet container + // set the static singleton reference gitblit = this; } } + /** + * Returns the Gitblit singleton. + * + * @return gitblit singleton + */ public static GitBlit self() { if (gitblit == null) { - gitblit = new GitBlit(); + new GitBlit(); } return gitblit; } + /** + * Returns the boolean value for the specified key. If the key does not + * exist or the value for the key can not be interpreted as a boolean, the + * defaultValue is returned. + * + * @see IStoredSettings.getBoolean(String, boolean) + * @param key + * @param defaultValue + * @return key value or defaultValue + */ public static boolean getBoolean(String key, boolean defaultValue) { - return self().storedSettings.getBoolean(key, defaultValue); - } - + return self().settings.getBoolean(key, defaultValue); + } + + /** + * Returns the integer value for the specified key. If the key does not + * exist or the value for the key can not be interpreted as an integer, the + * defaultValue is returned. + * + * @see IStoredSettings.getInteger(String key, int defaultValue) + * @param key + * @param defaultValue + * @return key value or defaultValue + */ public static int getInteger(String key, int defaultValue) { - return self().storedSettings.getInteger(key, defaultValue); - } - + return self().settings.getInteger(key, defaultValue); + } + + /** + * Returns the string value for the specified key. If the key does not exist + * or the value for the key can not be interpreted as a string, the + * defaultValue is returned. + * + * @see IStoredSettings.getString(String key, String defaultValue) + * @param key + * @param defaultValue + * @return key value or defaultValue + */ public static String getString(String key, String defaultValue) { - return self().storedSettings.getString(key, defaultValue); + return self().settings.getString(key, defaultValue); } + /** + * Returns a list of space-separated strings from the specified key. + * + * @see IStoredSettings.getStrings(String key) + * @param name + * @return list of strings + */ public static List getStrings(String key) { - return self().storedSettings.getStrings(key); + return self().settings.getStrings(key); } + /** + * Returns the list of keys whose name starts with the specified prefix. If + * the prefix is null or empty, all key names are returned. + * + * @see IStoredSettings.getAllKeys(String key) + * @param startingWith + * @return list of keys + */ + public static List getAllKeys(String startingWith) { - return self().storedSettings.getAllKeys(startingWith); + return self().settings.getAllKeys(startingWith); } + /** + * Is Gitblit running in debug mode? + * + * @return true if Gitblit is running in debug mode + */ public static boolean isDebugMode() { - return self().storedSettings.getBoolean(Keys.web.debugMode, false); + return self().settings.getBoolean(Keys.web.debugMode, false); } + /** + * Returns the list of non-Gitblit clone urls. This allows Gitblit to + * advertise alternative urls for Git client repository access. + * + * @param repositoryName + * @return list of non-gitblit clone urls + */ public List getOtherCloneUrls(String repositoryName) { List cloneUrls = new ArrayList(); - for (String url : storedSettings.getStrings(Keys.web.otherUrls)) { + for (String url : settings.getStrings(Keys.web.otherUrls)) { cloneUrls.add(MessageFormat.format(url, repositoryName)); } return cloneUrls; } + /** + * Set the user service. The user service authenticates all users and is + * responsible for managing user permissions. + * + * @param userService + */ public void setUserService(IUserService userService) { logger.info("Setting up user service " + userService.toString()); this.userService = userService; } + /** + * Authenticate a user based on a username and password. + * + * @see IUserService.authenticate(String, char[]) + * @param username + * @param password + * @return a user object or null + */ public UserModel authenticate(String username, char[] password) { if (userService == null) { return null; @@ -119,6 +214,12 @@ public class GitBlit implements ServletContextListener { return userService.authenticate(username, password); } + /** + * Authenticate a user based on their cookie. + * + * @param cookies + * @return a user object or null + */ public UserModel authenticate(Cookie[] cookies) { if (userService == null) { return null; @@ -136,6 +237,12 @@ public class GitBlit implements ServletContextListener { return null; } + /** + * Sets a cookie for the specified user. + * + * @param response + * @param user + */ public void setCookie(WebResponse response, UserModel user) { if (userService == null) { return; @@ -156,41 +263,100 @@ public class GitBlit implements ServletContextListener { } } + /** + * Returns the list of all users available to the login service. + * + * @see IUserService.getAllUsernames() + * @return list of all usernames + */ public List getAllUsernames() { List names = new ArrayList(userService.getAllUsernames()); Collections.sort(names); return names; } + /** + * Delete the user object with the specified username + * + * @see IUserService.deleteUser(String) + * @param username + * @return true if successful + */ public boolean deleteUser(String username) { return userService.deleteUser(username); } + /** + * Retrieve the user object for the specified username. + * + * @see IUserService.getUserModel(String) + * @param username + * @return a user object or null + */ public UserModel getUserModel(String username) { UserModel user = userService.getUserModel(username); return user; } + /** + * Returns the list of all users who are allowed to bypass the access + * restriction placed on the specified repository. + * + * @see IUserService.getUsernamesForRepositoryRole(String) + * @param repository + * @return list of all usernames that can bypass the access restriction + */ public List getRepositoryUsers(RepositoryModel repository) { - return userService.getUsernamesForRepository(repository.name); - } - + return userService.getUsernamesForRepositoryRole(repository.name); + } + + /** + * Sets the list of all uses who are allowed to bypass the access + * restriction placed on the specified repository. + * + * @see IUserService.setUsernamesForRepositoryRole(String, List) + * @param repository + * @param usernames + * @return true if successful + */ public boolean setRepositoryUsers(RepositoryModel repository, List repositoryUsers) { - return userService.setUsernamesForRepository(repository.name, repositoryUsers); - } - - public void editUserModel(String username, UserModel user, boolean isCreate) + return userService.setUsernamesForRepositoryRole(repository.name, repositoryUsers); + } + + /** + * Adds/updates a complete user object keyed by username. This method allows + * for renaming a user. + * + * @see IUserService.updateUserModel(String, UserModel) + * @param username + * @param user + * @param isCreate + * @throws GitBlitException + */ + public void updateUserModel(String username, UserModel user, boolean isCreate) throws GitBlitException { if (!userService.updateUserModel(username, user)) { throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!"); } } + /** + * Returns the list of all repositories available to Gitblit. This method + * does not consider user access permissions. + * + * @return list of all repositories + */ public List getRepositoryList() { return JGitUtils.getRepositoryList(repositoriesFolder, exportAll, - storedSettings.getBoolean(Keys.git.searchRepositoriesSubfolders, true)); + settings.getBoolean(Keys.git.searchRepositoriesSubfolders, true)); } + /** + * Returns the JGit repository for the specified name. + * + * @param repositoryName + * @return repository or null + */ public Repository getRepository(String repositoryName) { Repository r = null; try { @@ -199,13 +365,24 @@ public class GitBlit implements ServletContextListener { r = null; logger.error("GitBlit.getRepository(String) failed to find " + new File(repositoriesFolder, repositoryName).getAbsolutePath()); + } catch (ServiceNotAuthorizedException e) { + r = null; + logger.error("GitBlit.getRepository(String) failed to find " + + new File(repositoriesFolder, repositoryName).getAbsolutePath(), e); } catch (ServiceNotEnabledException e) { r = null; - e.printStackTrace(); + logger.error("GitBlit.getRepository(String) failed to find " + + new File(repositoriesFolder, repositoryName).getAbsolutePath(), e); } return r; } + /** + * Returns the list of repository models that are accessible to the user. + * + * @param user + * @return list of repository models accessible to user + */ public List getRepositoryModels(UserModel user) { List list = getRepositoryList(); List repositories = new ArrayList(); @@ -218,6 +395,14 @@ public class GitBlit implements ServletContextListener { return repositories; } + /** + * Returns a repository model if the repository exists and the user may + * access the repository. + * + * @param user + * @param repositoryName + * @return repository model or null + */ public RepositoryModel getRepositoryModel(UserModel user, String repositoryName) { RepositoryModel model = getRepositoryModel(repositoryName); if (model == null) { @@ -233,6 +418,13 @@ public class GitBlit implements ServletContextListener { } } + /** + * Returns the repository model for the specified repository. This method + * does not consider user access permissions. + * + * @param repositoryName + * @return repository model or null + */ public RepositoryModel getRepositoryModel(String repositoryName) { Repository r = getRepository(repositoryName); if (r == null) { @@ -258,6 +450,15 @@ public class GitBlit implements ServletContextListener { return model; } + /** + * Returns the gitblit string vlaue for the specified key. If key is not + * set, returns defaultValue. + * + * @param config + * @param field + * @param defaultValue + * @return field value or defaultValue + */ private String getConfig(StoredConfig config, String field, String defaultValue) { String value = config.getString("gitblit", null, field); if (StringUtils.isEmpty(value)) { @@ -266,11 +467,34 @@ public class GitBlit implements ServletContextListener { return value; } + /** + * Returns the gitblit boolean vlaue for the specified key. If key is not + * set, returns defaultValue. + * + * @param config + * @param field + * @param defaultValue + * @return field value or defaultValue + */ private boolean getConfig(StoredConfig config, String field, boolean defaultValue) { return config.getBoolean("gitblit", field, defaultValue); } - public void editRepositoryModel(String repositoryName, RepositoryModel repository, + /** + * Creates/updates the repository model keyed by reopsitoryName. Saves all + * repository settings in .git/config. This method allows for renaming + * repositories and will update user access permissions accordingly. + * + * All repositories created by this method are bare and automatically have + * .git appended to their names, which is the standard convention for bare + * repositories. + * + * @param repositoryName + * @param repository + * @param isCreate + * @throws GitBlitException + */ + public void updateRepositoryModel(String repositoryName, RepositoryModel repository, boolean isCreate) throws GitBlitException { Repository r = null; if (isCreate) { @@ -316,6 +540,8 @@ public class GitBlit implements ServletContextListener { r = repositoryResolver.open(null, repository.name); } catch (RepositoryNotFoundException e) { logger.error("Repository not found", e); + } catch (ServiceNotAuthorizedException e) { + logger.error("Service not authorized", e); } catch (ServiceNotEnabledException e) { logger.error("Service not enabled", e); } @@ -342,15 +568,29 @@ public class GitBlit implements ServletContextListener { } } + /** + * Deletes the repository from the file system and removes the repository + * permission from all repository users. + * + * @param model + * @return true if successful + */ public boolean deleteRepositoryModel(RepositoryModel model) { return deleteRepository(model.name); } + /** + * Deletes the repository from the file system and removes the repository + * permission from all repository users. + * + * @param repositoryName + * @return true if successful + */ public boolean deleteRepository(String repositoryName) { try { File folder = new File(repositoriesFolder, repositoryName); if (folder.exists() && folder.isDirectory()) { - FileUtils.delete(folder, FileUtils.RECURSIVE); + FileUtils.delete(folder, FileUtils.RECURSIVE | FileUtils.RETRY); if (userService.deleteRepositoryRole(repositoryName)) { return true; } @@ -361,25 +601,33 @@ public class GitBlit implements ServletContextListener { return false; } + /** + * Returns an html version of the commit message with any global or + * repository-specific regular expression substitution applied. + * + * @param repositoryName + * @param text + * @return html version of the commit message + */ public String processCommitMessage(String repositoryName, String text) { String html = StringUtils.breakLinesForHtml(text); Map map = new HashMap(); // global regex keys - if (storedSettings.getBoolean(Keys.regex.global, false)) { - for (String key : storedSettings.getAllKeys(Keys.regex.global)) { + if (settings.getBoolean(Keys.regex.global, false)) { + for (String key : settings.getAllKeys(Keys.regex.global)) { if (!key.equals(Keys.regex.global)) { String subKey = key.substring(key.lastIndexOf('.') + 1); - map.put(subKey, storedSettings.getString(key, "")); + map.put(subKey, settings.getString(key, "")); } } } // repository-specific regex keys - List keys = storedSettings.getAllKeys(Keys.regex._ROOT + "." + List keys = settings.getAllKeys(Keys.regex._ROOT + "." + repositoryName.toLowerCase()); for (String key : keys) { String subKey = key.substring(key.lastIndexOf('.') + 1); - map.put(subKey, storedSettings.getString(key, "")); + map.put(subKey, settings.getString(key, "")); } for (Entry entry : map.entrySet()) { @@ -396,23 +644,30 @@ public class GitBlit implements ServletContextListener { return html; } + /** + * Configure the Gitblit singleton with the specified settings source. This + * source may be file settings (Gitblit GO) or may be web.xml settings + * (Gitblit WAR). + * + * @param settings + */ public void configureContext(IStoredSettings settings) { logger.info("Reading configuration from " + settings.toString()); - this.storedSettings = settings; + this.settings = settings; repositoriesFolder = new File(settings.getString(Keys.git.repositoriesFolder, "git")); logger.info("Git repositories folder " + repositoriesFolder.getAbsolutePath()); repositoryResolver = new FileResolver(repositoriesFolder, exportAll); String realm = settings.getString(Keys.realm.userService, "users.properties"); IUserService loginService = null; try { - // Check to see if this "file" is a login service class + // check to see if this "file" is a login service class Class realmClass = Class.forName(realm); if (IUserService.class.isAssignableFrom(realmClass)) { loginService = (IUserService) realmClass.newInstance(); } } catch (Throwable t) { - // Not a login service class OR other issue - // Use default file login service + // not a login service class or class could not be instantiated. + // try to use default file login service File realmFile = new File(realm); if (!realmFile.exists()) { try { @@ -427,16 +682,25 @@ public class GitBlit implements ServletContextListener { setUserService(loginService); } + /** + * Configure Gitblit from the web.xml, if no configuration has already been + * specified. + * + * @see ServletContextListener.contextInitialize(ServletContextEvent) + */ @Override public void contextInitialized(ServletContextEvent contextEvent) { - if (storedSettings == null) { - // for running gitblit as a traditional webapp in a servlet - // container + if (settings == null) { + // Gitblit WAR is running in a servlet container WebXmlSettings webxmlSettings = new WebXmlSettings(contextEvent.getServletContext()); configureContext(webxmlSettings); } } + /** + * Gitblit is being shutdown either because the servlet container is + * shutting down or because the servlet container is re-deploying Gitblit. + */ @Override public void contextDestroyed(ServletContextEvent contextEvent) { logger.info("Gitblit context destroyed by servlet container."); diff --git a/src/com/gitblit/GitBlitException.java b/src/com/gitblit/GitBlitException.java index a79b12de..0cfc7825 100644 --- a/src/com/gitblit/GitBlitException.java +++ b/src/com/gitblit/GitBlitException.java @@ -15,6 +15,12 @@ */ package com.gitblit; +/** + * GitBlitException is a marginally useful class. :) + * + * @author James Moger + * + */ public class GitBlitException extends Exception { private static final long serialVersionUID = 1L; diff --git a/src/com/gitblit/GitBlitServer.java b/src/com/gitblit/GitBlitServer.java index 02cc54a0..61f681f0 100644 --- a/src/com/gitblit/GitBlitServer.java +++ b/src/com/gitblit/GitBlitServer.java @@ -40,6 +40,7 @@ import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSocketConnector; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jgit.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +50,17 @@ import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.gitblit.utils.StringUtils; +/** + * GitBlitServer is the embedded Jetty server for Gitblit GO. This class starts + * and stops an instance of Jetty that is configured from a combination of the + * gitblit.properties file and command line parameters. JCommander is used to + * simplify command line parameter processing. This class also automatically + * generates a self-signed certificate for localhost, if the keystore does not + * already exist. + * + * @author James Moger + * + */ public class GitBlitServer { private static Logger logger; @@ -72,6 +84,12 @@ public class GitBlitServer { } } + /** + * Display the command line usage of Gitblit GO. + * + * @param jc + * @param t + */ private static void usage(JCommander jc, ParameterException t) { System.out.println(Constants.BORDER); System.out.println(Constants.getGitBlitVersion()); @@ -84,13 +102,13 @@ public class GitBlitServer { if (jc != null) { jc.usage(); System.out - .println("\nExample:\n java -server -Xmx1024M -jar gitblit.jar --repos c:\\git --port 80 --securePort 443"); + .println("\nExample:\n java -server -Xmx1024M -jar gitblit.jar --repositoriesFolder c:\\git --httpPort 80 --httpsPort 443"); } System.exit(0); } /** - * Stop Server. + * Stop Gitblt GO. */ public static void stop(Params params) { try { @@ -108,7 +126,7 @@ public class GitBlitServer { } /** - * Start Server. + * Start Gitblit GO. */ private static void start(Params params) { FileSettings settings = Params.FILESETTINGS; @@ -122,8 +140,9 @@ public class GitBlitServer { String osversion = System.getProperty("os.version"); logger.info("Running on " + osname + " (" + osversion + ")"); - // Determine port connectors List connectors = new ArrayList(); + + // conditionally configure the http connector if (params.port > 0) { Connector httpConnector = createConnector(params.useNIO, params.port); String bindInterface = settings.getString(Keys.server.httpBindInterface, null); @@ -135,10 +154,11 @@ public class GitBlitServer { connectors.add(httpConnector); } + // conditionally configure the https connector if (params.securePort > 0) { File keystore = new File("keystore"); if (!keystore.exists()) { - logger.info("Generating self-signed SSL certificate"); + logger.info("Generating self-signed SSL certificate for localhost"); MakeCertificate.generateSelfSignedCertificate("localhost", keystore, params.storePassword); } @@ -158,13 +178,14 @@ public class GitBlitServer { } } - // tempDir = Directory where... - // * WebApp is expanded - // + // tempDir is where the embedded Gitblit web application is expanded and + // where Jetty creates any necessary temporary files File tempDir = new File(params.temp); if (tempDir.exists()) { - if (!deleteRecursively(tempDir)) { - logger.warn("Failed to delete temp dir " + tempDir.getAbsolutePath()); + try { + FileUtils.delete(tempDir, FileUtils.RECURSIVE | FileUtils.RETRY); + } catch (IOException x) { + logger.warn("Failed to delete temp dir " + tempDir.getAbsolutePath(), x); } } if (!tempDir.mkdirs()) { @@ -201,7 +222,7 @@ public class GitBlitServer { return; } - // Override settings + // Override settings from the command-line settings.overrideSetting(Keys.realm.userService, params.userService); settings.overrideSetting(Keys.git.repositoriesFolder, params.repositoriesFolder); @@ -213,12 +234,14 @@ public class GitBlitServer { gitblit.configureContext(settings); rootContext.addEventListener(gitblit); - // Start the Server try { + // start the shutdown monitor if (params.shutdownPort > 0) { Thread shutdownMonitor = new ShutdownMonitorThread(server, params); shutdownMonitor.start(); } + + // start Jetty server.start(); server.join(); } catch (Exception e) { @@ -227,6 +250,13 @@ public class GitBlitServer { } } + /** + * Creates an http connector. + * + * @param useNIO + * @param port + * @return an http cnonector + */ private static Connector createConnector(boolean useNIO, int port) { Connector connector; if (useNIO) { @@ -246,6 +276,15 @@ public class GitBlitServer { return connector; } + /** + * Creates an https connector. + * + * @param keystore + * @param password + * @param useNIO + * @param port + * @return an https connector + */ private static Connector createSSLConnector(File keystore, String password, boolean useNIO, int port) { SslConnector connector; @@ -269,22 +308,13 @@ public class GitBlitServer { } /** - * Recursively delete a folder and its contents. + * The ShutdownMonitorThread opens a socket on a specified port and waits + * for an incoming connection. When that connection is accepted a shutdown + * message is issued to the running Jetty server. + * + * @author James Moger * - * @param folder */ - private static boolean deleteRecursively(File folder) { - boolean deleted = true; - for (File file : folder.listFiles()) { - if (file.isDirectory()) { - deleted &= deleteRecursively(file); - } else { - deleted &= file.delete(); - } - } - return deleted && folder.delete(); - } - private static class ShutdownMonitorThread extends Thread { private final ServerSocket socket; @@ -356,7 +386,8 @@ public class GitBlitServer { * Authentication Parameters */ @Parameter(names = { "--userService" }, description = "Authentication and Authorization Service (filename or fully qualified classname)") - public String userService = FILESETTINGS.getString(Keys.realm.userService, "users.properties"); + public String userService = FILESETTINGS.getString(Keys.realm.userService, + "users.properties"); /* * JETTY Parameters diff --git a/src/com/gitblit/GitFilter.java b/src/com/gitblit/GitFilter.java index b3104426..3011413f 100644 --- a/src/com/gitblit/GitFilter.java +++ b/src/com/gitblit/GitFilter.java @@ -22,6 +22,14 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; +/** + * The GitFilter is an AccessRestrictionFilter which ensures that Git client + * requests for push, clone, or view restricted repositories are authenticated + * and authorized. + * + * @author James Moger + * + */ public class GitFilter extends AccessRestrictionFilter { protected final String gitReceivePack = "/git-receive-pack"; @@ -31,9 +39,16 @@ public class GitFilter extends AccessRestrictionFilter { protected final String[] suffixes = { gitReceivePack, gitUploadPack, "/info/refs", "/HEAD", "/objects" }; + /** + * Extract the repository name from the url. + * + * @param url + * @return repository name + */ @Override protected String extractRepositoryName(String url) { String repository = url; + // get the repository name from the url by finding a known url suffix for (String urlSuffix : suffixes) { if (repository.indexOf(urlSuffix) > -1) { repository = repository.substring(0, repository.indexOf(urlSuffix)); @@ -42,8 +57,15 @@ public class GitFilter extends AccessRestrictionFilter { return repository; } + /** + * Analyze the url and returns the action of the request. Return values are + * either "/git-receive-pack" or "/git-upload-pack". + * + * @param url + * @return action of the request + */ @Override - protected String getUrlRequestType(String suffix) { + protected String getUrlRequestAction(String suffix) { if (!StringUtils.isEmpty(suffix)) { if (suffix.startsWith(gitReceivePack)) { return gitReceivePack; @@ -58,20 +80,35 @@ public class GitFilter extends AccessRestrictionFilter { return null; } + /** + * Determine if the repository requires authentication. + * + * @param repository + * @return true if authentication required + */ @Override protected boolean requiresAuthentication(RepositoryModel repository) { return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH); } + /** + * Determine if the user can access the repository and perform the specified + * action. + * + * @param repository + * @param user + * @param action + * @return true if user may execute the action on the repository + */ @Override - protected boolean canAccess(RepositoryModel repository, UserModel user, String urlRequestType) { + protected boolean canAccess(RepositoryModel repository, UserModel user, String action) { if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) { // Git Servlet disabled return false; } if (repository.isFrozen || repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) { boolean authorizedUser = user.canAccessRepository(repository.name); - if (urlRequestType.equals(gitReceivePack)) { + if (action.equals(gitReceivePack)) { // Push request if (!repository.isFrozen && authorizedUser) { // clone-restricted or push-authorized @@ -82,7 +119,7 @@ public class GitFilter extends AccessRestrictionFilter { user.username, repository)); return false; } - } else if (urlRequestType.equals(gitUploadPack)) { + } else if (action.equals(gitUploadPack)) { // Clone request boolean cloneRestricted = repository.accessRestriction .atLeast(AccessRestrictionType.CLONE); diff --git a/src/com/gitblit/GitServlet.java b/src/com/gitblit/GitServlet.java index daf5d96e..34d1257d 100644 --- a/src/com/gitblit/GitServlet.java +++ b/src/com/gitblit/GitServlet.java @@ -1,9 +1,37 @@ +/* + * 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; +/** + * The GitServlet exists to force configuration of the JGit GitServlet based on + * the Gitblit settings from either gitblit.properties or from context + * parameters in the web.xml file. + * + * Access to this servlet is protected by the GitFilter. + * + * @author James Moger + * + */ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { private static final long serialVersionUID = 1L; + /** + * Configure the servlet from Gitblit's configuration. + */ @Override public String getInitParameter(String name) { if (name.equals("base-path")) { diff --git a/src/com/gitblit/IStoredSettings.java b/src/com/gitblit/IStoredSettings.java index e220a81c..b380c638 100644 --- a/src/com/gitblit/IStoredSettings.java +++ b/src/com/gitblit/IStoredSettings.java @@ -24,6 +24,12 @@ import org.slf4j.LoggerFactory; import com.gitblit.utils.StringUtils; +/** + * Base class for stored settings implementations. + * + * @author James Moger + * + */ public abstract class IStoredSettings { protected final Logger logger; @@ -42,6 +48,13 @@ public abstract class IStoredSettings { return props; } + /** + * Returns the list of keys whose name starts with the specified prefix. If + * the prefix is null or empty, all key names are returned. + * + * @param startingWith + * @return list of keys + */ public List getAllKeys(String startingWith) { List keys = new ArrayList(); Properties props = getSettings(); @@ -59,6 +72,15 @@ public abstract class IStoredSettings { return keys; } + /** + * Returns the boolean value for the specified key. If the key does not + * exist or the value for the key can not be interpreted as a boolean, the + * defaultValue is returned. + * + * @param key + * @param defaultValue + * @return key value or defaultValue + */ public boolean getBoolean(String name, boolean defaultValue) { Properties props = getSettings(); if (props.containsKey(name)) { @@ -70,6 +92,15 @@ public abstract class IStoredSettings { return defaultValue; } + /** + * Returns the integer value for the specified key. If the key does not + * exist or the value for the key can not be interpreted as an integer, the + * defaultValue is returned. + * + * @param key + * @param defaultValue + * @return key value or defaultValue + */ public int getInteger(String name, int defaultValue) { Properties props = getSettings(); if (props.containsKey(name)) { @@ -86,6 +117,15 @@ public abstract class IStoredSettings { return defaultValue; } + /** + * Returns the string value for the specified key. If the key does not exist + * or the value for the key can not be interpreted as a string, the + * defaultValue is returned. + * + * @param key + * @param defaultValue + * @return key value or defaultValue + */ public String getString(String name, String defaultValue) { Properties props = getSettings(); if (props.containsKey(name)) { @@ -97,10 +137,24 @@ public abstract class IStoredSettings { return defaultValue; } + /** + * Returns a list of space-separated strings from the specified key. + * + * @param name + * @return list of strings + */ public List getStrings(String name) { return getStrings(name, " "); } + /** + * Returns a list of strings from the specified key using the specified + * string separator. + * + * @param name + * @param separator + * @return list of strings + */ public List getStrings(String name, String separator) { List strings = new ArrayList(); Properties props = getSettings(); @@ -111,6 +165,12 @@ public abstract class IStoredSettings { return strings; } + /** + * Override the specified key with the specified value. + * + * @param key + * @param value + */ public void overrideSetting(String key, String value) { overrides.put(key, value); } diff --git a/src/com/gitblit/IUserService.java b/src/com/gitblit/IUserService.java index d0d0105a..426d38c0 100644 --- a/src/com/gitblit/IUserService.java +++ b/src/com/gitblit/IUserService.java @@ -19,35 +19,139 @@ import java.util.List; import com.gitblit.models.UserModel; +/** + * Implementations of IUserService control all aspects of UserModel objects and + * user authentication. + * + * @author James Moger + * + */ public interface IUserService { + /** + * Does the user service support cookie authentication? + * + * @return true or false + */ boolean supportsCookies(); + /** + * Returns the cookie value for the specified user. + * + * @param model + * @return cookie value + */ char[] getCookie(UserModel model); + /** + * Authenticate a user based on their cookie. + * + * @param cookie + * @return a user object or null + */ UserModel authenticate(char[] cookie); + /** + * Authenticate a user based on a username and password. + * + * @param username + * @param password + * @return a user object or null + */ UserModel authenticate(String username, char[] password); + /** + * Retrieve the user object for the specified username. + * + * @param username + * @return a user object or null + */ UserModel getUserModel(String username); + /** + * Updates/writes a complete user object. + * + * @param model + * @return true if update is successful + */ boolean updateUserModel(UserModel model); + /** + * Adds/updates a user object keyed by username. This method allows for + * renaming a user. + * + * @param username + * the old username + * @param model + * the user object to use for username + * @return true if update is successful + */ boolean updateUserModel(String username, UserModel model); + /** + * Deletes the user object from the user service. + * + * @param model + * @return true if successful + */ boolean deleteUserModel(UserModel model); + /** + * Delete the user object with the specified username + * + * @param username + * @return true if successful + */ boolean deleteUser(String username); + /** + * Returns the list of all users available to the login service. + * + * @return list of all usernames + */ List getAllUsernames(); - List getUsernamesForRepository(String role); - - boolean setUsernamesForRepository(String role, List usernames); - + /** + * Returns the list of all users who are allowed to bypass the access + * restriction placed on the specified repository. + * + * @param role + * the repository name + * @return list of all usernames that can bypass the access restriction + */ + List getUsernamesForRepositoryRole(String role); + + /** + * Sets the list of all uses who are allowed to bypass the access + * restriction placed on the specified repository. + * + * @param role + * the repository name + * @param usernames + * @return true if successful + */ + boolean setUsernamesForRepositoryRole(String role, List usernames); + + /** + * Renames a repository role. + * + * @param oldRole + * @param newRole + * @return true if successful + */ boolean renameRepositoryRole(String oldRole, String newRole); + /** + * Removes a repository role from all users. + * + * @param role + * @return true if successful + */ boolean deleteRepositoryRole(String role); + /** + * @See java.lang.Object.toString(); + * @return string representation of the login service + */ String toString(); } diff --git a/src/com/gitblit/Launcher.java b/src/com/gitblit/Launcher.java index 7865f244..da616499 100644 --- a/src/com/gitblit/Launcher.java +++ b/src/com/gitblit/Launcher.java @@ -30,9 +30,14 @@ import java.util.List; import com.gitblit.build.Build; /** - * Launch helper class that adds all jars found in the local "lib" folder and - * then calls the application main. Using this technique we do not have to - * specify a classpath and we can dynamically add jars to the distribution. + * Launch helper class that adds all jars found in the local "lib" & "ext" + * folders and then calls the application main. Using this technique we do not + * have to specify a classpath and we can dynamically add jars to the + * distribution. + * + * This class also downloads all runtime dependencies, if they are not found. + * + * @author James Moger * */ public class Launcher { @@ -52,6 +57,7 @@ public class Launcher { + protectionDomain.getCodeSource().getLocation().toExternalForm()); } + // download all runtime dependencies Build.runtime(); // Load the JARs in the lib and ext folder diff --git a/src/com/gitblit/MakeCertificate.java b/src/com/gitblit/MakeCertificate.java index 67253c42..894f7a84 100644 --- a/src/com/gitblit/MakeCertificate.java +++ b/src/com/gitblit/MakeCertificate.java @@ -43,6 +43,12 @@ import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.gitblit.utils.TimeUtils; +/** + * Utility class to generate self-signed certificates. + * + * @author James Moger + * + */ public class MakeCertificate { private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME; diff --git a/src/com/gitblit/ServletRequestWrapper.java b/src/com/gitblit/ServletRequestWrapper.java index b97c395f..9110ebf7 100644 --- a/src/com/gitblit/ServletRequestWrapper.java +++ b/src/com/gitblit/ServletRequestWrapper.java @@ -29,6 +29,22 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +/** + * ServletRequestWrapper is a pass-through/delegate wrapper class for a servlet + * request. This class is used in conjunction with ServletFilters, such as the + * AccessRestrictionFilter. + * + * The original request is wrapped by instances of this class and this class is + * set as the servlet request in the filter. This allows for specialized + * implementations of request methods, like getUserPrincipal() with delegation + * to the original request for any method not overridden. + * + * This class, by itself, is not altogether interesting. Subclasses of this + * class, however, are of interest. + * + * @author James Moger + * + */ public abstract class ServletRequestWrapper implements HttpServletRequest { protected final HttpServletRequest req; diff --git a/src/com/gitblit/SyndicationFilter.java b/src/com/gitblit/SyndicationFilter.java index 68f383b4..e0424605 100644 --- a/src/com/gitblit/SyndicationFilter.java +++ b/src/com/gitblit/SyndicationFilter.java @@ -19,25 +19,60 @@ import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; +/** + * The SyndicationFilter is an AccessRestrictionFilter which ensures that feed + * requests for view-restricted repositories have proper authentication + * credentials and are authorized for the requested feed. + * + * @author James Moger + * + */ public class SyndicationFilter extends AccessRestrictionFilter { + /** + * Extract the repository name from the url. + * + * @param url + * @return repository name + */ @Override protected String extractRepositoryName(String url) { return url; } + /** + * Analyze the url and returns the action of the request. + * + * @param url + * @return action of the request + */ @Override - protected String getUrlRequestType(String url) { - return "RESTRICTED"; + protected String getUrlRequestAction(String url) { + return "VIEW"; } + /** + * Determine if the repository requires authentication. + * + * @param repository + * @return true if authentication required + */ @Override protected boolean requiresAuthentication(RepositoryModel repository) { return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW); } + /** + * Determine if the user can access the repository and perform the specified + * action. + * + * @param repository + * @param user + * @param action + * @return true if user may execute the action on the repository + */ @Override - protected boolean canAccess(RepositoryModel repository, UserModel user, String restrictedURL) { + protected boolean canAccess(RepositoryModel repository, UserModel user, String action) { return user.canAccessRepository(repository.name); } diff --git a/src/com/gitblit/SyndicationServlet.java b/src/com/gitblit/SyndicationServlet.java index 998949ad..df5bb4e5 100644 --- a/src/com/gitblit/SyndicationServlet.java +++ b/src/com/gitblit/SyndicationServlet.java @@ -31,12 +31,32 @@ import com.gitblit.utils.StringUtils; import com.gitblit.utils.SyndicationUtils; import com.gitblit.wicket.WicketUtils; +/** + * SyndicationServlet generates RSS 2.0 feeds and feed links. + * + * Access to this servlet is protected by the SyndicationFilter. + * + * @author James Moger + * + */ public class SyndicationServlet extends HttpServlet { private static final long serialVersionUID = 1L; private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class); + /** + * Create a feed link for the specified repository and branch/tag/commit id. + * + * @param baseURL + * @param repository + * the repository name + * @param objectId + * the branch, tag, or first commit for the feed + * @param length + * the number of commits to include in the feed + * @return an RSS feed url + */ public static String asLink(String baseURL, String repository, String objectId, int length) { if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { baseURL = baseURL.substring(0, baseURL.length() - 1); @@ -63,6 +83,13 @@ public class SyndicationServlet extends HttpServlet { return url.toString(); } + /** + * Determines the appropriate title for a feed. + * + * @param repository + * @param objectId + * @return title of the feed + */ public static String getTitle(String repository, String objectId) { String id = objectId; if (!StringUtils.isEmpty(id)) { @@ -77,6 +104,14 @@ public class SyndicationServlet extends HttpServlet { return MessageFormat.format("{0} ({1})", repository, id); } + /** + * Generates the feed content. + * + * @param request + * @param response + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { diff --git a/src/com/gitblit/WebXmlSettings.java b/src/com/gitblit/WebXmlSettings.java index dff5700e..4b0358d2 100644 --- a/src/com/gitblit/WebXmlSettings.java +++ b/src/com/gitblit/WebXmlSettings.java @@ -22,6 +22,12 @@ import javax.servlet.ServletContext; import com.gitblit.utils.StringUtils; +/** + * Loads Gitblit settings from the context-parameter values of a web.xml file. + * + * @author James Moger + * + */ public class WebXmlSettings extends IStoredSettings { private final Properties properties = new Properties(); @@ -36,9 +42,9 @@ public class WebXmlSettings extends IStoredSettings { logger.debug(key + "=" + properties.getProperty(key)); } } - + private String decodeValue(String value) { - // Decode escaped backslashes and HTML entities + // decode escaped backslashes and HTML entities return StringUtils.decodeFromHtml(value).replace("\\\\", "\\"); } diff --git a/src/com/gitblit/build/Build.java b/src/com/gitblit/build/Build.java index 641b7aa7..998e7ba6 100644 --- a/src/com/gitblit/build/Build.java +++ b/src/com/gitblit/build/Build.java @@ -35,6 +35,18 @@ import java.util.Properties; import com.gitblit.Constants; import com.gitblit.utils.StringUtils; +/** + * The Build class downloads runtime and compile-time jar files from the Apache + * or Eclipse Maven repositories. + * + * It also generates the Keys class from the gitblit.properties file. + * + * Its important that this class have minimal compile dependencies since its + * called very early in the build script. + * + * @author James Moger + * + */ public class Build { public static enum BuildType { @@ -95,6 +107,10 @@ public class Build { downloadFromApache(MavenObject.COMMONSNET, BuildType.RUNTIME); } + /** + * Builds the Keys class based on the gitblit.properties file and inserts + * the class source into the project source folder. + */ public static void buildSettingKeys() { // Load all keys Properties properties = new Properties(); diff --git a/src/com/gitblit/build/BuildSite.java b/src/com/gitblit/build/BuildSite.java index 5a9825aa..81cc8f29 100644 --- a/src/com/gitblit/build/BuildSite.java +++ b/src/com/gitblit/build/BuildSite.java @@ -15,7 +15,6 @@ */ package com.gitblit.build; -import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -37,9 +36,21 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.gitblit.Constants; +import com.gitblit.utils.FileUtils; import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils; +/** + * Builds the web site or deployment documentation from Markdown source files. + * + * All Markdown source files must have the .mkd extension. + * + * Natural string sort order of the Markdown source filenames is the order of + * page links. "##_" prefixes are used to control the sort order. + * + * @author James Moger + * + */ public class BuildSite { public static void main(String... args) { @@ -87,8 +98,8 @@ public class BuildSite { sb.setLength(sb.length() - 3); sb.trimToSize(); - String htmlHeader = readContent(new File(params.pageHeader), "\n"); - String htmlFooter = readContent(new File(params.pageFooter), "\n"); + String htmlHeader = FileUtils.readContent(new File(params.pageHeader), "\n"); + String htmlFooter = FileUtils.readContent(new File(params.pageFooter), "\n"); final String links = sb.toString(); final String header = MessageFormat.format(htmlHeader, Constants.FULL_NAME, links); final String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); @@ -109,7 +120,7 @@ public class BuildSite { } for (String alias : params.loads) { String[] kv = alias.split("="); - String loadedContent = readContent(new File(kv[1]), "\n"); + String loadedContent = FileUtils.readContent(new File(kv[1]), "\n"); loadedContent = StringUtils.escapeForHtml(loadedContent, false); loadedContent = StringUtils.breakLinesForHtml(loadedContent); content = content.replace(kv[0], loadedContent); @@ -129,32 +140,15 @@ public class BuildSite { } } - private static String readContent(File file, String lineEnding) { - StringBuilder sb = new StringBuilder(); - try { - InputStreamReader is = new InputStreamReader(new FileInputStream(file), - Charset.forName("UTF-8")); - BufferedReader reader = new BufferedReader(is); - String line = null; - while ((line = reader.readLine()) != null) { - sb.append(line); - if (lineEnding != null) { - sb.append(lineEnding); - } - } - reader.close(); - } catch (Throwable t) { - System.err.println("Failed to read content of " + file.getAbsolutePath()); - t.printStackTrace(); - } - return sb.toString(); - } - private static String getDocumentName(File file) { String displayName = file.getName().substring(0, file.getName().lastIndexOf('.')) .toLowerCase(); - // trim leading ##_ which is to control display order - return displayName.substring(3); + int underscore = displayName.indexOf('_') + 1; + if (underscore > -1) { + // trim leading ##_ which is to control display order + return displayName.substring(underscore); + } + return displayName; } private static void usage(JCommander jc, ParameterException t) { diff --git a/src/com/gitblit/build/BuildThumbnails.java b/src/com/gitblit/build/BuildThumbnails.java index f1bdbdef..0676ecaf 100644 --- a/src/com/gitblit/build/BuildThumbnails.java +++ b/src/com/gitblit/build/BuildThumbnails.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; +import java.text.MessageFormat; import java.util.Iterator; import javax.imageio.ImageIO; @@ -33,6 +34,12 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; +/** + * Generates PNG thumbnails of the PNG images from the specified source folder. + * + * @author James Moger + * + */ public class BuildThumbnails { public static void main(String[] args) { @@ -47,6 +54,15 @@ public class BuildThumbnails { createImageThumbnail(params.sourceFolder, params.destinationFolder, params.maximumDimension); } + /** + * Generates thumbnails from all PNG images in the source folder and saves + * them to the destination folder. + * + * @param sourceFolder + * @param destinationFolder + * @param maxDimension + * the maximum height or width of the image. + */ public static void createImageThumbnail(String sourceFolder, String destinationFolder, int maxDimension) { if (maxDimension <= 0) @@ -71,18 +87,18 @@ public class BuildThumbnails { // Scale to Width w = maxDimension; float f = maxDimension; - h = (int) ((f / sz.width) * sz.height); // normalize height + // normalize height + h = (int) ((f / sz.width) * sz.height); } else if (sz.height > maxDimension) { // Scale to Height h = maxDimension; float f = maxDimension; - w = (int) ((f / sz.height) * sz.width); // normalize width - } else { - // No thumbnail - return; + // normalize width + w = (int) ((f / sz.height) * sz.width); } - System.out.println("Generating thumbnail for " + sourceFile.getName() + " as (" + w - + "," + h + ")"); + System.out.println(MessageFormat.format( + "Generating thumbnail for {0} as ({1,number,#}, {2,number,#})", + sourceFile.getName(), w, h)); BufferedImage image = ImageIO.read(sourceFile); Image scaledImage = image.getScaledInstance(w, h, BufferedImage.SCALE_SMOOTH); BufferedImage destImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); @@ -98,6 +114,13 @@ public class BuildThumbnails { } } + /** + * Return the dimensions of the specified image file. + * + * @param file + * @return dimensions of the image + * @throws IOException + */ public static Dimension getImageDimensions(File file) throws IOException { ImageInputStream in = ImageIO.createImageInputStream(file); try { diff --git a/src/com/gitblit/build/BuildWebXml.java b/src/com/gitblit/build/BuildWebXml.java index c37f0147..e53a4aff 100644 --- a/src/com/gitblit/build/BuildWebXml.java +++ b/src/com/gitblit/build/BuildWebXml.java @@ -29,9 +29,15 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.gitblit.Keys; -import com.gitblit.Keys.server; import com.gitblit.utils.StringUtils; +/** + * Builds the Gitblit WAR web.xml file by merging the Gitblit GO web.xml file + * with the gitblit.properties comments, settings, and values. + * + * @author James Moger + * + */ public class BuildWebXml { private static final String PARAMS = ""; @@ -88,7 +94,8 @@ public class BuildWebXml { for (String comment : setting.comments) { parameters.append(MessageFormat.format(COMMENT_PATTERN, comment)); } - parameters.append(MessageFormat.format(PARAM_PATTERN, setting.name, StringUtils.escapeForHtml(setting.value, false))); + parameters.append(MessageFormat.format(PARAM_PATTERN, setting.name, + StringUtils.escapeForHtml(setting.value, false))); } // Read the prototype web.xml file diff --git a/src/com/gitblit/utils/FileUtils.java b/src/com/gitblit/utils/FileUtils.java new file mode 100644 index 00000000..3cfe6894 --- /dev/null +++ b/src/com/gitblit/utils/FileUtils.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.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +/** + * Common file utilities. + * + * @author James Moger + * + */ +public class FileUtils { + + /** + * Returns the string content of the specified file. + * + * @param file + * @param lineEnding + * @return + */ + public static String readContent(File file, String lineEnding) { + StringBuilder sb = new StringBuilder(); + try { + InputStreamReader is = new InputStreamReader(new FileInputStream(file), + Charset.forName("UTF-8")); + BufferedReader reader = new BufferedReader(is); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line); + if (lineEnding != null) { + sb.append(lineEnding); + } + } + reader.close(); + } catch (Throwable t) { + System.err.println("Failed to read content of " + file.getAbsolutePath()); + t.printStackTrace(); + } + return sb.toString(); + } +} diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/com/gitblit/wicket/pages/EditRepositoryPage.java index 52ed548c..af9358e7 100644 --- a/src/com/gitblit/wicket/pages/EditRepositoryPage.java +++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.java @@ -143,7 +143,7 @@ public class EditRepositoryPage extends BasePage { } // save the repository - GitBlit.self().editRepositoryModel(oldName, repositoryModel, isCreate); + GitBlit.self().updateRepositoryModel(oldName, repositoryModel, isCreate); // save the repository access list if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) { diff --git a/src/com/gitblit/wicket/pages/EditUserPage.java b/src/com/gitblit/wicket/pages/EditUserPage.java index 8f68ac28..4ba892f6 100644 --- a/src/com/gitblit/wicket/pages/EditUserPage.java +++ b/src/com/gitblit/wicket/pages/EditUserPage.java @@ -143,7 +143,7 @@ public class EditUserPage extends BasePage { userModel.repositories.clear(); userModel.repositories.addAll(repos); try { - GitBlit.self().editUserModel(oldName, userModel, isCreate); + GitBlit.self().updateUserModel(oldName, userModel, isCreate); } catch (GitBlitException e) { error(e.getMessage()); return; diff --git a/tests/com/gitblit/tests/GitBlitSuite.java b/tests/com/gitblit/tests/GitBlitSuite.java index 31e29c96..bbf7c4b3 100644 --- a/tests/com/gitblit/tests/GitBlitSuite.java +++ b/tests/com/gitblit/tests/GitBlitSuite.java @@ -101,7 +101,7 @@ public class GitBlitSuite extends TestSetup { try { RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName); model.useTickets = true; - GitBlit.self().editRepositoryModel(model.name, model, false); + GitBlit.self().updateRepositoryModel(model.name, model, false); } catch (GitBlitException g) { g.printStackTrace(); } @@ -111,7 +111,7 @@ public class GitBlitSuite extends TestSetup { try { RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName); model.useDocs = true; - GitBlit.self().editRepositoryModel(model.name, model, false); + GitBlit.self().updateRepositoryModel(model.name, model, false); } catch (GitBlitException g) { g.printStackTrace(); } @@ -121,7 +121,7 @@ public class GitBlitSuite extends TestSetup { try { RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName); model.showRemoteBranches = true; - GitBlit.self().editRepositoryModel(model.name, model, false); + GitBlit.self().updateRepositoryModel(model.name, model, false); } catch (GitBlitException g) { g.printStackTrace(); } -- 2.39.5