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 |  290 ++++++++++++++++++++++-----------------------------------
 1 files changed, 114 insertions(+), 176 deletions(-)

diff --git a/src/com/gitblit/AccessRestrictionFilter.java b/src/com/gitblit/AccessRestrictionFilter.java
index 6ec70db..495d343 100644
--- a/src/com/gitblit/AccessRestrictionFilter.java
+++ b/src/com/gitblit/AccessRestrictionFilter.java
@@ -16,34 +16,22 @@
 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 a servlet filter that preprocesses requests
- * that match its url pattern definition in the web.xml file.
+ * The AccessRestrictionFilter is an AuthenticationFilter that confirms that the
+ * requested repository can be accessed by the anonymous or named user.
  * 
  * The filter extracts the name of the repository from the url and determines if
  * the requested action for the repository requires a Basic authentication
@@ -55,19 +43,7 @@
  * @author James Moger
  * 
  */
-public abstract class AccessRestrictionFilter implements Filter {
-
-	private static final String BASIC = "Basic";
-
-	private static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";
-
-	private static final String SESSION_SECURED = "com.gitblit.secured";
-
-	protected transient Logger logger;
-
-	public AccessRestrictionFilter() {
-		logger = LoggerFactory.getLogger(getClass());
-	}
+public abstract class AccessRestrictionFilter extends AuthenticationFilter {
 
 	/**
 	 * Extract the repository name from the url.
@@ -86,12 +62,29 @@
 	protected abstract String getUrlRequestAction(String url);
 
 	/**
+	 * 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);
+
+	/**
 	 * Determine if the repository requires authentication.
 	 * 
 	 * @param repository
+	 * @param action
 	 * @return true if authentication required
 	 */
-	protected abstract boolean requiresAuthentication(RepositoryModel repository);
+	protected abstract boolean requiresAuthentication(RepositoryModel repository, String action);
 
 	/**
 	 * Determine if the user can access the repository and perform the specified
@@ -104,6 +97,18 @@
 	 */
 	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.
@@ -118,6 +123,56 @@
 		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.
 		// JGit requires either:
@@ -127,165 +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 = getUrlRequestAction(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());
-				}
-			}
-		}
-	}
-
-	/**
-	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
-	 */
-	@Override
-	public void init(final FilterConfig config) throws ServletException {
-	}
-
-	/**
-	 * @see javax.servlet.Filter#destroy()
-	 */
-	@Override
-	public void destroy() {
-	}
-
-	/**
-	 * Wraps a standard HttpServletRequest and overrides user principal methods.
-	 */
-	public static class AccessRestrictionRequest extends ServletRequestWrapper {
-
-		private UserModel user;
-
-		public AccessRestrictionRequest(HttpServletRequest req) {
-			super(req);
-			user = new UserModel("anonymous");
-		}
-
-		void setUser(UserModel user) {
-			this.user = user;
-		}
-
-		@Override
-		public String getRemoteUser() {
-			return user.username;
-		}
-
-		@Override
-		public boolean isUserInRole(String role) {
-			if (role.equals(Constants.ADMIN_ROLE)) {
-				return user.canAdmin;
-			}
-			return user.canAccessRepository(role);
-		}
-
-		@Override
-		public Principal getUserPrincipal() {
-			return user;
-		}
+		chain.doFilter(authenticatedRequest, httpResponse);
 	}
 }
\ No newline at end of file

--
Gitblit v1.9.1