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