mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-06-13 09:00:14 +02:00
Replace static mercurial permission handling
Replace our own hg permission handling with the default hg permission which is controlled by the `web` config
This commit is contained in:
committed by
SCM-Manager
parent
e679dac94d
commit
026ffa18fd
@@ -98,7 +98,7 @@ public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
|
||||
*/
|
||||
@Override
|
||||
public void service(HttpServletRequest request,
|
||||
HttpServletResponse response, Repository repository)
|
||||
HttpServletResponse response, Repository repository)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
@@ -121,13 +121,13 @@ public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
|
||||
getActionAsString(writeRequest), repository.getName(),
|
||||
getUserName(subject));
|
||||
|
||||
sendAccessDenied(request, response, subject);
|
||||
sendAccessDenied(request, response);
|
||||
}
|
||||
}
|
||||
catch (ScmSecurityException | AuthorizationException ex)
|
||||
{
|
||||
logger.warn("user " + subject.getPrincipal() + " has not enough permissions", ex);
|
||||
sendAccessDenied(request, response, subject);
|
||||
sendAccessDenied(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -142,7 +142,7 @@ public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void sendNotEnoughPrivilegesError(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
HttpServletResponse response)
|
||||
throws IOException
|
||||
{
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
@@ -158,7 +158,7 @@ public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void sendUnauthorizedError(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
HttpServletResponse response)
|
||||
throws IOException
|
||||
{
|
||||
HttpUtil.sendUnauthorized(response, configuration.getRealmDescription());
|
||||
@@ -169,12 +169,11 @@ public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
|
||||
*
|
||||
* @param request current http request object
|
||||
* @param response current http response object
|
||||
* @param subject user subject
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void sendAccessDenied(HttpServletRequest request,
|
||||
HttpServletResponse response, Subject subject)
|
||||
HttpServletResponse response)
|
||||
throws IOException
|
||||
{
|
||||
if (!Authentications.isAuthenticatedSubjectAnonymous())
|
||||
@@ -233,7 +232,7 @@ public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
|
||||
*
|
||||
* @return true if the current user has the required permissions
|
||||
*/
|
||||
private boolean hasPermission(Repository repository, boolean writeRequest)
|
||||
public static boolean hasPermission(Repository repository, boolean writeRequest)
|
||||
{
|
||||
boolean permitted;
|
||||
|
||||
|
||||
@@ -31,19 +31,19 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.HgEnvironmentBuilder;
|
||||
import sonia.scm.repository.HgExtensions;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgConfigResolver;
|
||||
import sonia.scm.repository.HgEnvironmentBuilder;
|
||||
import sonia.scm.repository.HgExtensions;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
||||
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||
import sonia.scm.web.cgi.CGIExecutor;
|
||||
import sonia.scm.web.cgi.CGIExecutorFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import sonia.scm.web.cgi.EnvList;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -147,12 +147,12 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||
HgConfig config = configResolver.resolve(repository);
|
||||
executor.setWorkDirectory(config.getDirectory());
|
||||
|
||||
executor.setArgs(createArgs(config));
|
||||
executor.setArgs(createArgs(repository, config));
|
||||
executor.execute(config.getHgBinary());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private List<String> createArgs(HgConfig config) {
|
||||
private List<String> createArgs(Repository repository, HgConfig config) {
|
||||
List<String> args = new ArrayList<>();
|
||||
config(args, "extensions.cgiserve", extension.getAbsolutePath());
|
||||
|
||||
@@ -160,9 +160,19 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||
config(args, "hooks.pretxnchangegroup.scm", String.format("python:%s:pre_hook", hooks));
|
||||
config(args, "hooks.changegroup.scm", String.format("python:%s:post_hook", hooks));
|
||||
|
||||
if (RepositoryPermissions.push(repository).isPermitted()) {
|
||||
config(args, "web.allow_push", "*");
|
||||
} else {
|
||||
config(args, "web.deny_push", "*");
|
||||
}
|
||||
|
||||
if(RepositoryPermissions.pull(repository).isPermitted()) {
|
||||
config(args, "web.allow_read", "*");
|
||||
} else {
|
||||
config(args, "web.deny_read", "*");
|
||||
}
|
||||
|
||||
config(args, "web.push_ssl", "false");
|
||||
config(args, "web.allow_read", "*");
|
||||
config(args, "web.allow_push", "*");
|
||||
|
||||
// enable experimental httppostargs protocol of mercurial
|
||||
// Issue 970: https://goo.gl/poascp
|
||||
|
||||
@@ -21,40 +21,34 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.web;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||
import sonia.scm.web.filter.PermissionFilter;
|
||||
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Permission filter for mercurial repositories.
|
||||
*
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgPermissionFilter extends PermissionFilter
|
||||
{
|
||||
|
||||
private static final Set<String> READ_METHODS = ImmutableSet.of("GET", "HEAD", "OPTIONS", "TRACE");
|
||||
public class HgPermissionFilter extends PermissionFilter {
|
||||
|
||||
private static final Set<String> READ_METHODS = Set.of("GET", "HEAD", "OPTIONS", "TRACE");
|
||||
|
||||
private final HgRepositoryHandler repositoryHandler;
|
||||
|
||||
public HgPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate, HgRepositoryHandler repositoryHandler)
|
||||
{
|
||||
public HgPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate, HgRepositoryHandler repositoryHandler) {
|
||||
super(configuration, delegate);
|
||||
this.repositoryHandler = repositoryHandler;
|
||||
}
|
||||
@@ -73,10 +67,9 @@ public class HgPermissionFilter extends PermissionFilter
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWriteRequest(HttpServletRequest request)
|
||||
{
|
||||
public boolean isWriteRequest(HttpServletRequest request) {
|
||||
if (isHttpPostArgsEnabled()) {
|
||||
return isHttpPostArgsWriteRequest(request);
|
||||
return true;
|
||||
}
|
||||
return isDefaultWriteRequest(request);
|
||||
}
|
||||
@@ -85,14 +78,7 @@ public class HgPermissionFilter extends PermissionFilter
|
||||
return repositoryHandler.getConfig().isEnableHttpPostArgs();
|
||||
}
|
||||
|
||||
private boolean isHttpPostArgsWriteRequest(HttpServletRequest request) {
|
||||
return WireProtocol.isWriteRequest(request);
|
||||
}
|
||||
|
||||
private boolean isDefaultWriteRequest(HttpServletRequest request) {
|
||||
if (READ_METHODS.contains(request.getMethod())) {
|
||||
return WireProtocol.isWriteRequest(request);
|
||||
}
|
||||
return true;
|
||||
return !READ_METHODS.contains(request.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
package sonia.scm.web;
|
||||
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||
@@ -35,13 +34,9 @@ import javax.inject.Inject;
|
||||
@Extension
|
||||
public class HgPermissionFilterFactory implements ScmProviderHttpServletDecoratorFactory {
|
||||
|
||||
private final ScmConfiguration configuration;
|
||||
private final HgRepositoryHandler repositoryHandler;
|
||||
|
||||
@Inject
|
||||
public HgPermissionFilterFactory(ScmConfiguration configuration, HgRepositoryHandler repositoryHandler) {
|
||||
this.configuration = configuration;
|
||||
this.repositoryHandler = repositoryHandler;
|
||||
public HgPermissionFilterFactory() {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,6 +46,6 @@ public class HgPermissionFilterFactory implements ScmProviderHttpServletDecorato
|
||||
|
||||
@Override
|
||||
public ScmProviderHttpServlet createDecorator(ScmProviderHttpServlet delegate) {
|
||||
return new HgPermissionFilter(configuration, delegate,repositoryHandler);
|
||||
return delegate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,20 @@ import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* WireProtocol provides methods for handling the mercurial wire protocol.
|
||||
@@ -45,52 +51,9 @@ import java.util.*;
|
||||
*/
|
||||
public final class WireProtocol {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WireProtocol.class);
|
||||
|
||||
private static final Set<String> READ_COMMANDS = ImmutableSet.of(
|
||||
"batch", "between", "branchmap", "branches", "capabilities", "changegroup", "changegroupsubset", "clonebundles",
|
||||
"getbundle", "heads", "hello", "listkeys", "lookup", "known", "stream_out",
|
||||
// could not find lheads in the wireprotocol description but mercurial 4.5.2 uses it for clone
|
||||
"lheads",
|
||||
// For HG Evolve Extension
|
||||
"evoext_obshashrange_v1"
|
||||
);
|
||||
|
||||
private static final Set<String> WRITE_COMMANDS = ImmutableSet.of(
|
||||
"pushkey", "unbundle"
|
||||
);
|
||||
|
||||
private WireProtocol() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the request is a write request. The method will always return {@code true}, expect for the
|
||||
* following cases:
|
||||
*
|
||||
* - no command was specified with the request (is required for the hgweb ui)
|
||||
* - the command in the query string was found in the list of read request
|
||||
* - if query string contains the batch command, then all commands specified in X-HgArg headers must be
|
||||
* in the list of read requests
|
||||
* - in case of enabled HttpPostArgs protocol and query string container the batch command, the header X-HgArgs-Post
|
||||
* is read and the commands which are specified in the body from 0 to the value of X-HgArgs-Post must be in the list
|
||||
* of read requests
|
||||
*
|
||||
* @param request http request
|
||||
*
|
||||
* @return {@code true} for write requests.
|
||||
*/
|
||||
public static boolean isWriteRequest(HttpServletRequest request) {
|
||||
List<String> commands = commandsOf(request);
|
||||
boolean write = isWriteRequest(commands);
|
||||
LOG.trace("mercurial request {} is write: {}", commands, write);
|
||||
return write;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean isWriteRequest(List<String> commands) {
|
||||
return !READ_COMMANDS.containsAll(commands);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static List<String> commandsOf(HttpServletRequest request) {
|
||||
List<String> listOfCmds = Lists.newArrayList();
|
||||
@@ -126,7 +89,7 @@ public final class WireProtocol {
|
||||
byte[] bytes = hgRequest.getInputStream().readAndCapture(hgArgsPostSize);
|
||||
// we use iso-8859-1 for encoding, because the post args are normally http headers which are using iso-8859-1
|
||||
// see https://tools.ietf.org/html/rfc7230#section-3.2.4
|
||||
String hgArgs = new String(bytes, Charsets.ISO_8859_1);
|
||||
String hgArgs = new String(bytes, StandardCharsets.ISO_8859_1);
|
||||
String decoded = decodeValue(hgArgs);
|
||||
parseHgCommandHeader(listOfCmds, decoded);
|
||||
} catch (IOException ex) {
|
||||
|
||||
@@ -29,44 +29,27 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.*;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.HgGlobalConfig;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.RepositoryProvider;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.*;
|
||||
import static sonia.scm.web.WireProtocolRequestMockFactory.CMDS_HEADS_KNOWN_NODES;
|
||||
import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.BOOKMARKS;
|
||||
import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.PHASES;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HgPermissionFilter}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HgPermissionFilterTest {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private ScmConfiguration configuration;
|
||||
|
||||
@Mock
|
||||
private RepositoryProvider repositoryProvider;
|
||||
|
||||
@Mock
|
||||
private HgRepositoryHandler hgRepositoryHandler;
|
||||
|
||||
private WireProtocolRequestMockFactory wireProtocol = new WireProtocolRequestMockFactory("/scm/hg/repo");
|
||||
|
||||
@InjectMocks
|
||||
private HgPermissionFilter filter;
|
||||
|
||||
@@ -75,9 +58,6 @@ public class HgPermissionFilterTest {
|
||||
when(hgRepositoryHandler.getConfig()).thenReturn(new HgGlobalConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#wrapRequestIfRequired(HttpServletRequest)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWrapRequestIfRequired() {
|
||||
assertSame(request, filter.wrapRequestIfRequired(request));
|
||||
@@ -88,140 +68,4 @@ public class HgPermissionFilterTest {
|
||||
|
||||
assertThat(filter.wrapRequestIfRequired(request), is(instanceOf(HgServletRequest.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)}.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequest() {
|
||||
// read methods
|
||||
assertFalse(isWriteRequest("GET"));
|
||||
assertFalse(isWriteRequest("HEAD"));
|
||||
assertFalse(isWriteRequest("TRACE"));
|
||||
assertFalse(isWriteRequest("OPTIONS"));
|
||||
|
||||
// write methods
|
||||
assertTrue(isWriteRequest("POST"));
|
||||
assertTrue(isWriteRequest("PUT"));
|
||||
assertTrue(isWriteRequest("DELETE"));
|
||||
assertTrue(isWriteRequest("KA"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)} with enabled httppostargs option.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequestWithEnabledHttpPostArgs() {
|
||||
HgGlobalConfig config = new HgGlobalConfig();
|
||||
config.setEnableHttpPostArgs(true);
|
||||
when(hgRepositoryHandler.getConfig()).thenReturn(config);
|
||||
|
||||
assertFalse(isWriteRequest("POST"));
|
||||
assertFalse(isWriteRequest("POST", "heads"));
|
||||
assertTrue(isWriteRequest("POST", "unbundle"));
|
||||
}
|
||||
|
||||
private boolean isWriteRequest(String method) {
|
||||
return isWriteRequest(method, "capabilities");
|
||||
}
|
||||
|
||||
private boolean isWriteRequest(String method, String command) {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
when(request.getQueryString()).thenReturn("cmd=" + command);
|
||||
when(request.getMethod()).thenReturn(method);
|
||||
return filter.isWriteRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)} with a set of requests, which are used for a
|
||||
* fresh clone of a repository.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequestWithClone() {
|
||||
assertIsReadRequest(wireProtocol.capabilities());
|
||||
assertIsReadRequest(wireProtocol.listkeys(BOOKMARKS));
|
||||
assertIsReadRequest(wireProtocol.batch(CMDS_HEADS_KNOWN_NODES));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)} with a set of requests, which are used for a
|
||||
* push of a single changeset.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequestWithSingleChangesetPush() {
|
||||
assertIsReadRequest(wireProtocol.capabilities());
|
||||
assertIsReadRequest(wireProtocol.batch(CMDS_HEADS_KNOWN_NODES.concat("c0ceccb3b2f0f5c977ff32b9337519e5f37942c2")));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsReadRequest(wireProtocol.listkeys(BOOKMARKS));
|
||||
assertIsWriteRequest(wireProtocol.unbundle(261L, "686173686564+6768033e216468247bd031a0a2d9876d79818f8f"));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsWriteRequest(wireProtocol.pushkey("c0ceccb3b2f0f5c977ff32b9337519e5f37942c2&namespace=phases&new=0&old=1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)} with a set of requests, which are used for a
|
||||
* push to a single changeset.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequestWithMultipleChangesetsPush() {
|
||||
assertIsReadRequest(wireProtocol.capabilities());
|
||||
assertIsReadRequest(wireProtocol.batch(CMDS_HEADS_KNOWN_NODES.concat("ef5993bb4abb32a0565c347844c6d939fc4f4b98")));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsReadRequest(wireProtocol.listkeys(BOOKMARKS));
|
||||
assertIsReadRequest(wireProtocol.branchmap());
|
||||
assertIsReadRequest(wireProtocol.listkeys(BOOKMARKS));
|
||||
assertIsWriteRequest(wireProtocol.unbundle(746L, "686173686564+95373ca7cd5371cb6c49bb755ee451d9ec585845"));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsWriteRequest(wireProtocol.pushkey("ef5993bb4abb32a0565c347844c6d939fc4f4b98&namespace=phases&new=0&old=1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)} with a set of requests, which are used for a
|
||||
* push of multiple branches to a new repository.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequestWithMutlipleBranchesToNewRepositoryPush() {
|
||||
assertIsReadRequest(wireProtocol.capabilities());
|
||||
assertIsReadRequest(wireProtocol.batch(CMDS_HEADS_KNOWN_NODES.concat("ef5993bb4abb32a0565c347844c6d939fc4f4b98")));
|
||||
assertIsReadRequest(wireProtocol.known("c0ceccb3b2f0f5c977ff32b9337519e5f37942c2+187ddf37e237c370514487a0bb1a226f11a780b3+b5914611f84eae14543684b2721eec88b0edac12+8b63a323606f10c86b30465570c2574eb7a3a989"));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsReadRequest(wireProtocol.listkeys(BOOKMARKS));
|
||||
assertIsWriteRequest(wireProtocol.unbundle(913L, "686173686564+6768033e216468247bd031a0a2d9876d79818f8f"));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsWriteRequest(wireProtocol.pushkey("ef5993bb4abb32a0565c347844c6d939fc4f4b98&namespace=phases&new=0&old=1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)} with a set of requests, which are used for a
|
||||
* push of a bookmark.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequestWithBookmarkPush() {
|
||||
assertIsReadRequest(wireProtocol.capabilities());
|
||||
assertIsReadRequest(wireProtocol.batch(CMDS_HEADS_KNOWN_NODES.concat("ef5993bb4abb32a0565c347844c6d939fc4f4b98")));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsReadRequest(wireProtocol.listkeys(BOOKMARKS));
|
||||
assertIsReadRequest(wireProtocol.listkeys(PHASES));
|
||||
assertIsWriteRequest(wireProtocol.pushkey("markone&namespace=bookmarks&new=ef5993bb4abb32a0565c347844c6d939fc4f4b98&old="));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)} with a write request hidden in a batch GET
|
||||
* request.
|
||||
*
|
||||
* @see <a href="https://goo.gl/poascp">Issue #970</a>
|
||||
*/
|
||||
@Test
|
||||
public void testIsWriteRequestWithBookmarkPushInABatch() {
|
||||
assertIsWriteRequest(wireProtocol.batch("pushkey key=markthree,namespace=bookmarks,new=187ddf37e237c370514487a0bb1a226f11a780b3,old="));
|
||||
}
|
||||
|
||||
private void assertIsReadRequest(HttpServletRequest request) {
|
||||
assertFalse(filter.isWriteRequest(request));
|
||||
}
|
||||
|
||||
private void assertIsWriteRequest(HttpServletRequest request) {
|
||||
assertTrue(filter.isWriteRequest(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.web;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class WireProtocolRequestMockFactory {
|
||||
|
||||
public enum Namespace {
|
||||
PHASES, BOOKMARKS;
|
||||
}
|
||||
|
||||
public static final String CMDS_HEADS_KNOWN_NODES = "heads+%3Bknown+nodes%3D";
|
||||
|
||||
private String repositoryPath;
|
||||
|
||||
public WireProtocolRequestMockFactory(String repositoryPath) {
|
||||
this.repositoryPath = repositoryPath;
|
||||
}
|
||||
|
||||
public HttpServletRequest capabilities() {
|
||||
return base("GET", "cmd=capabilities");
|
||||
}
|
||||
|
||||
public HttpServletRequest listkeys(Namespace namespace) {
|
||||
HttpServletRequest request = base("GET", "cmd=capabilities");
|
||||
header(request, "vary", "X-HgArg-1");
|
||||
header(request, "x-hgarg-1", namespaceValue(namespace));
|
||||
return request;
|
||||
}
|
||||
|
||||
public HttpServletRequest branchmap() {
|
||||
return base("GET", "cmd=branchmap");
|
||||
}
|
||||
|
||||
public HttpServletRequest batch(String... args) {
|
||||
HttpServletRequest request = base("GET", "cmd=batch");
|
||||
args(request, "cmds", args);
|
||||
return request;
|
||||
}
|
||||
|
||||
public HttpServletRequest unbundle(long contentLength, String... heads) {
|
||||
HttpServletRequest request = base("POST", "cmd=unbundle");
|
||||
header(request, "Content-Length", String.valueOf(contentLength));
|
||||
args(request, "heads", heads);
|
||||
return request;
|
||||
}
|
||||
|
||||
public HttpServletRequest pushkey(String... keys) {
|
||||
HttpServletRequest request = base("POST", "cmd=pushkey");
|
||||
args(request, "key", keys);
|
||||
return request;
|
||||
}
|
||||
|
||||
public HttpServletRequest known(String... nodes) {
|
||||
HttpServletRequest request = base("GET", "cmd=known");
|
||||
args(request, "nodes", nodes);
|
||||
return request;
|
||||
}
|
||||
|
||||
private void args(HttpServletRequest request, String prefix, String[] values) {
|
||||
List<String> headers = Lists.newArrayList();
|
||||
|
||||
StringBuilder vary = new StringBuilder();
|
||||
for ( int i=0; i<values.length; i++ ) {
|
||||
String header = "X-HgArg-" + (i+1);
|
||||
|
||||
if (i>0) {
|
||||
vary.append(",");
|
||||
}
|
||||
|
||||
vary.append(header);
|
||||
headers.add(header);
|
||||
|
||||
header(request, header, prefix + "=" + values[i]);
|
||||
}
|
||||
header(request, "Vary", vary.toString());
|
||||
|
||||
when(request.getHeaderNames()).thenReturn(Collections.enumeration(headers));
|
||||
}
|
||||
|
||||
private HttpServletRequest base(String method, String queryStringValue) {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
when(request.getMethod()).thenReturn(method);
|
||||
|
||||
queryString(request, queryStringValue);
|
||||
|
||||
header(request, "Accept", "application/mercurial-0.1");
|
||||
header(request, "Accept-Encoding", "identity");
|
||||
header(request, "User-Agent", "mercurial/proto-1.0 (Mercurial 4.3.1)");
|
||||
return request;
|
||||
}
|
||||
|
||||
private void queryString(HttpServletRequest request, String queryString) {
|
||||
when(request.getQueryString()).thenReturn(queryString);
|
||||
}
|
||||
|
||||
private void header(HttpServletRequest request, String header, String value) {
|
||||
when(request.getHeader(header)).thenReturn(value);
|
||||
}
|
||||
|
||||
private String namespaceValue(Namespace namespace) {
|
||||
return "namespace=" + namespace.toString().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,31 +51,6 @@ public class WireProtocolTest {
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Test
|
||||
public void testIsWriteRequestOnPost() {
|
||||
assertIsWriteRequest("capabilities", "unbundle");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsWriteRequest() {
|
||||
assertIsWriteRequest("unbundle");
|
||||
assertIsWriteRequest("capabilities", "unbundle");
|
||||
assertIsWriteRequest("capabilities", "postkeys");
|
||||
assertIsReadRequest();
|
||||
assertIsReadRequest("capabilities");
|
||||
assertIsReadRequest("capabilities", "branches", "branchmap");
|
||||
}
|
||||
|
||||
private void assertIsWriteRequest(String... commands) {
|
||||
List<String> cmdList = Lists.newArrayList(commands);
|
||||
assertTrue(WireProtocol.isWriteRequest(cmdList));
|
||||
}
|
||||
|
||||
private void assertIsReadRequest(String... commands) {
|
||||
List<String> cmdList = Lists.newArrayList(commands);
|
||||
assertFalse(WireProtocol.isWriteRequest(cmdList));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCommandsOf() {
|
||||
expectQueryCommand("capabilities", "cmd=capabilities");
|
||||
|
||||
Reference in New Issue
Block a user