From eecaad8b8e2c447429c31a01d49260ddd6b4ee03 Mon Sep 17 00:00:00 2001 From: Paul Martin <paul@paulsputer.com> Date: Sat, 16 Apr 2016 17:35:32 -0400 Subject: [PATCH] Proof of concept #1026 --- src/main/java/com/gitblit/servlet/GitFilter.java | 138 +++++++++++++++++++++++++++++++++++++-------- 1 files changed, 112 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/gitblit/servlet/GitFilter.java b/src/main/java/com/gitblit/servlet/GitFilter.java index c44f7ef..9522893 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; } /** @@ -93,8 +102,8 @@ } /** - * Analyze the url and returns the action of the request. Return values are - * either "/git-receive-pack" or "/git-upload-pack". + * Analyze the url and returns the action of the request. Return values are: + * "/git-receive-pack", "/git-upload-pack" or "/info/lfs". * * @param serverUrl * @return action of the request @@ -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,44 @@ // repository could not be created or action was not a push return null; } + + /** + * Git lfs action uses an alternative authentication header, + * dependent on the viewing method. + * + * @param httpRequest + * @param action + * @return + */ + @Override + protected String getAuthenticationHeader(HttpServletRequest httpRequest, String action) { + + if (action.equals(gitLfs)) { + if (hasContentInRequestHeader(httpRequest, "Accept", FilestoreServlet.GIT_LFS_META_MIME)) { + return "LFS-Authenticate"; + } + } + + return super.getAuthenticationHeader(httpRequest, 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