From 27ae9095639bb228a1b7ff86a3ebe4264abf05be Mon Sep 17 00:00:00 2001
From: mschaefers <mschaefers@scoop-gmbh.de>
Date: Thu, 29 Nov 2012 12:33:09 -0500
Subject: [PATCH] feature: when using LdapUserService one can configure Gitblit to fetch all users from ldap that can possibly login. This allows to see newly generated LDAP users instantly in Gitblit. By now an LDAP user had to log in once to appear in GitBlit.
---
src/com/gitblit/AccessRestrictionFilter.java | 329 ++++++++++++++++++++++++++----------------------------
1 files changed, 158 insertions(+), 171 deletions(-)
diff --git a/src/com/gitblit/AccessRestrictionFilter.java b/src/com/gitblit/AccessRestrictionFilter.java
index e3dcc62..495d343 100644
--- a/src/com/gitblit/AccessRestrictionFilter.java
+++ b/src/com/gitblit/AccessRestrictionFilter.java
@@ -16,64 +16,162 @@
package com.gitblit;
import java.io.IOException;
-import java.nio.charset.Charset;
-import java.security.Principal;
import java.text.MessageFormat;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import javax.servlet.Filter;
import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.eclipse.jgit.util.Base64;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
/**
+ * The AccessRestrictionFilter is an AuthenticationFilter that confirms that the
+ * requested repository can be accessed by the anonymous or named user.
+ *
+ * The filter extracts the name of the repository from the url and determines if
+ * the requested action for the repository requires a Basic authentication
+ * prompt. If authentication is required and no credentials are stored in the
+ * "Authorization" header, then a basic authentication challenge is issued.
*
* http://en.wikipedia.org/wiki/Basic_access_authentication
+ *
+ * @author James Moger
+ *
*/
-public abstract class AccessRestrictionFilter implements Filter {
+public abstract class AccessRestrictionFilter extends AuthenticationFilter {
- 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());
- }
-
+ /**
+ * Extract the repository name from the url.
+ *
+ * @param url
+ * @return repository name
+ */
protected abstract String extractRepositoryName(String url);
- protected abstract String getUrlRequestType(String url);
+ /**
+ * Analyze the url and returns the action of the request.
+ *
+ * @param url
+ * @return action of the request
+ */
+ protected abstract String getUrlRequestAction(String url);
- protected abstract boolean requiresAuthentication(RepositoryModel repository);
+ /**
+ * Determine if a non-existing repository can be created using this filter.
+ *
+ * @return true if the filter allows repository creation
+ */
+ protected abstract boolean isCreationAllowed();
+
+ /**
+ * Determine if the action may be executed on the repository.
+ *
+ * @param repository
+ * @param action
+ * @return true if the action may be performed
+ */
+ protected abstract boolean isActionAllowed(RepositoryModel repository, String action);
- protected abstract boolean canAccess(RepositoryModel repository, UserModel user,
- String restrictedUrl);
+ /**
+ * Determine if the repository requires authentication.
+ *
+ * @param repository
+ * @param action
+ * @return true if authentication required
+ */
+ protected abstract boolean requiresAuthentication(RepositoryModel repository, String action);
+ /**
+ * Determine if the user can access the repository and perform the specified
+ * action.
+ *
+ * @param repository
+ * @param user
+ * @param action
+ * @return true if user may execute the action on the repository
+ */
+ protected abstract boolean canAccess(RepositoryModel repository, UserModel user, String action);
+
+ /**
+ * Allows a filter to create a repository, if one does not exist.
+ *
+ * @param user
+ * @param repository
+ * @param action
+ * @return the repository model, if it is created, null otherwise
+ */
+ protected RepositoryModel createRepository(UserModel user, String repository, String action) {
+ return null;
+ }
+
+ /**
+ * doFilter does the actual work of preprocessing the request to ensure that
+ * the user may proceed.
+ *
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse, javax.servlet.FilterChain)
+ */
@Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+ String fullUrl = getFullUrl(httpRequest);
+ String repository = extractRepositoryName(fullUrl);
+
+ if (GitBlit.self().isCollectingGarbage(repository)) {
+ logger.info(MessageFormat.format("ARF: Rejecting request for {0}, busy collecting garbage!", repository));
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ // Determine if the request URL is restricted
+ String fullSuffix = fullUrl.substring(repository.length());
+ String urlRequestType = getUrlRequestAction(fullSuffix);
+
+ UserModel user = getUser(httpRequest);
+
+ // Load the repository model
+ RepositoryModel model = GitBlit.self().getRepositoryModel(repository);
+ if (model == null) {
+ if (isCreationAllowed()) {
+ if (user == null) {
+ // challenge client to provide credentials for creation. send 401.
+ if (GitBlit.isDebugMode()) {
+ logger.info(MessageFormat.format("ARF: CREATE CHALLENGE {0}", fullUrl));
+ }
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ } else {
+ // see if we can create a repository for this request
+ model = createRepository(user, repository, urlRequestType);
+ }
+ }
+
+ if (model == null) {
+ // repository not found. send 404.
+ logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,
+ HttpServletResponse.SC_NOT_FOUND));
+ httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ }
+
+ // Confirm that the action may be executed on the repository
+ if (!isActionAllowed(model, urlRequestType)) {
+ logger.info(MessageFormat.format("ARF: action {0} on {1} forbidden ({2})",
+ urlRequestType, model, HttpServletResponse.SC_FORBIDDEN));
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
// Wrap the HttpServletRequest with the AccessRestrictionRequest which
// overrides the servlet container user principal methods.
@@ -84,159 +182,48 @@
//
// 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;
+ AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);
+ if (user != null) {
+ authenticatedRequest.setUser(user);
}
// BASIC authentication challenge and response processing
- if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model)) {
- // look for client authorization credentials in header
- final String authorization = httpRequest.getHeader("Authorization");
- if (authorization != null && authorization.startsWith(BASIC)) {
- // Authorization: Basic base64credentials
- String base64Credentials = authorization.substring(BASIC.length()).trim();
- String credentials = new String(Base64.decode(base64Credentials),
- Charset.forName("UTF-8"));
- // credentials = username:password
- final String[] values = credentials.split(":");
-
- if (values.length == 2) {
- String username = values[0];
- char[] password = values[1].toCharArray();
- UserModel user = GitBlit.self().authenticate(username, password);
- if (user != null) {
- accessRequest.setUser(user);
- if (user.canAdmin || canAccess(model, user, urlRequestType)) {
- // authenticated request permitted.
- // pass processing to the restricted servlet.
- newSession(accessRequest, httpResponse);
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE
- + ") authenticated");
- chain.doFilter(accessRequest, httpResponse);
- return;
- }
- // valid user, but not for requested access. send 403.
- if (GitBlit.isDebugMode()) {
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_FORBIDDEN
- + ")");
- logger.info(MessageFormat.format("AUTH: {0} forbidden to access {1}",
- user.username, url));
- }
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
- return;
- }
- }
+ if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model, urlRequestType)) {
+ if (user == null) {
+ // challenge client to provide credentials. send 401.
if (GitBlit.isDebugMode()) {
- logger.info(MessageFormat
- .format("AUTH: invalid credentials ({0})", credentials));
+ logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));
}
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ } else {
+ // check user access for request
+ if (user.canAdmin() || canAccess(model, user, urlRequestType)) {
+ // authenticated request permitted.
+ // pass processing to the restricted servlet.
+ newSession(authenticatedRequest, httpResponse);
+ logger.info(MessageFormat.format("ARF: {0} ({1}) authenticated", fullUrl,
+ HttpServletResponse.SC_CONTINUE));
+ chain.doFilter(authenticatedRequest, httpResponse);
+ return;
+ }
+ // valid user, but not for requested access. send 403.
+ if (GitBlit.isDebugMode()) {
+ logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",
+ user.username, fullUrl));
+ }
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
}
-
- // challenge client to provide credentials. send 401.
- if (GitBlit.isDebugMode()) {
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_UNAUTHORIZED + ")");
- logger.info("AUTH: Challenge " + CHALLENGE);
- }
- httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
- httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
- return;
}
if (GitBlit.isDebugMode()) {
- logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE
- + ") unauthenticated");
+ logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,
+ HttpServletResponse.SC_CONTINUE));
}
// unauthenticated request permitted.
// pass processing to the restricted servlet.
- chain.doFilter(accessRequest, httpResponse);
- }
-
- /**
- * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()
- */
- protected void newSession(HttpServletRequest request, HttpServletResponse response) {
- HttpSession oldSession = request.getSession(false);
- if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {
- synchronized (this) {
- Map<String, Object> attributes = new HashMap<String, Object>();
- Enumeration<String> e = oldSession.getAttributeNames();
- while (e.hasMoreElements()) {
- String name = e.nextElement();
- attributes.put(name, oldSession.getAttribute(name));
- oldSession.removeAttribute(name);
- }
- oldSession.invalidate();
-
- HttpSession newSession = request.getSession(true);
- newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);
- for (Map.Entry<String, Object> entry : attributes.entrySet()) {
- newSession.setAttribute(entry.getKey(), entry.getValue());
- }
- }
- }
- }
-
- @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;
- }
+ chain.doFilter(authenticatedRequest, httpResponse);
}
}
\ No newline at end of file
--
Gitblit v1.9.1