From e41e8f8c3bc9f5edab1d271464364f95620ece8c Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Thu, 19 Nov 2015 17:55:38 -0500
Subject: [PATCH] Create filestore directory on startup

---
 src/main/java/com/gitblit/servlet/GitFilter.java |  130 +++++++++++++++++++++++++++++++++++--------
 1 files changed, 106 insertions(+), 24 deletions(-)

diff --git a/src/main/java/com/gitblit/servlet/GitFilter.java b/src/main/java/com/gitblit/servlet/GitFilter.java
index c44f7ef..27408f0 100644
--- a/src/main/java/com/gitblit/servlet/GitFilter.java
+++ b/src/main/java/com/gitblit/servlet/GitFilter.java
@@ -17,19 +17,20 @@
 
 import java.text.MessageFormat;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
-import com.gitblit.Constants;
+import javax.servlet.http.HttpServletRequest;
+
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
 import com.gitblit.GitBlitException;
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
-import com.gitblit.Constants.AccessRestrictionType;
-import com.gitblit.Constants.AuthorizationControl;
-import com.gitblit.Keys.git;
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.manager.IAuthenticationManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
@@ -48,20 +49,28 @@
 	protected static final String gitReceivePack = "/git-receive-pack";
 
 	protected static final String gitUploadPack = "/git-upload-pack";
-
+	
+	protected static final String gitLfs = "/info/lfs";
+	
 	protected static final String[] suffixes = { gitReceivePack, gitUploadPack, "/info/refs", "/HEAD",
-			"/objects" };
+			"/objects", gitLfs };
 
-	private final IStoredSettings settings;
+	private IStoredSettings settings;
+
+	private IFederationManager federationManager;
 
 	@Inject
 	public GitFilter(
+			IStoredSettings settings,
 			IRuntimeManager runtimeManager,
 			IAuthenticationManager authenticationManager,
-			IRepositoryManager repositoryManager) {
+			IRepositoryManager repositoryManager,
+			IFederationManager federationManager) {
 
 		super(runtimeManager, authenticationManager, repositoryManager);
-		this.settings = runtimeManager.getSettings();
+
+		this.settings = settings;
+		this.federationManager = federationManager;
 	}
 
 	/**
@@ -110,6 +119,8 @@
 				return gitReceivePack;
 			} else if (suffix.contains("?service=git-upload-pack")) {
 				return gitUploadPack;
+			} else if (suffix.startsWith(gitLfs)) {
+				return gitLfs;
 			} else {
 				return gitUploadPack;
 			}
@@ -118,12 +129,33 @@
 	}
 
 	/**
+	 * Returns the user making the request, if the user has authenticated.
+	 *
+	 * @param httpRequest
+	 * @return user
+	 */
+	@Override
+	protected UserModel getUser(HttpServletRequest httpRequest) {
+		UserModel user = authenticationManager.authenticate(httpRequest, requiresClientCertificate());
+		if (user == null) {
+			user = federationManager.authenticate(httpRequest);
+		}
+		return user;
+	}
+
+	/**
 	 * Determine if a non-existing repository can be created using this filter.
 	 *
 	 * @return true if the server allows repository creation on-push
 	 */
 	@Override
-	protected boolean isCreationAllowed() {
+	protected boolean isCreationAllowed(String action) {
+		
+		//Repository must already exist before large files can be deposited
+		if (action.equals(gitLfs)) {
+			return false;
+		}
+		
 		return settings.getBoolean(Keys.git.allowCreateOnPush, true);
 	}
 
@@ -135,9 +167,15 @@
 	 * @return true if the action may be performed
 	 */
 	@Override
-	protected boolean isActionAllowed(RepositoryModel repository, String action) {
+	protected boolean isActionAllowed(RepositoryModel repository, String action, String method) {
 		// the log here has been moved into ReceiveHook to provide clients with
 		// error messages
+		if (gitLfs.equals(action)) {
+			if (!method.matches("GET|POST|PUT|HEAD")) {
+				return false;
+			}
+		}
+		
 		return true;
 	}
 
@@ -151,16 +189,25 @@
 	 *
 	 * @param repository
 	 * @param action
+	 * @param method
 	 * @return true if authentication required
 	 */
 	@Override
-	protected boolean requiresAuthentication(RepositoryModel repository, String action) {
+	protected boolean requiresAuthentication(RepositoryModel repository, String action, String method) {
 		if (gitUploadPack.equals(action)) {
 			// send to client
 			return repository.accessRestriction.atLeast(AccessRestrictionType.CLONE);
 		} else if (gitReceivePack.equals(action)) {
 			// receive from client
 			return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);
+		} else if (gitLfs.equals(action)) {
+			
+			if (method.matches("GET|HEAD")) {
+				return repository.accessRestriction.atLeast(AccessRestrictionType.CLONE);
+			} else {
+				//NOTE: Treat POST as PUT as as without reading message type cannot determine 
+				return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);
+			}
 		}
 		return false;
 	}
@@ -181,15 +228,8 @@
 			return false;
 		}
 		if (action.equals(gitReceivePack)) {
-			// Push request
-			if (user.canPush(repository)) {
-				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;
-			}
+			// push permissions are enforced in the receive pack
+			return true;
 		} else if (action.equals(gitUploadPack)) {
 			// Clone request
 			if (user.canClone(repository)) {
@@ -216,6 +256,12 @@
 	@Override
 	protected RepositoryModel createRepository(UserModel user, String repository, String action) {
 		boolean isPush = !StringUtils.isEmpty(action) && gitReceivePack.equals(action);
+		
+		if (action.equals(gitLfs)) {
+			//Repository must already exist for any filestore actions
+			return null;
+		}
+		
 		if (isPush) {
 			if (user.canCreate(repository)) {
 				// user is pushing to a new repository
@@ -267,4 +313,40 @@
 		// repository could not be created or action was not a push
 		return null;
 	}
+	
+	/**
+	 * Git lfs action uses an alternative authentication header, 
+	 * 
+	 * @param action
+	 * @return
+	 */
+	@Override
+	protected String getAuthenticationHeader(String action) {
+
+		if (action.equals(gitLfs)) {
+			return "LFS-Authenticate";
+		}
+		
+		return super.getAuthenticationHeader(action);
+	}
+	
+	/**
+	 * Interrogates the request headers based on the action
+	 * @param action
+	 * @param request
+	 * @return
+	 */
+	@Override
+	protected boolean hasValidRequestHeader(String action,
+			HttpServletRequest request) {
+
+		if (action.equals(gitLfs) && request.getMethod().equals("POST")) {
+			if ( 	!hasContentInRequestHeader(request, "Accept", FilestoreServlet.GIT_LFS_META_MIME)
+				 || !hasContentInRequestHeader(request, "Content-Type", FilestoreServlet.GIT_LFS_META_MIME)) {
+				return false;
+			}				
+		}
+			
+		return super.hasValidRequestHeader(action, request);
+	}
 }

--
Gitblit v1.9.1