- Gitblit may have security holes. Patches welcome. :)\r
\r
### Todo List\r
-- Custom BASIC authentication servlet or servlet filter\r
- Code documentation\r
- Unit testing\r
- Update Build.java to JGit 1.0.0, when its released\r
+- WAR solution\r
\r
### Idea List\r
- Consider clone remote repository feature\r
-- Consider [Apache Shiro](http://shiro.apache.org) for authentication\r
- Stronger Ticgit read-only integration\r
- activity/timeline\r
- query feature with paging support\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.IOException;\r
+import java.security.Principal;\r
+import java.text.MessageFormat;\r
+import java.util.Enumeration;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import javax.servlet.Filter;\r
+import javax.servlet.FilterChain;\r
+import javax.servlet.FilterConfig;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.ServletRequest;\r
+import javax.servlet.ServletResponse;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+import javax.servlet.http.HttpSession;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * \r
+ * http://en.wikipedia.org/wiki/Basic_access_authentication\r
+ */\r
+public abstract class AccessRestrictionFilter implements Filter {\r
+\r
+ private static final String BASIC = "Basic";\r
+\r
+ private static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";\r
+\r
+ private static final String SESSION_SECURED = "com.gitblit.secured";\r
+\r
+ protected transient Logger logger;\r
+\r
+ public AccessRestrictionFilter() {\r
+ logger = LoggerFactory.getLogger(getClass());\r
+ }\r
+\r
+ protected abstract String extractRepositoryName(String url);\r
+\r
+ protected abstract String getUrlRequestType(String url);\r
+\r
+ protected abstract boolean requiresAuthentication(RepositoryModel repository);\r
+\r
+ protected abstract boolean canAccess(RepositoryModel repository, UserModel user,\r
+ String restrictedUrl);\r
+\r
+ @Override\r
+ public void doFilter(final ServletRequest request, final ServletResponse response,\r
+ final FilterChain chain) throws IOException, ServletException {\r
+\r
+ HttpServletRequest httpRequest = (HttpServletRequest) request;\r
+ HttpServletResponse httpResponse = (HttpServletResponse) response;\r
+\r
+ // Wrap the HttpServletRequest with the AccessRestrictionRequest which\r
+ // overrides the servlet container user principal methods.\r
+ // JGit requires either:\r
+ //\r
+ // 1. servlet container authenticated user\r
+ // 2. http.receivepack = true in each repository's config\r
+ //\r
+ // Gitblit must conditionally authenticate users per-repository so just\r
+ // enabling http.receivepack is insufficient.\r
+\r
+ AccessRestrictionRequest accessRequest = new AccessRestrictionRequest(httpRequest);\r
+\r
+ String url = httpRequest.getRequestURI().substring(httpRequest.getServletPath().length());\r
+ String params = httpRequest.getQueryString();\r
+ if (url.length() > 0 && url.charAt(0) == '/') {\r
+ url = url.substring(1);\r
+ }\r
+ String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));\r
+\r
+ String repository = extractRepositoryName(url);\r
+\r
+ // Determine if the request URL is restricted\r
+ String fullSuffix = fullUrl.substring(repository.length());\r
+ String urlRequestType = getUrlRequestType(fullSuffix);\r
+\r
+ // Load the repository model\r
+ RepositoryModel model = GitBlit.self().getRepositoryModel(repository);\r
+ if (model == null) {\r
+ // repository not found. send 404.\r
+ logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_NOT_FOUND + ")");\r
+ httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);\r
+ return;\r
+ }\r
+\r
+ // BASIC authentication challenge and response processing\r
+ if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model)) {\r
+ // look for client authorization credentials in header\r
+ final String authorization = httpRequest.getHeader("Authorization");\r
+ if (authorization != null && authorization.startsWith(BASIC)) {\r
+ // Authorization: Basic base64credentials\r
+ String base64Credentials = authorization.substring(BASIC.length()).trim();\r
+ String credentials = StringUtils.decodeBase64(base64Credentials);\r
+ if (GitBlit.isDebugMode()) {\r
+ logger.info(MessageFormat.format("AUTH: {0} ({1})", authorization, credentials));\r
+ }\r
+ // credentials = username:password\r
+ final String[] values = credentials.split(":");\r
+\r
+ if (values.length == 2) {\r
+ String username = values[0];\r
+ char[] password = values[1].toCharArray();\r
+ UserModel user = GitBlit.self().authenticate(username, password);\r
+ if (user != null) {\r
+ accessRequest.setUser(user);\r
+ if (user.canAdmin || canAccess(model, user, urlRequestType)) {\r
+ // authenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ newSession(accessRequest, httpResponse);\r
+ logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE + ") authenticated");\r
+ chain.doFilter(accessRequest, httpResponse);\r
+ return;\r
+ }\r
+ // valid user, but not for requested access. send 403.\r
+ if (GitBlit.isDebugMode()) {\r
+ logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_FORBIDDEN\r
+ + ")");\r
+ logger.info(MessageFormat.format("AUTH: {0} forbidden to access {1}",\r
+ user.username, url));\r
+ }\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ }\r
+ if (GitBlit.isDebugMode()) {\r
+ logger.info(MessageFormat\r
+ .format("AUTH: invalid credentials ({0})", credentials));\r
+ }\r
+ }\r
+\r
+ // challenge client to provide credentials. send 401.\r
+ if (GitBlit.isDebugMode()) {\r
+ logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_UNAUTHORIZED + ")");\r
+ logger.info("AUTH: Challenge " + CHALLENGE);\r
+ }\r
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
+ return;\r
+ }\r
+\r
+ if (GitBlit.isDebugMode()) {\r
+ logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE + ") unauthenticated");\r
+ }\r
+ // unauthenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ chain.doFilter(accessRequest, httpResponse);\r
+ }\r
+\r
+ /**\r
+ * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()\r
+ */\r
+ protected void newSession(HttpServletRequest request, HttpServletResponse response) {\r
+ HttpSession oldSession = request.getSession(false);\r
+ if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {\r
+ synchronized (this) {\r
+ Map<String, Object> attributes = new HashMap<String, Object>();\r
+ Enumeration<String> e = oldSession.getAttributeNames();\r
+ while (e.hasMoreElements()) {\r
+ String name = e.nextElement();\r
+ attributes.put(name, oldSession.getAttribute(name));\r
+ oldSession.removeAttribute(name);\r
+ }\r
+ oldSession.invalidate();\r
+\r
+ HttpSession newSession = request.getSession(true);\r
+ newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);\r
+ for (Map.Entry<String, Object> entry : attributes.entrySet()) {\r
+ newSession.setAttribute(entry.getKey(), entry.getValue());\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void init(final FilterConfig config) throws ServletException {\r
+ }\r
+\r
+ @Override\r
+ public void destroy() {\r
+ }\r
+ \r
+ /**\r
+ * Wraps a standard HttpServletRequest and overrides user principal methods.\r
+ */\r
+ public static class AccessRestrictionRequest extends ServletRequestWrapper {\r
+\r
+ private UserModel user;\r
+ \r
+ public AccessRestrictionRequest(HttpServletRequest req) {\r
+ super(req);\r
+ user = new UserModel("anonymous");\r
+ }\r
+ \r
+ void setUser(UserModel user) {\r
+ this.user = user;\r
+ }\r
+\r
+ @Override\r
+ public String getRemoteUser() {\r
+ return user.username;\r
+ }\r
+\r
+ @Override\r
+ public boolean isUserInRole(String role) {\r
+ if (role.equals(Constants.ADMIN_ROLE)) {\r
+ return user.canAdmin;\r
+ }\r
+ return user.canAccessRepository(role);\r
+ }\r
+\r
+ @Override\r
+ public Principal getUserPrincipal() {\r
+ return user;\r
+ }\r
+ }\r
+}
\ No newline at end of file
public static final String ZIP_SERVLET_PATH = "/zip/";\r
\r
public static final String SYNDICATION_SERVLET_PATH = "/feed/";\r
+ \r
+ public static final String RESOURCE_PATH = "/com/gitblit/wicket/resources/";\r
\r
public static final String BORDER = "***********************************************************";\r
\r
}\r
\r
public static String asLink(String baseURL, String repository, String objectId, String path) {\r
- if (baseURL.charAt(baseURL.length() - 1) == '/') {\r
+ if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
baseURL = baseURL.substring(0, baseURL.length() - 1);\r
}\r
return baseURL + Constants.ZIP_SERVLET_PATH + "?r=" + repository\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.File;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Properties;\r
+import java.util.Set;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+public class FileLoginService extends FileSettings implements ILoginService {\r
+\r
+ private final Logger logger = LoggerFactory.getLogger(FileLoginService.class);\r
+\r
+ public FileLoginService(File realmFile) {\r
+ super(realmFile.getAbsolutePath());\r
+ }\r
+\r
+ @Override\r
+ public UserModel authenticate(String username, char[] password) {\r
+ Properties allUsers = read();\r
+ String userInfo = allUsers.getProperty(username);\r
+ if (StringUtils.isEmpty(userInfo)) {\r
+ return null;\r
+ }\r
+ UserModel returnedUser = null;\r
+ UserModel user = getUserModel(username);\r
+ if (user.password.startsWith(StringUtils.MD5_TYPE)) {\r
+ String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password));\r
+ if (user.password.equalsIgnoreCase(md5)) {\r
+ returnedUser = user;\r
+ }\r
+ }\r
+ if (user.password.equals(new String(password))) {\r
+ returnedUser = user;\r
+ }\r
+ return returnedUser;\r
+ }\r
+\r
+ @Override\r
+ public UserModel getUserModel(String username) {\r
+ Properties allUsers = read();\r
+ String userInfo = allUsers.getProperty(username);\r
+ if (userInfo == null) {\r
+ return null;\r
+ }\r
+ UserModel model = new UserModel(username);\r
+ String[] userValues = userInfo.split(",");\r
+ model.password = userValues[0];\r
+ for (int i = 1; i < userValues.length; i++) {\r
+ String role = userValues[i];\r
+ switch (role.charAt(0)) {\r
+ case '#':\r
+ // Permissions\r
+ if (role.equalsIgnoreCase(Constants.ADMIN_ROLE)) {\r
+ model.canAdmin = true;\r
+ }\r
+ break;\r
+ default:\r
+ model.addRepository(role);\r
+ }\r
+ }\r
+ return model;\r
+ }\r
+\r
+ @Override\r
+ public boolean updateUserModel(UserModel model) {\r
+ return updateUserModel(model.username, model);\r
+ }\r
+\r
+ @Override\r
+ public boolean updateUserModel(String username, UserModel model) {\r
+ try {\r
+ Properties allUsers = read();\r
+ ArrayList<String> roles = new ArrayList<String>(model.repositories);\r
+\r
+ // Permissions\r
+ if (model.canAdmin) {\r
+ roles.add(Constants.ADMIN_ROLE);\r
+ }\r
+\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append(model.password);\r
+ sb.append(',');\r
+ for (String role : roles) {\r
+ sb.append(role);\r
+ sb.append(',');\r
+ }\r
+ // trim trailing comma\r
+ sb.setLength(sb.length() - 1);\r
+ allUsers.remove(username);\r
+ allUsers.put(model.username, sb.toString());\r
+\r
+ write(allUsers);\r
+ return true;\r
+ } catch (Throwable t) {\r
+ logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),\r
+ t);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public boolean deleteUserModel(UserModel model) {\r
+ return deleteUser(model.username);\r
+ }\r
+\r
+ @Override\r
+ public boolean deleteUser(String username) {\r
+ try {\r
+ // Read realm file\r
+ Properties allUsers = read();\r
+ allUsers.remove(username);\r
+ write(allUsers);\r
+ return true;\r
+ } catch (Throwable t) {\r
+ logger.error(MessageFormat.format("Failed to delete user {0}!", username), t);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public List<String> getAllUsernames() {\r
+ Properties allUsers = read();\r
+ List<String> list = new ArrayList<String>(allUsers.stringPropertyNames());\r
+ return list;\r
+ }\r
+\r
+ @Override\r
+ public List<String> getUsernamesForRole(String role) {\r
+ List<String> list = new ArrayList<String>();\r
+ try {\r
+ Properties allUsers = read();\r
+ for (String username : allUsers.stringPropertyNames()) {\r
+ String value = allUsers.getProperty(username);\r
+ String[] values = value.split(",");\r
+ // skip first value (password)\r
+ for (int i = 1; i < values.length; i++) {\r
+ String r = values[i];\r
+ if (r.equalsIgnoreCase(role)) {\r
+ list.add(username);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ } catch (Throwable t) {\r
+ logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t);\r
+ }\r
+ return list;\r
+ }\r
+\r
+ @Override\r
+ public boolean setUsernamesForRole(String role, List<String> usernames) {\r
+ try {\r
+ Set<String> specifiedUsers = new HashSet<String>(usernames);\r
+ Set<String> needsAddRole = new HashSet<String>(specifiedUsers);\r
+ Set<String> needsRemoveRole = new HashSet<String>();\r
+\r
+ // identify users which require add and remove role\r
+ Properties allUsers = read();\r
+ for (String username : allUsers.stringPropertyNames()) {\r
+ String value = allUsers.getProperty(username);\r
+ String[] values = value.split(",");\r
+ // skip first value (password)\r
+ for (int i = 1; i < values.length; i++) {\r
+ String r = values[i];\r
+ if (r.equalsIgnoreCase(role)) {\r
+ // user has role, check against revised user list\r
+ if (specifiedUsers.contains(username)) {\r
+ needsAddRole.remove(username);\r
+ } else {\r
+ // remove role from user\r
+ needsRemoveRole.add(username);\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // add roles to users\r
+ for (String user : needsAddRole) {\r
+ String userValues = allUsers.getProperty(user);\r
+ userValues += "," + role;\r
+ allUsers.put(user, userValues);\r
+ }\r
+\r
+ // remove role from user\r
+ for (String user : needsRemoveRole) {\r
+ String[] values = allUsers.getProperty(user).split(",");\r
+ String password = values[0];\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append(password);\r
+ sb.append(',');\r
+ List<String> revisedRoles = new ArrayList<String>();\r
+ // skip first value (password)\r
+ for (int i = 1; i < values.length; i++) {\r
+ String value = values[i];\r
+ if (!value.equalsIgnoreCase(role)) {\r
+ revisedRoles.add(value);\r
+ sb.append(value);\r
+ sb.append(',');\r
+ }\r
+ }\r
+ sb.setLength(sb.length() - 1);\r
+\r
+ // update properties\r
+ allUsers.put(user, sb.toString());\r
+ }\r
+\r
+ // persist changes\r
+ write(allUsers);\r
+ return true;\r
+ } catch (Throwable t) {\r
+ logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public boolean renameRole(String oldRole, String newRole) {\r
+ try {\r
+ Properties allUsers = read();\r
+ Set<String> needsRenameRole = new HashSet<String>();\r
+\r
+ // identify users which require role rename\r
+ for (String username : allUsers.stringPropertyNames()) {\r
+ String value = allUsers.getProperty(username);\r
+ String[] roles = value.split(",");\r
+ // skip first value (password)\r
+ for (int i = 1; i < roles.length; i++) {\r
+ String r = roles[i];\r
+ if (r.equalsIgnoreCase(oldRole)) {\r
+ needsRenameRole.remove(username);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // rename role for identified users\r
+ for (String user : needsRenameRole) {\r
+ String userValues = allUsers.getProperty(user);\r
+ String[] values = userValues.split(",");\r
+ String password = values[0];\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append(password);\r
+ sb.append(',');\r
+ List<String> revisedRoles = new ArrayList<String>();\r
+ revisedRoles.add(newRole);\r
+ // skip first value (password)\r
+ for (int i = 1; i < values.length; i++) {\r
+ String value = values[i];\r
+ if (!value.equalsIgnoreCase(oldRole)) {\r
+ revisedRoles.add(value);\r
+ sb.append(value);\r
+ sb.append(',');\r
+ }\r
+ }\r
+ sb.setLength(sb.length() - 1);\r
+\r
+ // update properties\r
+ allUsers.put(user, sb.toString());\r
+ }\r
+\r
+ // persist changes\r
+ write(allUsers);\r
+ return true;\r
+ } catch (Throwable t) {\r
+ logger.error(\r
+ MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public boolean deleteRole(String role) {\r
+ try {\r
+ Properties allUsers = read();\r
+ Set<String> needsDeleteRole = new HashSet<String>();\r
+\r
+ // identify users which require role rename\r
+ for (String username : allUsers.stringPropertyNames()) {\r
+ String value = allUsers.getProperty(username);\r
+ String[] roles = value.split(",");\r
+ // skip first value (password)\r
+ for (int i = 1; i < roles.length; i++) {\r
+ String r = roles[i];\r
+ if (r.equalsIgnoreCase(role)) {\r
+ needsDeleteRole.remove(username);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // delete role for identified users\r
+ for (String user : needsDeleteRole) {\r
+ String userValues = allUsers.getProperty(user);\r
+ String[] values = userValues.split(",");\r
+ String password = values[0];\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append(password);\r
+ sb.append(',');\r
+ List<String> revisedRoles = new ArrayList<String>();\r
+ // skip first value (password)\r
+ for (int i = 1; i < values.length; i++) {\r
+ String value = values[i];\r
+ if (!value.equalsIgnoreCase(role)) {\r
+ revisedRoles.add(value);\r
+ sb.append(value);\r
+ sb.append(',');\r
+ }\r
+ }\r
+ sb.setLength(sb.length() - 1);\r
+\r
+ // update properties\r
+ allUsers.put(user, sb.toString());\r
+ }\r
+\r
+ // persist changes\r
+ write(allUsers);\r
+ return true;\r
+ } catch (Throwable t) {\r
+ logger.error(MessageFormat.format("Failed to delete role {0}!", role), t);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ private void write(Properties properties) throws IOException {\r
+ // Update realm file\r
+ File realmFileCopy = new File(propertiesFile.getAbsolutePath() + ".tmp");\r
+ FileWriter writer = new FileWriter(realmFileCopy);\r
+ properties\r
+ .store(writer,\r
+ "# Gitblit realm file format: username=password,\\#permission,repository1,repository2...");\r
+ writer.close();\r
+ if (realmFileCopy.exists() && realmFileCopy.length() > 0) {\r
+ if (propertiesFile.delete()) {\r
+ if (!realmFileCopy.renameTo(propertiesFile)) {\r
+ throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",\r
+ realmFileCopy.getAbsolutePath(), propertiesFile.getAbsolutePath()));\r
+ }\r
+ } else {\r
+ throw new IOException(MessageFormat.format("Failed to delete (0)!",\r
+ propertiesFile.getAbsolutePath()));\r
+ }\r
+ } else {\r
+ throw new IOException(MessageFormat.format("Failed to save {0}!",\r
+ realmFileCopy.getAbsolutePath()));\r
+ }\r
+ }\r
+}\r
*/\r
public class FileSettings extends IStoredSettings {\r
\r
- private final File propertiesFile;\r
+ protected final File propertiesFile;\r
\r
private final Properties properties = new Properties();\r
\r
import java.text.MessageFormat;\r
import java.util.ArrayList;\r
import java.util.Collections;\r
+import java.util.HashMap;\r
import java.util.List;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
\r
import javax.servlet.ServletContextEvent;\r
import javax.servlet.ServletContextListener;\r
return GITBLIT.storedSettings.getAllKeys(startingWith);\r
}\r
\r
- public boolean isDebugMode() {\r
- return storedSettings.getBoolean(Keys.web.debugMode, false);\r
+ public static boolean isDebugMode() {\r
+ return GITBLIT.storedSettings.getBoolean(Keys.web.debugMode, false);\r
}\r
\r
public List<String> getOtherCloneUrls(String repositoryName) {\r
return false;\r
}\r
\r
+ public String processCommitMessage(String repositoryName, String text) {\r
+ String html = StringUtils.breakLinesForHtml(text);\r
+ Map<String, String> map = new HashMap<String, String>();\r
+ // global regex keys\r
+ if (storedSettings.getBoolean(Keys.regex.global, false)) {\r
+ for (String key : storedSettings.getAllKeys(Keys.regex.global)) {\r
+ if (!key.equals(Keys.regex.global)) {\r
+ String subKey = key.substring(key.lastIndexOf('.') + 1);\r
+ map.put(subKey, storedSettings.getString(key, ""));\r
+ }\r
+ }\r
+ }\r
+\r
+ // repository-specific regex keys\r
+ List<String> keys = storedSettings.getAllKeys(Keys.regex._ROOT + "."\r
+ + repositoryName.toLowerCase());\r
+ for (String key : keys) {\r
+ String subKey = key.substring(key.lastIndexOf('.') + 1);\r
+ map.put(subKey, storedSettings.getString(key, ""));\r
+ }\r
+\r
+ for (Entry<String, String> entry : map.entrySet()) {\r
+ String definition = entry.getValue().trim();\r
+ String[] chunks = definition.split("!!!");\r
+ if (chunks.length == 2) {\r
+ html = html.replaceAll(chunks[0], chunks[1]);\r
+ } else {\r
+ logger.warn(entry.getKey()\r
+ + " improperly formatted. Use !!! to separate match from replacement: "\r
+ + definition);\r
+ }\r
+ }\r
+ return html;\r
+ }\r
+\r
public void configureContext(IStoredSettings settings) {\r
logger.info("Reading configuration from " + settings.toString());\r
this.storedSettings = settings;\r
@Override\r
public void contextInitialized(ServletContextEvent contextEvent) {\r
if (storedSettings == null) {\r
- // for running gitblit as a traditional webapp in a servlet container\r
+ // for running gitblit as a traditional webapp in a servlet\r
+ // container\r
WebXmlSettings webxmlSettings = new WebXmlSettings(contextEvent.getServletContext());\r
configureContext(webxmlSettings);\r
}\r
import org.apache.log4j.PatternLayout;\r
import org.apache.wicket.protocol.http.ContextParamWebApplicationFactory;\r
import org.apache.wicket.protocol.http.WicketFilter;\r
-import org.eclipse.jetty.http.security.Constraint;\r
-import org.eclipse.jetty.security.ConstraintMapping;\r
-import org.eclipse.jetty.security.ConstraintSecurityHandler;\r
-import org.eclipse.jetty.security.LoginService;\r
-import org.eclipse.jetty.security.authentication.BasicAuthenticator;\r
import org.eclipse.jetty.server.Connector;\r
-import org.eclipse.jetty.server.Handler;\r
import org.eclipse.jetty.server.Server;\r
import org.eclipse.jetty.server.bio.SocketConnector;\r
import org.eclipse.jetty.server.nio.SelectChannelConnector;\r
import org.eclipse.jetty.servlet.ServletHolder;\r
import org.eclipse.jetty.util.thread.QueuedThreadPool;\r
import org.eclipse.jetty.webapp.WebAppContext;\r
+import org.eclipse.jgit.http.server.GitServlet;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
\r
wicketFilter.setInitParameter(ContextParamWebApplicationFactory.APP_CLASS_PARAM,\r
GitBlitWebApp.class.getName());\r
wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec);\r
- wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/");\r
+ wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/,feed/,zip/");\r
rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT);\r
\r
- // Zip Servlet\r
- rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");\r
-\r
- // Syndication Servlet\r
- rootContext.addServlet(SyndicationServlet.class, Constants.SYNDICATION_SERVLET_PATH + "*");\r
-\r
- // Git Servlet\r
- ServletHolder gitServlet = null;\r
- String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*";\r
+ // JGit Filter and Servlet\r
if (settings.getBoolean(Keys.git.enableGitServlet, true)) {\r
- gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec);\r
- gitServlet.setInitParameter("base-path", params.repositoriesFolder);\r
- gitServlet.setInitParameter("export-all",\r
+ String jgitPathSpec = Constants.GIT_SERVLET_PATH + "*";\r
+ rootContext.addFilter(GitFilter.class, jgitPathSpec, FilterMapping.DEFAULT);\r
+ ServletHolder jGitServlet = rootContext.addServlet(GitServlet.class, jgitPathSpec);\r
+ jGitServlet.setInitParameter("base-path", params.repositoriesFolder);\r
+ jGitServlet.setInitParameter("export-all",\r
settings.getBoolean(Keys.git.exportAll, true) ? "1" : "0");\r
}\r
\r
+ // Syndication Filter and Servlet\r
+ String feedPathSpec = Constants.SYNDICATION_SERVLET_PATH + "*";\r
+ rootContext.addFilter(SyndicationFilter.class, feedPathSpec, FilterMapping.DEFAULT);\r
+ rootContext.addServlet(SyndicationServlet.class, feedPathSpec);\r
+\r
+ // Zip Servlet\r
+ rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");\r
+\r
// Login Service\r
- LoginService loginService = null;\r
String realmUsers = params.realmFile;\r
- if (!StringUtils.isEmpty(realmUsers)) {\r
- File realmFile = new File(realmUsers);\r
- if (realmFile.exists()) {\r
- logger.info("Setting up login service from " + realmUsers);\r
- JettyLoginService jettyLoginService = new JettyLoginService(realmFile);\r
- GitBlit.self().setLoginService(jettyLoginService);\r
- loginService = jettyLoginService;\r
- }\r
+ if (StringUtils.isEmpty(realmUsers)) {\r
+ logger.error(MessageFormat.format("PLEASE SPECIFY {0}!!", Keys.realm.realmFile));\r
+ return;\r
}\r
-\r
- // Determine what handler to use\r
- Handler handler;\r
- if (gitServlet != null) {\r
- if (loginService != null) {\r
- // Authenticate Clone/Push\r
- logger.info("Setting up authenticated git servlet clone/push access");\r
-\r
- Constraint constraint = new Constraint();\r
- constraint.setAuthenticate(true);\r
- constraint.setRoles(new String[] { "*" });\r
-\r
- ConstraintMapping mapping = new ConstraintMapping();\r
- mapping.setPathSpec(gitServletPathSpec);\r
- mapping.setConstraint(constraint);\r
-\r
- ConstraintSecurityHandler security = new ConstraintSecurityHandler();\r
- security.addConstraintMapping(mapping);\r
- security.setAuthenticator(new BasicAuthenticator());\r
- security.setLoginService(loginService);\r
- security.setStrict(false);\r
-\r
- security.setHandler(rootContext);\r
-\r
- handler = security;\r
- } else {\r
- // Anonymous Pull/Push\r
- logger.info("Setting up anonymous git servlet pull/push access");\r
- handler = rootContext;\r
+ File realmFile = new File(realmUsers);\r
+ if (!realmFile.exists()) {\r
+ try {\r
+ realmFile.createNewFile();\r
+ } catch (IOException x) {\r
+ logger.error(MessageFormat.format("COULD NOT CREATE REALM FILE {0}!", realmUsers),\r
+ x);\r
+ return;\r
}\r
- } else {\r
- logger.info("Git servlet clone/push disabled");\r
- handler = rootContext;\r
}\r
+ logger.info("Setting up login service from " + realmUsers);\r
+ FileLoginService loginService = new FileLoginService(realmFile);\r
+ GitBlit.self().setLoginService(loginService);\r
\r
logger.info("Git repositories folder "\r
+ new File(params.repositoriesFolder).getAbsolutePath());\r
\r
// Set the server's contexts\r
- server.setHandler(handler);\r
+ server.setHandler(rootContext);\r
\r
// Setup the GitBlit context\r
GitBlit gitblit = GitBlit.self();\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.eclipse.jgit.http.server.GitServlet;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.AccessRestrictionType;\r
-import com.gitblit.models.RepositoryModel;\r
-\r
-public class GitBlitServlet extends GitServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private transient Logger logger = LoggerFactory.getLogger(GitBlitServlet.class);\r
-\r
- public GitBlitServlet() {\r
- super();\r
- }\r
-\r
- @Override\r
- protected void service(final HttpServletRequest req, final HttpServletResponse rsp)\r
- throws ServletException, IOException {\r
- // admins have full git access to all repositories\r
- if (req.isUserInRole(Constants.ADMIN_ROLE)) {\r
- // admins can do whatever\r
- super.service(req, rsp);\r
- return;\r
- }\r
-\r
- // try to intercept repository names for authenticated access\r
- String url = req.getRequestURI().substring(req.getServletPath().length());\r
- if (url.charAt(0) == '/' && url.length() > 1) {\r
- url = url.substring(1);\r
- }\r
- int forwardSlash = url.indexOf('/');\r
- if (forwardSlash > -1) {\r
- String repository = url.substring(0, forwardSlash).toLowerCase();\r
- String function = url.substring(forwardSlash + 1);\r
- String query = req.getQueryString() == null ? "" : req.getQueryString();\r
- RepositoryModel model = GitBlit.self().getRepositoryModel(repository);\r
- if (model != null) {\r
- if (model.isFrozen || model.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {\r
- boolean authorizedUser = req.isUserInRole(repository);\r
- if (function.startsWith("git-receive-pack")\r
- || (query.indexOf("service=git-receive-pack") > -1)) {\r
- // Push request\r
- if (!model.isFrozen && authorizedUser) {\r
- // clone-restricted or push-authorized\r
- super.service(req, rsp);\r
- return;\r
- } else {\r
- // user is unauthorized to push to this repository\r
- logger.warn(MessageFormat.format(\r
- "user {0} is not authorized to push to {1}", req\r
- .getUserPrincipal().getName(), repository));\r
- rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format(\r
- "you are not authorized to push to {0}", repository));\r
- return;\r
- }\r
- } else if (function.startsWith("git-upload-pack")\r
- || (query.indexOf("service=git-upload-pack") > -1)) {\r
- // Clone request\r
- boolean cloneRestricted = model.accessRestriction\r
- .atLeast(AccessRestrictionType.CLONE);\r
- if (!cloneRestricted || (cloneRestricted && authorizedUser)) {\r
- // push-restricted or clone-authorized\r
- super.service(req, rsp);\r
- return;\r
- } else {\r
- // user is unauthorized to clone this repository\r
- logger.warn(MessageFormat.format(\r
- "user {0} is not authorized to clone {1}", req\r
- .getUserPrincipal().getName(), repository));\r
- rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format(\r
- "you are not authorized to clone {0}", repository));\r
- return;\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- // pass-through to git servlet\r
- super.service(req, rsp);\r
- }\r
-}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.text.MessageFormat;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+public class GitFilter extends AccessRestrictionFilter {\r
+\r
+ protected final String gitReceivePack = "/git-receive-pack";\r
+\r
+ protected final String gitUploadPack = "/git-upload-pack";\r
+\r
+ protected final String[] suffixes = { gitReceivePack, gitUploadPack, "/info/refs", "/HEAD",\r
+ "/objects" };\r
+\r
+ @Override\r
+ protected String extractRepositoryName(String url) {\r
+ String repository = url;\r
+ for (String urlSuffix : suffixes) {\r
+ if (repository.indexOf(urlSuffix) > -1) {\r
+ repository = repository.substring(0, repository.indexOf(urlSuffix));\r
+ }\r
+ }\r
+ return repository;\r
+ }\r
+\r
+ @Override\r
+ protected String getUrlRequestType(String suffix) {\r
+ if (!StringUtils.isEmpty(suffix)) {\r
+ if (suffix.startsWith(gitReceivePack)) {\r
+ return gitReceivePack;\r
+ } else if (suffix.startsWith(gitUploadPack)) {\r
+ return gitUploadPack;\r
+ } else if (suffix.contains("?service=git-receive-pack")) {\r
+ return gitReceivePack;\r
+ } else if (suffix.contains("?service=git-upload-pack")) {\r
+ return gitUploadPack;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ protected boolean requiresAuthentication(RepositoryModel repository) {\r
+ return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);\r
+ }\r
+\r
+ @Override\r
+ protected boolean canAccess(RepositoryModel repository, UserModel user, String urlRequestType) {\r
+ if (repository.isFrozen || repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {\r
+ boolean authorizedUser = user.canAccessRepository(repository.name);\r
+ if (urlRequestType.equals(gitReceivePack)) {\r
+ // Push request\r
+ if (!repository.isFrozen && authorizedUser) {\r
+ // clone-restricted or push-authorized\r
+ return true;\r
+ } else {\r
+ // user is unauthorized to push to this repository\r
+ logger.warn(MessageFormat.format("user {0} is not authorized to push to {1}",\r
+ user.username, repository));\r
+ return false;\r
+ }\r
+ } else if (urlRequestType.equals(gitUploadPack)) {\r
+ // Clone request\r
+ boolean cloneRestricted = repository.accessRestriction\r
+ .atLeast(AccessRestrictionType.CLONE);\r
+ if (!cloneRestricted || (cloneRestricted && authorizedUser)) {\r
+ // push-restricted or clone-authorized\r
+ return true;\r
+ } else {\r
+ // user is unauthorized to clone this repository\r
+ logger.warn(MessageFormat.format("user {0} is not authorized to clone {1}",\r
+ user.username, repository));\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+}\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.io.FileReader;\r
-import java.io.FileWriter;\r
-import java.io.IOException;\r
-import java.security.Principal;\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Properties;\r
-import java.util.Set;\r
-\r
-import javax.security.auth.Subject;\r
-\r
-import org.eclipse.jetty.http.security.Credential;\r
-import org.eclipse.jetty.security.IdentityService;\r
-import org.eclipse.jetty.security.MappedLoginService;\r
-import org.eclipse.jetty.server.UserIdentity;\r
-import org.eclipse.jetty.util.log.Log;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.models.UserModel;\r
-\r
-public class JettyLoginService extends MappedLoginService implements ILoginService {\r
-\r
- private final Logger logger = LoggerFactory.getLogger(JettyLoginService.class);\r
-\r
- private final File realmFile;\r
-\r
- public JettyLoginService(File realmFile) {\r
- super();\r
- setName(Constants.NAME);\r
- this.realmFile = realmFile;\r
- }\r
-\r
- @Override\r
- public UserModel authenticate(String username, char[] password) {\r
- UserIdentity identity = login(username, new String(password));\r
- if (identity == null || identity.equals(UserIdentity.UNAUTHENTICATED_IDENTITY)) {\r
- return null;\r
- }\r
- UserModel user = new UserModel(username);\r
- user.canAdmin = identity.isUserInRole(Constants.ADMIN_ROLE, null);\r
-\r
- // Add repositories\r
- for (Principal principal : identity.getSubject().getPrincipals()) {\r
- if (principal instanceof RolePrincipal) {\r
- RolePrincipal role = (RolePrincipal) principal;\r
- String roleName = role.getName();\r
- if (roleName.charAt(0) != '#') {\r
- user.addRepository(roleName);\r
- }\r
- }\r
- }\r
- return user;\r
- }\r
-\r
- @Override\r
- public UserModel getUserModel(String username) {\r
- UserIdentity identity = _users.get(username);\r
- if (identity == null) {\r
- return null;\r
- }\r
- UserModel model = new UserModel(username);\r
- Subject subject = identity.getSubject();\r
- for (Principal principal : subject.getPrincipals()) {\r
- if (principal instanceof RolePrincipal) {\r
- RolePrincipal role = (RolePrincipal) principal;\r
- String name = role.getName();\r
- switch (name.charAt(0)) {\r
- case '#':\r
- // Permissions\r
- if (name.equalsIgnoreCase(Constants.ADMIN_ROLE)) {\r
- model.canAdmin = true;\r
- }\r
- break;\r
- default:\r
- model.addRepository(name);\r
- }\r
- }\r
- }\r
- // Retrieve the password from the realm file.\r
- // Stupid, I know, but the password is buried within protected inner\r
- // classes in private variables. Too much work to reflectively retrieve.\r
- try {\r
- Properties allUsers = readRealmFile();\r
- String value = allUsers.getProperty(username);\r
- String password = value.split(",")[0];\r
- model.password = password;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to read password for user {0}!", username), t);\r
- }\r
- return model;\r
- }\r
-\r
- @Override\r
- public boolean updateUserModel(UserModel model) {\r
- return updateUserModel(model.username, model);\r
- }\r
-\r
- @Override\r
- public boolean updateUserModel(String username, UserModel model) {\r
- try {\r
- Properties allUsers = readRealmFile();\r
- ArrayList<String> roles = new ArrayList<String>(model.repositories);\r
-\r
- // Permissions\r
- if (model.canAdmin) {\r
- roles.add(Constants.ADMIN_ROLE);\r
- }\r
-\r
- StringBuilder sb = new StringBuilder();\r
- sb.append(model.password);\r
- sb.append(',');\r
- for (String role : roles) {\r
- sb.append(role);\r
- sb.append(',');\r
- }\r
- // trim trailing comma\r
- sb.setLength(sb.length() - 1);\r
- allUsers.remove(username);\r
- allUsers.put(model.username, sb.toString());\r
-\r
- writeRealmFile(allUsers);\r
-\r
- // Update login service\r
- removeUser(username);\r
- putUser(model.username, Credential.getCredential(model.password),\r
- roles.toArray(new String[0]));\r
- return true;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),\r
- t);\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean deleteUserModel(UserModel model) {\r
- return deleteUser(model.username);\r
- }\r
-\r
- @Override\r
- public boolean deleteUser(String username) {\r
- try {\r
- // Read realm file\r
- Properties allUsers = readRealmFile();\r
- allUsers.remove(username);\r
- writeRealmFile(allUsers);\r
-\r
- // Drop user from map\r
- removeUser(username);\r
- return true;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to delete user {0}!", username), t);\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public List<String> getAllUsernames() {\r
- List<String> list = new ArrayList<String>();\r
- list.addAll(_users.keySet());\r
- return list;\r
- }\r
-\r
- @Override\r
- public List<String> getUsernamesForRole(String role) {\r
- List<String> list = new ArrayList<String>();\r
- try {\r
- Properties allUsers = readRealmFile();\r
- for (String username : allUsers.stringPropertyNames()) {\r
- String value = allUsers.getProperty(username);\r
- String[] values = value.split(",");\r
- // skip first value (password)\r
- for (int i = 1; i < values.length; i++) {\r
- String r = values[i];\r
- if (r.equalsIgnoreCase(role)) {\r
- list.add(username);\r
- break;\r
- }\r
- }\r
- }\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t);\r
- }\r
- return list;\r
- }\r
-\r
- @Override\r
- public boolean setUsernamesForRole(String role, List<String> usernames) {\r
- try {\r
- Set<String> specifiedUsers = new HashSet<String>(usernames);\r
- Set<String> needsAddRole = new HashSet<String>(specifiedUsers);\r
- Set<String> needsRemoveRole = new HashSet<String>();\r
-\r
- // identify users which require add and remove role\r
- Properties allUsers = readRealmFile();\r
- for (String username : allUsers.stringPropertyNames()) {\r
- String value = allUsers.getProperty(username);\r
- String[] values = value.split(",");\r
- // skip first value (password)\r
- for (int i = 1; i < values.length; i++) {\r
- String r = values[i];\r
- if (r.equalsIgnoreCase(role)) {\r
- // user has role, check against revised user list\r
- if (specifiedUsers.contains(username)) {\r
- needsAddRole.remove(username);\r
- } else {\r
- // remove role from user\r
- needsRemoveRole.add(username);\r
- }\r
- break;\r
- }\r
- }\r
- }\r
-\r
- // add roles to users\r
- for (String user : needsAddRole) {\r
- String userValues = allUsers.getProperty(user);\r
- userValues += "," + role;\r
- allUsers.put(user, userValues);\r
- String[] values = userValues.split(",");\r
- String password = values[0];\r
- String[] roles = new String[values.length - 1];\r
- System.arraycopy(values, 1, roles, 0, values.length - 1);\r
- putUser(user, Credential.getCredential(password), roles);\r
- }\r
-\r
- // remove role from user\r
- for (String user : needsRemoveRole) {\r
- String[] values = allUsers.getProperty(user).split(",");\r
- String password = values[0];\r
- StringBuilder sb = new StringBuilder();\r
- sb.append(password);\r
- sb.append(',');\r
- List<String> revisedRoles = new ArrayList<String>();\r
- // skip first value (password)\r
- for (int i = 1; i < values.length; i++) {\r
- String value = values[i];\r
- if (!value.equalsIgnoreCase(role)) {\r
- revisedRoles.add(value);\r
- sb.append(value);\r
- sb.append(',');\r
- }\r
- }\r
- sb.setLength(sb.length() - 1);\r
-\r
- // update properties\r
- allUsers.put(user, sb.toString());\r
-\r
- // update memory\r
- putUser(user, Credential.getCredential(password),\r
- revisedRoles.toArray(new String[0]));\r
- }\r
-\r
- // persist changes\r
- writeRealmFile(allUsers);\r
- return true;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t);\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean renameRole(String oldRole, String newRole) {\r
- try {\r
- Properties allUsers = readRealmFile();\r
- Set<String> needsRenameRole = new HashSet<String>();\r
-\r
- // identify users which require role rename\r
- for (String username : allUsers.stringPropertyNames()) {\r
- String value = allUsers.getProperty(username);\r
- String[] roles = value.split(",");\r
- // skip first value (password)\r
- for (int i = 1; i < roles.length; i++) {\r
- String r = roles[i];\r
- if (r.equalsIgnoreCase(oldRole)) {\r
- needsRenameRole.remove(username);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- // rename role for identified users\r
- for (String user : needsRenameRole) {\r
- String userValues = allUsers.getProperty(user);\r
- String[] values = userValues.split(",");\r
- String password = values[0];\r
- StringBuilder sb = new StringBuilder();\r
- sb.append(password);\r
- sb.append(',');\r
- List<String> revisedRoles = new ArrayList<String>();\r
- revisedRoles.add(newRole);\r
- // skip first value (password)\r
- for (int i = 1; i < values.length; i++) {\r
- String value = values[i];\r
- if (!value.equalsIgnoreCase(oldRole)) {\r
- revisedRoles.add(value);\r
- sb.append(value);\r
- sb.append(',');\r
- }\r
- }\r
- sb.setLength(sb.length() - 1);\r
-\r
- // update properties\r
- allUsers.put(user, sb.toString());\r
-\r
- // update memory\r
- putUser(user, Credential.getCredential(password),\r
- revisedRoles.toArray(new String[0]));\r
- }\r
-\r
- // persist changes\r
- writeRealmFile(allUsers);\r
- return true;\r
- } catch (Throwable t) {\r
- logger.error(\r
- MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean deleteRole(String role) {\r
- try {\r
- Properties allUsers = readRealmFile();\r
- Set<String> needsDeleteRole = new HashSet<String>();\r
-\r
- // identify users which require role rename\r
- for (String username : allUsers.stringPropertyNames()) {\r
- String value = allUsers.getProperty(username);\r
- String[] roles = value.split(",");\r
- // skip first value (password)\r
- for (int i = 1; i < roles.length; i++) {\r
- String r = roles[i];\r
- if (r.equalsIgnoreCase(role)) {\r
- needsDeleteRole.remove(username);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- // delete role for identified users\r
- for (String user : needsDeleteRole) {\r
- String userValues = allUsers.getProperty(user);\r
- String[] values = userValues.split(",");\r
- String password = values[0];\r
- StringBuilder sb = new StringBuilder();\r
- sb.append(password);\r
- sb.append(',');\r
- List<String> revisedRoles = new ArrayList<String>();\r
- // skip first value (password)\r
- for (int i = 1; i < values.length; i++) {\r
- String value = values[i];\r
- if (!value.equalsIgnoreCase(role)) {\r
- revisedRoles.add(value);\r
- sb.append(value);\r
- sb.append(',');\r
- }\r
- }\r
- sb.setLength(sb.length() - 1);\r
-\r
- // update properties\r
- allUsers.put(user, sb.toString());\r
-\r
- // update memory\r
- putUser(user, Credential.getCredential(password),\r
- revisedRoles.toArray(new String[0]));\r
- }\r
-\r
- // persist changes\r
- writeRealmFile(allUsers);\r
- return true;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to delete role {0}!", role), t);\r
- }\r
- return false;\r
- }\r
-\r
- private Properties readRealmFile() throws IOException {\r
- Properties allUsers = new Properties();\r
- FileReader reader = new FileReader(realmFile);\r
- allUsers.load(reader);\r
- reader.close();\r
- return allUsers;\r
- }\r
-\r
- private void writeRealmFile(Properties properties) throws IOException {\r
- // Update realm file\r
- File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");\r
- FileWriter writer = new FileWriter(realmFileCopy);\r
- properties\r
- .store(writer,\r
- "# Gitblit realm file format: username=password,\\#permission,repository1,repository2...");\r
- writer.close();\r
- if (realmFileCopy.exists() && realmFileCopy.length() > 0) {\r
- if (realmFile.delete()) {\r
- if (!realmFileCopy.renameTo(realmFile)) {\r
- throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",\r
- realmFileCopy.getAbsolutePath(), realmFile.getAbsolutePath()));\r
- }\r
- } else {\r
- throw new IOException(MessageFormat.format("Failed to delete (0)!",\r
- realmFile.getAbsolutePath()));\r
- }\r
- } else {\r
- throw new IOException(MessageFormat.format("Failed to save {0}!",\r
- realmFileCopy.getAbsolutePath()));\r
- }\r
- }\r
-\r
- /* ------------------------------------------------------------ */\r
- @Override\r
- public void loadUsers() throws IOException {\r
- if (realmFile == null) {\r
- return;\r
- }\r
-\r
- if (Log.isDebugEnabled()) {\r
- Log.debug("Load " + this + " from " + realmFile);\r
- }\r
- Properties allUsers = readRealmFile();\r
-\r
- // Map Users\r
- for (Map.Entry<Object, Object> entry : allUsers.entrySet()) {\r
- String username = ((String) entry.getKey()).trim();\r
- String credentials = ((String) entry.getValue()).trim();\r
- String roles = null;\r
- int c = credentials.indexOf(',');\r
- if (c > 0) {\r
- roles = credentials.substring(c + 1).trim();\r
- credentials = credentials.substring(0, c).trim();\r
- }\r
-\r
- if (username != null && username.length() > 0 && credentials != null\r
- && credentials.length() > 0) {\r
- String[] roleArray = IdentityService.NO_ROLES;\r
- if (roles != null && roles.length() > 0) {\r
- roleArray = roles.split(",");\r
- }\r
- putUser(username, Credential.getCredential(credentials), roleArray);\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- protected UserIdentity loadUser(String username) {\r
- return null;\r
- }\r
-}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+import java.security.Principal;\r
+import java.util.Enumeration;\r
+import java.util.Locale;\r
+import java.util.Map;\r
+\r
+import javax.servlet.RequestDispatcher;\r
+import javax.servlet.ServletInputStream;\r
+import javax.servlet.http.Cookie;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpSession;\r
+\r
+public abstract class ServletRequestWrapper implements HttpServletRequest {\r
+\r
+ protected final HttpServletRequest req;\r
+\r
+ public ServletRequestWrapper(HttpServletRequest req) {\r
+ this.req = req;\r
+ }\r
+\r
+ @Override\r
+ public Object getAttribute(String name) {\r
+ return req.getAttribute(name);\r
+ }\r
+\r
+ @Override\r
+ public Enumeration getAttributeNames() {\r
+ return req.getAttributeNames();\r
+ }\r
+\r
+ @Override\r
+ public String getCharacterEncoding() {\r
+ return req.getCharacterEncoding();\r
+ }\r
+\r
+ @Override\r
+ public void setCharacterEncoding(String env) throws UnsupportedEncodingException {\r
+ req.setCharacterEncoding(env);\r
+ }\r
+\r
+ @Override\r
+ public int getContentLength() {\r
+ return req.getContentLength();\r
+ }\r
+\r
+ @Override\r
+ public String getContentType() {\r
+ return req.getContentType();\r
+ }\r
+\r
+ @Override\r
+ public ServletInputStream getInputStream() throws IOException {\r
+ return req.getInputStream();\r
+ }\r
+\r
+ @Override\r
+ public String getParameter(String name) {\r
+ return req.getParameter(name);\r
+ }\r
+\r
+ @Override\r
+ public Enumeration getParameterNames() {\r
+ return req.getParameterNames();\r
+ }\r
+\r
+ @Override\r
+ public String[] getParameterValues(String name) {\r
+ return req.getParameterValues(name);\r
+ }\r
+\r
+ @Override\r
+ public Map getParameterMap() {\r
+ return req.getParameterMap();\r
+ }\r
+\r
+ @Override\r
+ public String getProtocol() {\r
+ return req.getProtocol();\r
+ }\r
+\r
+ @Override\r
+ public String getScheme() {\r
+ return req.getScheme();\r
+ }\r
+\r
+ @Override\r
+ public String getServerName() {\r
+ return req.getServerName();\r
+ }\r
+\r
+ @Override\r
+ public int getServerPort() {\r
+ return req.getServerPort();\r
+ }\r
+\r
+ @Override\r
+ public BufferedReader getReader() throws IOException {\r
+ return req.getReader();\r
+ }\r
+\r
+ @Override\r
+ public String getRemoteAddr() {\r
+ return req.getRemoteAddr();\r
+ }\r
+\r
+ @Override\r
+ public String getRemoteHost() {\r
+ return req.getRemoteHost();\r
+ }\r
+\r
+ @Override\r
+ public void setAttribute(String name, Object o) {\r
+ req.setAttribute(name, o);\r
+ }\r
+\r
+ @Override\r
+ public void removeAttribute(String name) {\r
+ req.removeAttribute(name);\r
+ }\r
+\r
+ @Override\r
+ public Locale getLocale() {\r
+ return req.getLocale();\r
+ }\r
+\r
+ @Override\r
+ public Enumeration getLocales() {\r
+ return req.getLocales();\r
+ }\r
+\r
+ @Override\r
+ public boolean isSecure() {\r
+ return req.isSecure();\r
+ }\r
+\r
+ @Override\r
+ public RequestDispatcher getRequestDispatcher(String path) {\r
+ return req.getRequestDispatcher(path);\r
+ }\r
+\r
+ @Override\r
+ @Deprecated\r
+ public String getRealPath(String path) {\r
+ return req.getRealPath(path);\r
+ }\r
+\r
+ @Override\r
+ public int getRemotePort() {\r
+ return req.getRemotePort();\r
+ }\r
+\r
+ @Override\r
+ public String getLocalName() {\r
+ return req.getLocalName();\r
+ }\r
+\r
+ @Override\r
+ public String getLocalAddr() {\r
+ return req.getLocalAddr();\r
+ }\r
+\r
+ @Override\r
+ public int getLocalPort() {\r
+ return req.getLocalPort();\r
+ }\r
+\r
+ @Override\r
+ public String getAuthType() {\r
+ return req.getAuthType();\r
+ }\r
+\r
+ @Override\r
+ public Cookie[] getCookies() {\r
+ return req.getCookies();\r
+ }\r
+\r
+ @Override\r
+ public long getDateHeader(String name) {\r
+ return req.getDateHeader(name);\r
+ }\r
+\r
+ @Override\r
+ public String getHeader(String name) {\r
+ return req.getHeader(name);\r
+ }\r
+\r
+ @Override\r
+ public Enumeration getHeaders(String name) {\r
+ return req.getHeaders(name);\r
+ }\r
+\r
+ @Override\r
+ public Enumeration getHeaderNames() {\r
+ return req.getHeaderNames();\r
+ }\r
+\r
+ @Override\r
+ public int getIntHeader(String name) {\r
+ return req.getIntHeader(name);\r
+ }\r
+\r
+ @Override\r
+ public String getMethod() {\r
+ return req.getMethod();\r
+ }\r
+\r
+ @Override\r
+ public String getPathInfo() {\r
+ return req.getPathInfo();\r
+ }\r
+\r
+ @Override\r
+ public String getPathTranslated() {\r
+ return req.getPathTranslated();\r
+ }\r
+\r
+ @Override\r
+ public String getContextPath() {\r
+ return req.getContextPath();\r
+ }\r
+\r
+ @Override\r
+ public String getQueryString() {\r
+ return req.getQueryString();\r
+ }\r
+\r
+ @Override\r
+ public String getRemoteUser() {\r
+ return req.getRemoteUser();\r
+ }\r
+\r
+ @Override\r
+ public boolean isUserInRole(String role) {\r
+ return req.isUserInRole(role);\r
+ }\r
+\r
+ @Override\r
+ public Principal getUserPrincipal() {\r
+ return req.getUserPrincipal();\r
+ }\r
+\r
+ @Override\r
+ public String getRequestedSessionId() {\r
+ return req.getRequestedSessionId();\r
+ }\r
+\r
+ @Override\r
+ public String getRequestURI() {\r
+ return req.getRequestURI();\r
+ }\r
+\r
+ @Override\r
+ public StringBuffer getRequestURL() {\r
+ return req.getRequestURL();\r
+ }\r
+\r
+ @Override\r
+ public String getServletPath() {\r
+ return req.getServletPath();\r
+ }\r
+\r
+ @Override\r
+ public HttpSession getSession(boolean create) {\r
+ return req.getSession(create);\r
+ }\r
+\r
+ @Override\r
+ public HttpSession getSession() {\r
+ return req.getSession();\r
+ }\r
+\r
+ @Override\r
+ public boolean isRequestedSessionIdValid() {\r
+ return req.isRequestedSessionIdValid();\r
+ }\r
+\r
+ @Override\r
+ public boolean isRequestedSessionIdFromCookie() {\r
+ return req.isRequestedSessionIdFromCookie();\r
+ }\r
+\r
+ @Override\r
+ public boolean isRequestedSessionIdFromURL() {\r
+ return req.isRequestedSessionIdFromURL();\r
+ }\r
+\r
+ @Override\r
+ @Deprecated\r
+ public boolean isRequestedSessionIdFromUrl() {\r
+ return req.isRequestedSessionIdFromUrl();\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+\r
+public class SyndicationFilter extends AccessRestrictionFilter {\r
+\r
+ @Override\r
+ protected String extractRepositoryName(String url) {\r
+ return url;\r
+ }\r
+\r
+ @Override\r
+ protected String getUrlRequestType(String url) {\r
+ return "RESTRICTED";\r
+ }\r
+\r
+ @Override\r
+ protected boolean requiresAuthentication(RepositoryModel repository) {\r
+ return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);\r
+ }\r
+\r
+ @Override\r
+ protected boolean canAccess(RepositoryModel repository, UserModel user, String restrictedURL) {\r
+ return user.canAccessRepository(repository.name);\r
+ }\r
+\r
+}\r
*/\r
package com.gitblit;\r
\r
+import java.text.MessageFormat;\r
import java.util.List;\r
\r
import javax.servlet.http.HttpServlet;\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.utils.SyndicationUtils;\r
+import com.gitblit.wicket.WicketUtils;\r
\r
public class SyndicationServlet extends HttpServlet {\r
\r
private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class);\r
\r
public static String asLink(String baseURL, String repository, String objectId, int length) {\r
- if (baseURL.charAt(baseURL.length() - 1) == '/') {\r
+ if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
baseURL = baseURL.substring(0, baseURL.length() - 1);\r
}\r
- return baseURL + Constants.SYNDICATION_SERVLET_PATH + "?r=" + repository\r
- + (objectId == null ? "" : ("&h=" + objectId)) + (length > 0 ? "&l=" + length : "");\r
+ StringBuilder url = new StringBuilder();\r
+ url.append(baseURL);\r
+ url.append(Constants.SYNDICATION_SERVLET_PATH);\r
+ url.append(repository);\r
+ if (!StringUtils.isEmpty(objectId) || length > 0) {\r
+ StringBuilder parameters = new StringBuilder("?");\r
+ if (StringUtils.isEmpty(objectId)) {\r
+ parameters.append("l=");\r
+ parameters.append(length);\r
+ } else {\r
+ parameters.append("h=");\r
+ parameters.append(objectId);\r
+ if (length > 0) {\r
+ parameters.append("&l=");\r
+ parameters.append(length);\r
+ }\r
+ }\r
+ url.append(parameters);\r
+ }\r
+ return url.toString();\r
+ }\r
+ \r
+ public static String getTitle(String repository, String objectId) {\r
+ String id = objectId;\r
+ if (!StringUtils.isEmpty(id)) {\r
+ if (id.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) {\r
+ id = id.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length());\r
+ } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_REMOTES)) {\r
+ id = id.substring(org.eclipse.jgit.lib.Constants.R_REMOTES.length());\r
+ } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) {\r
+ id = id.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length());\r
+ }\r
+ }\r
+ return MessageFormat.format("{0} ({1})", repository, id);\r
}\r
\r
private void processRequest(javax.servlet.http.HttpServletRequest request,\r
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
java.io.IOException {\r
- String hostUrl = request.getRequestURL().toString();\r
- String servlet = request.getServletPath();\r
- hostUrl = hostUrl.substring(0, hostUrl.indexOf(servlet));\r
- String repositoryName = request.getParameter("r");\r
+\r
+ String hostURL = WicketUtils.getHostURL(request);\r
+ String url = request.getRequestURI().substring(request.getServletPath().length());\r
+ if (url.charAt(0) == '/' && url.length() > 1) {\r
+ url = url.substring(1);\r
+ }\r
+ String repositoryName = url;\r
String objectId = request.getParameter("h");\r
String l = request.getParameter("l");\r
int length = GitBlit.getInteger(Keys.web.syndicationEntries, 25);\r
} catch (NumberFormatException x) {\r
}\r
}\r
- \r
- // TODO confirm repository is accessible!!\r
\r
Repository repository = GitBlit.self().getRepository(repositoryName);\r
RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);\r
List<RevCommit> commits = JGitUtils.getRevLog(repository, objectId, 0, length);\r
try {\r
- SyndicationUtils.toRSS(hostUrl, model.name + " " + objectId, model.description, model.name, commits, response.getOutputStream());\r
+ SyndicationUtils.toRSS(hostURL, getTitle(model.name, objectId), model.description,\r
+ model.name, commits, response.getOutputStream());\r
} catch (Exception e) {\r
logger.error("An error occurred during feed generation", e);\r
}\r
package com.gitblit.models;\r
\r
import java.io.Serializable;\r
+import java.security.Principal;\r
import java.util.ArrayList;\r
import java.util.List;\r
\r
-public class UserModel implements Serializable {\r
+public class UserModel implements Principal, Serializable {\r
\r
private static final long serialVersionUID = 1L;\r
\r
repositories.add(name.toLowerCase());\r
}\r
\r
+ @Override\r
+ public String getName() { \r
+ return username;\r
+ }\r
+\r
@Override\r
public String toString() {\r
return username;\r
package com.gitblit.utils;\r
\r
import java.io.UnsupportedEncodingException;\r
+import java.nio.charset.Charset;\r
import java.security.MessageDigest;\r
import java.security.NoSuchAlgorithmException;\r
import java.util.ArrayList;\r
import java.util.List;\r
import java.util.regex.PatternSyntaxException;\r
\r
+import org.eclipse.jetty.util.log.Log;\r
+import org.eclipse.jgit.util.Base64;\r
+\r
public class StringUtils {\r
\r
+ public static final String MD5_TYPE = "MD5:";\r
+\r
public static boolean isEmpty(String value) {\r
return value == null || value.trim().length() == 0;\r
}\r
return retStr.toString();\r
}\r
\r
+ public static String encodeURL(String inStr) {\r
+ StringBuffer retStr = new StringBuffer();\r
+ int i = 0;\r
+ while (i < inStr.length()) {\r
+ if (inStr.charAt(i) == '/') {\r
+ retStr.append("%2F");\r
+ } else if (inStr.charAt(i) == ' ') {\r
+ retStr.append("%20");\r
+ } else {\r
+ retStr.append(inStr.charAt(i));\r
+ }\r
+ i++;\r
+ }\r
+ return retStr.toString();\r
+ }\r
+\r
public static String flattenStrings(List<String> values) {\r
return flattenStrings(values, " ");\r
}\r
try {\r
MessageDigest md = MessageDigest.getInstance("SHA-1");\r
md.update(bytes, 0, bytes.length);\r
- byte[] sha1hash = md.digest();\r
- StringBuilder sb = new StringBuilder(sha1hash.length * 2);\r
- for (int i = 0; i < sha1hash.length; i++) {\r
- if (((int) sha1hash[i] & 0xff) < 0x10) {\r
- sb.append('0');\r
- }\r
- sb.append(Long.toString((int) sha1hash[i] & 0xff, 16));\r
- }\r
- return sb.toString();\r
+ byte[] digest = md.digest();\r
+ return toHex(digest);\r
} catch (NoSuchAlgorithmException t) {\r
throw new RuntimeException(t);\r
}\r
}\r
\r
+ public static String getMD5(String string) {\r
+ try {\r
+ MessageDigest md = MessageDigest.getInstance("MD5");\r
+ md.reset();\r
+ md.update(string.getBytes("iso-8859-1"));\r
+ byte[] digest = md.digest();\r
+ return toHex(digest);\r
+ } catch (Exception e) {\r
+ Log.warn(e);\r
+ return null;\r
+ }\r
+ }\r
+\r
+ private static String toHex(byte[] bytes) {\r
+ StringBuilder sb = new StringBuilder(bytes.length * 2);\r
+ for (int i = 0; i < bytes.length; i++) {\r
+ if (((int) bytes[i] & 0xff) < 0x10) {\r
+ sb.append('0');\r
+ }\r
+ sb.append(Long.toString((int) bytes[i] & 0xff, 16));\r
+ }\r
+ return sb.toString();\r
+ }\r
+\r
+ public static String decodeBase64(String base64) {\r
+ return new String(Base64.decode(base64), Charset.forName("UTF-8"));\r
+ }\r
+\r
public static String getRootPath(String path) {\r
if (path.indexOf('/') > -1) {\r
return path.substring(0, path.lastIndexOf('/'));\r
}\r
return relativePath;\r
}\r
- \r
+\r
public static List<String> getStringsFromValue(String value) {\r
return getStringsFromValue(value, " ");\r
}\r
- \r
+\r
public static List<String> getStringsFromValue(String value, String separator) {\r
List<String> strings = new ArrayList<String>();\r
try {\r
\r
import org.eclipse.jgit.revwalk.RevCommit;\r
\r
+import com.gitblit.Constants;\r
import com.sun.syndication.feed.synd.SyndContent;\r
import com.sun.syndication.feed.synd.SyndContentImpl;\r
import com.sun.syndication.feed.synd.SyndEntry;\r
import com.sun.syndication.feed.synd.SyndEntryImpl;\r
import com.sun.syndication.feed.synd.SyndFeed;\r
import com.sun.syndication.feed.synd.SyndFeedImpl;\r
+import com.sun.syndication.feed.synd.SyndImageImpl;\r
import com.sun.syndication.io.FeedException;\r
import com.sun.syndication.io.SyndFeedOutput;\r
\r
public class SyndicationUtils {\r
\r
- public static void toRSS(String hostUrl, String title, String description, String repository, List<RevCommit> commits, OutputStream os)\r
- throws IOException, FeedException {\r
+ public static void toRSS(String hostUrl, String title, String description, String repository,\r
+ List<RevCommit> commits, OutputStream os) throws IOException, FeedException {\r
\r
SyndFeed feed = new SyndFeedImpl();\r
- feed.setFeedType("rss_1.0");\r
+ feed.setFeedType("rss_2.0");\r
feed.setTitle(title);\r
- feed.setLink(MessageFormat.format("{0}/summary/{1}", hostUrl, repository));\r
+ feed.setLink(MessageFormat.format("{0}/summary/{1}", hostUrl,\r
+ StringUtils.encodeURL(repository)));\r
feed.setDescription(description);\r
+ SyndImageImpl image = new SyndImageImpl();\r
+ image.setTitle(Constants.NAME);\r
+ image.setUrl(hostUrl + Constants.RESOURCE_PATH + "gitblt_25.png");\r
+ image.setLink(hostUrl);\r
+ feed.setImage(image);\r
\r
List<SyndEntry> entries = new ArrayList<SyndEntry>();\r
for (RevCommit commit : commits) {\r
SyndEntry entry = new SyndEntryImpl();\r
entry.setTitle(commit.getShortMessage());\r
entry.setAuthor(commit.getAuthorIdent().getName());\r
- entry.setLink(MessageFormat.format("{0}/commit/{1}/{2}", hostUrl, repository, commit.getName()));\r
+ entry.setLink(MessageFormat.format("{0}/commit/{1}/{2}", hostUrl,\r
+ StringUtils.encodeURL(repository), commit.getName()));\r
entry.setPublishedDate(commit.getCommitterIdent().getWhen());\r
\r
SyndContent content = new SyndContentImpl();\r
\r
@Override\r
public final String getConfigurationType() {\r
- if (GitBlit.self().isDebugMode()) {\r
+ if (GitBlit.isDebugMode()) {\r
return Application.DEVELOPMENT;\r
}\r
return Application.DEPLOYMENT;\r
import java.util.List;\r
import java.util.TimeZone;\r
\r
+import javax.servlet.http.HttpServletRequest;\r
+\r
import org.apache.wicket.Component;\r
import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.Request;\r
+import org.apache.wicket.behavior.HeaderContributor;\r
import org.apache.wicket.behavior.SimpleAttributeModifier;\r
+import org.apache.wicket.markup.html.IHeaderContributor;\r
+import org.apache.wicket.markup.html.IHeaderResponse;\r
import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.image.ContextImage;\r
+import org.apache.wicket.protocol.http.WebRequest;\r
import org.apache.wicket.resource.ContextRelativeResource;\r
import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
import org.eclipse.jgit.lib.Constants;\r
}\r
\r
public static ContextImage newImage(String wicketId, String file, String tooltip) {\r
- ContextImage img = new ContextImage(wicketId, "/com/gitblit/wicket/resources/" + file);\r
+ ContextImage img = new ContextImage(wicketId, com.gitblit.Constants.RESOURCE_PATH + file);\r
if (!StringUtils.isEmpty(tooltip)) {\r
setHtmlTooltip(img, tooltip);\r
}\r
}\r
\r
public static ContextRelativeResource getResource(String file) {\r
- return new ContextRelativeResource("/com/gitblit/wicket/resources/" + file);\r
+ return new ContextRelativeResource(com.gitblit.Constants.RESOURCE_PATH + file);\r
+ }\r
+\r
+ public static String getHostURL(Request request) {\r
+ HttpServletRequest req = ((WebRequest) request).getHttpServletRequest();\r
+ return getHostURL(req);\r
+ }\r
+\r
+ public static String getHostURL(HttpServletRequest request) {\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append(request.getScheme());\r
+ sb.append("://");\r
+ sb.append(request.getServerName());\r
+ if ((request.getScheme().equals("http") && request.getServerPort() != 80)\r
+ || (request.getScheme().equals("https") && request.getServerPort() != 443)) {\r
+ sb.append(":" + request.getServerPort());\r
+ }\r
+ return sb.toString();\r
+ }\r
+\r
+ public static HeaderContributor syndicationDiscoveryLink(final String feedTitle,\r
+ final String url) {\r
+ return new HeaderContributor(new IHeaderContributor() {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public void renderHead(IHeaderResponse response) {\r
+ String contentType = "application/rss+xml";\r
+\r
+ StringBuffer buffer = new StringBuffer();\r
+ buffer.append("<link rel=\"alternate\" ");\r
+ buffer.append("type=\"").append(contentType).append("\" ");\r
+ buffer.append("title=\"").append(feedTitle).append("\" ");\r
+ buffer.append("href=\"").append(url).append("\" />");\r
+ response.renderString(buffer.toString());\r
+ }\r
+ });\r
}\r
\r
public static PageParameters newUsernameParameter(String username) {\r
SearchType.AUTHOR));\r
item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef\r
.getAuthorIdent().getWhen(), getTimeZone()));\r
- item.add(new Label("noteContent", substituteText(entry.content))\r
+ item.add(new Label("noteContent", GitBlit.self().processCommitMessage(repositoryName, entry.content))\r
.setEscapeModelStrings(false));\r
}\r
};\r
import org.apache.wicket.model.Model;\r
import org.apache.wicket.model.util.CollectionModel;\r
import org.apache.wicket.model.util.ListModel;\r
-import org.eclipse.jetty.http.security.Credential.Crypt;\r
-import org.eclipse.jetty.http.security.Credential.MD5;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.GitBlit;\r
return;\r
}\r
String password = userModel.password;\r
- if (!password.toUpperCase().startsWith(Crypt.__TYPE)\r
- && !password.toUpperCase().startsWith(MD5.__TYPE)) {\r
+ if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE)) {\r
// This is a plain text password.\r
// Check length.\r
int minLength = GitBlit.getInteger(Keys.realm.minPasswordLength, 5);\r
String type = GitBlit.getString(Keys.realm.passwordStorage, "md5");\r
if (type.equalsIgnoreCase("md5")) {\r
// store MD5 digest of password\r
- userModel.password = MD5.digest(userModel.password);\r
+ userModel.password = StringUtils.MD5_TYPE + StringUtils.getMD5(userModel.password);\r
}\r
}\r
\r
public LogPage(PageParameters params) {\r
super(params);\r
\r
+ addSyndicationDiscoveryLink();\r
+ \r
int pageNumber = WicketUtils.getPage(params);\r
int prevPage = Math.max(0, pageNumber - 1);\r
int nextPage = pageNumber + 1;\r
import java.util.HashMap;\r
import java.util.List;\r
import java.util.Map;\r
-import java.util.Map.Entry;\r
\r
import org.apache.wicket.Component;\r
import org.apache.wicket.PageParameters;\r
}\r
};\r
add(extrasView);\r
- \r
+\r
add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()\r
.getRelativePathPrefixToContextRoot(), repositoryName, null, 0)));\r
\r
}\r
}\r
\r
+ protected void addSyndicationDiscoveryLink() {\r
+ add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(repositoryName,\r
+ objectId), SyndicationServlet.asLink(getRequest()\r
+ .getRelativePathPrefixToContextRoot(), repositoryName, objectId, 0)));\r
+ }\r
+\r
protected Repository getRepository() {\r
if (r == null) {\r
Repository r = GitBlit.self().getRepository(repositoryName);\r
protected void addFullText(String wicketId, String text, boolean substituteRegex) {\r
String html;\r
if (substituteRegex) {\r
- html = substituteText(text);\r
+ html = GitBlit.self().processCommitMessage(repositoryName, text);\r
} else {\r
html = StringUtils.breakLinesForHtml(text);\r
}\r
add(new Label(wicketId, html).setEscapeModelStrings(false));\r
}\r
\r
- protected String substituteText(String text) {\r
- String html = StringUtils.breakLinesForHtml(text);\r
- Map<String, String> map = new HashMap<String, String>();\r
- // global regex keys\r
- if (GitBlit.getBoolean(Keys.regex.global, false)) {\r
- for (String key : GitBlit.getAllKeys(Keys.regex.global)) {\r
- if (!key.equals(Keys.regex.global)) {\r
- String subKey = key.substring(key.lastIndexOf('.') + 1);\r
- map.put(subKey, GitBlit.getString(key, ""));\r
- }\r
- }\r
- }\r
-\r
- // repository-specific regex keys\r
- List<String> keys = GitBlit.getAllKeys(Keys.regex._ROOT + "."\r
- + repositoryName.toLowerCase());\r
- for (String key : keys) {\r
- String subKey = key.substring(key.lastIndexOf('.') + 1);\r
- map.put(subKey, GitBlit.getString(key, ""));\r
- }\r
-\r
- for (Entry<String, String> entry : map.entrySet()) {\r
- String definition = entry.getValue().trim();\r
- String[] chunks = definition.split("!!!");\r
- if (chunks.length == 2) {\r
- html = html.replaceAll(chunks[0], chunks[1]);\r
- } else {\r
- logger.warn(entry.getKey()\r
- + " improperly formatted. Use !!! to separate match from replacement: "\r
- + definition);\r
- }\r
- }\r
- return html;\r
- }\r
-\r
protected abstract String getPageName();\r
\r
protected Component createPersonPanel(String wicketId, PersonIdent identity,\r
import java.util.ArrayList;\r
import java.util.List;\r
\r
-import javax.servlet.http.HttpServletRequest;\r
-\r
import org.apache.wicket.PageParameters;\r
import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
-import org.apache.wicket.protocol.http.WebRequest;\r
import org.eclipse.jgit.lib.Repository;\r
import org.eclipse.jgit.revwalk.RevCommit;\r
import org.wicketstuff.googlecharts.Chart;\r
metrics = MetricUtils.getDateMetrics(r, null, true, null);\r
metricsTotal = metrics.remove(0);\r
}\r
+ \r
+ addSyndicationDiscoveryLink();\r
\r
// repository description\r
add(new Label("repositoryDescription", getRepositoryModel().description));\r
default:\r
add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));\r
}\r
-\r
- HttpServletRequest req = ((WebRequest) getRequestCycle().getRequest())\r
- .getHttpServletRequest();\r
StringBuilder sb = new StringBuilder();\r
- sb.append(req.getScheme());\r
- sb.append("://");\r
- sb.append(req.getServerName());\r
- if ((req.getScheme().equals("http") && req.getServerPort() != 80)\r
- || (req.getScheme().equals("https") && req.getServerPort() != 443)) {\r
- sb.append(":" + req.getServerPort());\r
- }\r
+ sb.append(WicketUtils.getHostURL(getRequestCycle().getRequest())); \r
sb.append(Constants.GIT_SERVLET_PATH);\r
sb.append(repositoryName);\r
repositoryUrls.add(sb.toString());\r
<th wicket:id="orderByOwner"><wicket:message key="gb.owner">Owner</wicket:message></th>\r
<th></th>\r
<th wicket:id="orderByDate"><wicket:message key="gb.lastChange">Last Change</wicket:message></th>\r
- <th clas="right"></th>\r
+ <th class="right"></th>\r
</tr>\r
</wicket:fragment>\r
\r
<td class="author"><span wicket:id="repositoryOwner">[repository owner]</span></td>\r
<td style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="ticketsIcon" /><img class="inlineIcon" wicket:id="docsIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td>\r
<td><span wicket:id="repositoryLastChange">[last change]</span></td>\r
- <td class="rightAlign"><span wicket:id="repositoryLinks"></span></td>\r
+ <td class="rightAlign">\r
+ <span wicket:id="repositoryLinks"></span>\r
+ <a style="text-decoration: none;" wicket:id="syndication">\r
+ <img style="border:0px;vertical-align:middle;" src="/com/gitblit/wicket/resources/feed_16x16.png"></img>\r
+ </a>\r
+ </td> \r
</wicket:fragment>\r
\r
</wicket:panel>\r
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;\r
import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.link.ExternalLink;\r
import org.apache.wicket.markup.html.link.Link;\r
import org.apache.wicket.markup.html.panel.Fragment;\r
import org.apache.wicket.markup.repeater.Item;\r
import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.GitBlit;\r
import com.gitblit.Keys;\r
+import com.gitblit.SyndicationServlet;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.utils.StringUtils;\r
} else {\r
row.add(new Label("repositoryLinks"));\r
}\r
+ row.add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()\r
+ .getRelativePathPrefixToContextRoot(), entry.name, null, 0)));\r
WicketUtils.setAlternatingBackground(item, counter);\r
counter++;\r
}\r
import org.eclipse.jgit.lib.Repository;\r
import org.eclipse.jgit.storage.file.FileRepository;\r
\r
+import com.gitblit.FileLoginService;\r
import com.gitblit.FileSettings;\r
import com.gitblit.GitBlit;\r
import com.gitblit.GitBlitException;\r
-import com.gitblit.JettyLoginService;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.utils.JGitUtils;\r
\r
protected void setUp() throws Exception {\r
FileSettings settings = new FileSettings("distrib/gitblit.properties");\r
GitBlit.self().configureContext(settings);\r
- JettyLoginService loginService = new JettyLoginService(new File("distrib/users.properties"));\r
- loginService.loadUsers();\r
+ FileLoginService loginService = new FileLoginService(new File("distrib/users.properties"));\r
GitBlit.self().setLoginService(loginService);\r
\r
if (REPOSITORIES.exists() || REPOSITORIES.mkdirs()) {\r