diff --git a/scm-core/src/main/java/sonia/scm/ArgumentIsInvalidException.java b/scm-core/src/main/java/sonia/scm/ArgumentIsInvalidException.java deleted file mode 100644 index 727e9a8160..0000000000 --- a/scm-core/src/main/java/sonia/scm/ArgumentIsInvalidException.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - -package sonia.scm; - -/** - * - * @author Sebastian Sdorra - * @since 1.17 - */ -public class ArgumentIsInvalidException extends IllegalStateException -{ - - /** - * Constructs ... - * - */ - public ArgumentIsInvalidException() - { - super(); - } - - /** - * Constructs ... - * - * - * @param s - */ - public ArgumentIsInvalidException(String s) - { - super(s); - } - - /** - * Constructs ... - * - * - * @param cause - */ - public ArgumentIsInvalidException(Throwable cause) - { - super(cause); - } - - /** - * Constructs ... - * - * - * @param message - * @param cause - */ - public ArgumentIsInvalidException(String message, Throwable cause) - { - super(message, cause); - } -} diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java index 6f6831b058..0797134c9f 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java @@ -3,7 +3,6 @@ package sonia.scm.api.v2.resources; import com.google.common.collect.ImmutableList; import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; @@ -14,7 +13,7 @@ import java.util.Arrays; * builder for each method. * *
- * LinkBuilder builder = new LinkBuilder(uriInfo, MainResource.class, SubResource.class);
+ * LinkBuilder builder = new LinkBuilder(pathInfo, MainResource.class, SubResource.class);
* Link link = builder
* .method("sub")
* .parameters("param")
@@ -25,16 +24,16 @@ import java.util.Arrays;
*/
@SuppressWarnings("WeakerAccess") // Non-public will result in IllegalAccessError for plugins
public class LinkBuilder {
- private final UriInfo uriInfo;
+ private final ScmPathInfo pathInfo;
private final Class[] classes;
private final ImmutableList calls;
- public LinkBuilder(UriInfo uriInfo, Class... classes) {
- this(uriInfo, classes, ImmutableList.of());
+ public LinkBuilder(ScmPathInfo pathInfo, Class... classes) {
+ this(pathInfo, classes, ImmutableList.of());
}
- private LinkBuilder(UriInfo uriInfo, Class[] classes, ImmutableList calls) {
- this.uriInfo = uriInfo;
+ private LinkBuilder(ScmPathInfo pathInfo, Class[] classes, ImmutableList calls) {
+ this.pathInfo = pathInfo;
this.classes = classes;
this.calls = calls;
}
@@ -51,7 +50,7 @@ public class LinkBuilder {
throw new IllegalStateException("not enough methods for all classes");
}
- URI baseUri = uriInfo.getBaseUri();
+ URI baseUri = pathInfo.getApiRestUri();
URI relativeUri = createRelativeUri();
return baseUri.resolve(relativeUri);
}
@@ -61,7 +60,7 @@ public class LinkBuilder {
}
private LinkBuilder add(String method, String[] parameters) {
- return new LinkBuilder(uriInfo, classes, appendNewCall(method, parameters));
+ return new LinkBuilder(pathInfo, classes, appendNewCall(method, parameters));
}
private ImmutableList appendNewCall(String method, String[] parameters) {
diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/ScmPathInfo.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/ScmPathInfo.java
new file mode 100644
index 0000000000..fa975520c1
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/ScmPathInfo.java
@@ -0,0 +1,14 @@
+package sonia.scm.api.v2.resources;
+
+import java.net.URI;
+
+public interface ScmPathInfo {
+
+ String REST_API_PATH = "/api/rest";
+
+ URI getApiRestUri();
+
+ default URI getRootUri() {
+ return getApiRestUri().resolve("../..");
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/ScmPathInfoStore.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/ScmPathInfoStore.java
new file mode 100644
index 0000000000..c88bd4a2b5
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/ScmPathInfoStore.java
@@ -0,0 +1,18 @@
+package sonia.scm.api.v2.resources;
+
+public class ScmPathInfoStore {
+
+ private ScmPathInfo pathInfo;
+
+ public ScmPathInfo get() {
+ return pathInfo;
+ }
+
+ public void set(ScmPathInfo info) {
+ if (this.pathInfo != null) {
+ throw new IllegalStateException("UriInfo already set");
+ }
+ this.pathInfo = info;
+ }
+
+}
diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/UriInfoStore.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/UriInfoStore.java
deleted file mode 100644
index 2f61383cfd..0000000000
--- a/scm-core/src/main/java/sonia/scm/api/v2/resources/UriInfoStore.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package sonia.scm.api.v2.resources;
-
-import javax.ws.rs.core.UriInfo;
-
-public class UriInfoStore {
-
- private UriInfo uriInfo;
-
- public UriInfo get() {
- return uriInfo;
- }
-
- public void set(UriInfo uriInfo) {
- if (this.uriInfo != null) {
- throw new IllegalStateException("UriInfo already set");
- }
- this.uriInfo = uriInfo;
- }
-}
diff --git a/scm-core/src/main/java/sonia/scm/filter/Filters.java b/scm-core/src/main/java/sonia/scm/filter/Filters.java
index b6a45811bc..b1f5ea47cf 100644
--- a/scm-core/src/main/java/sonia/scm/filter/Filters.java
+++ b/scm-core/src/main/java/sonia/scm/filter/Filters.java
@@ -31,6 +31,8 @@
package sonia.scm.filter;
+import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
+
/**
* Useful constants for filter implementations.
*
@@ -44,26 +46,26 @@ public final class Filters
public static final String PATTERN_ALL = "/*";
/** Field description */
- public static final String PATTERN_CONFIG = "/api/rest/config*";
+ public static final String PATTERN_CONFIG = REST_API_PATH + "/config*";
/** Field description */
public static final String PATTERN_DEBUG = "/debug.html";
/** Field description */
- public static final String PATTERN_GROUPS = "/api/rest/groups*";
+ public static final String PATTERN_GROUPS = REST_API_PATH + "/groups*";
/** Field description */
- public static final String PATTERN_PLUGINS = "/api/rest/plugins*";
+ public static final String PATTERN_PLUGINS = REST_API_PATH + "/plugins*";
/** Field description */
public static final String PATTERN_RESOURCE_REGEX =
"^/(?:resources|api|plugins|index)[\\./].*(?:html|\\.css|\\.js|\\.xml|\\.json|\\.txt)";
/** Field description */
- public static final String PATTERN_RESTAPI = "/api/rest/*";
+ public static final String PATTERN_RESTAPI = REST_API_PATH + "/*";
/** Field description */
- public static final String PATTERN_USERS = "/api/rest/users*";
+ public static final String PATTERN_USERS = REST_API_PATH + "/users*";
/** authentication priority */
public static final int PRIORITY_AUTHENTICATION = 5000;
diff --git a/scm-core/src/main/java/sonia/scm/repository/Repository.java b/scm-core/src/main/java/sonia/scm/repository/Repository.java
index 0d8c6a6af8..cad36f2d88 100644
--- a/scm-core/src/main/java/sonia/scm/repository/Repository.java
+++ b/scm-core/src/main/java/sonia/scm/repository/Repository.java
@@ -40,7 +40,6 @@ import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import sonia.scm.BasicPropertiesAware;
import sonia.scm.ModelObject;
-import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util;
import sonia.scm.util.ValidationUtil;
@@ -349,17 +348,6 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
// do not copy health check results
}
- /**
- * Creates the url of the repository.
- *
- * @param baseUrl base url of the server including the context path
- * @return url of the repository
- * @since 1.17
- */
- public String createUrl(String baseUrl) {
- return HttpUtil.concatenate(baseUrl, type, namespace, name);
- }
-
/**
* Returns true if the {@link Repository} is the same as the obj argument.
*
diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java
index 2c4d958d8d..1e2fdccf42 100644
--- a/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java
+++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java
@@ -38,7 +38,6 @@ package sonia.scm.repository;
import sonia.scm.AlreadyExistsException;
import sonia.scm.TypeManager;
-import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collection;
@@ -99,29 +98,6 @@ public interface RepositoryManager
*/
public Collection getConfiguredTypes();
- /**
- * Returns the {@link Repository} associated to the request uri.
- *
- *
- * @param request the current http request
- *
- * @return associated to the request uri
- * @since 1.9
- */
- public Repository getFromRequest(HttpServletRequest request);
-
- /**
- * Returns the {@link Repository} associated to the request uri.
- *
- *
- *
- * @param uri request uri without context path
- *
- * @return associated to the request uri
- * @since 1.9
- */
- public Repository getFromUri(String uri);
-
/**
* Returns a {@link RepositoryHandler} by the given type (hg, git, svn ...).
*
diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java
index aa4117af34..87df960da7 100644
--- a/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java
+++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java
@@ -39,7 +39,6 @@ import sonia.scm.AlreadyExistsException;
import sonia.scm.ManagerDecorator;
import sonia.scm.Type;
-import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collection;
@@ -120,34 +119,6 @@ public class RepositoryManagerDecorator
return decorated;
}
- /**
- * {@inheritDoc}
- *
- *
- * @param request
- *
- * @return
- */
- @Override
- public Repository getFromRequest(HttpServletRequest request)
- {
- return decorated.getFromRequest(request);
- }
-
- /**
- * {@inheritDoc}
- *
- *
- * @param uri
- *
- * @return
- */
- @Override
- public Repository getFromUri(String uri)
- {
- return decorated.getFromUri(uri);
- }
-
/**
* {@inheritDoc}
*
diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java
index 070221117a..9dd866daa4 100644
--- a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java
+++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java
@@ -44,8 +44,8 @@ import sonia.scm.NotFoundException;
public class RepositoryNotFoundException extends NotFoundException
{
- /** Field description */
private static final long serialVersionUID = -6583078808900520166L;
+ private static final String TYPE_REPOSITORY = "repository";
//~--- constructors ---------------------------------------------------------
@@ -55,10 +55,14 @@ public class RepositoryNotFoundException extends NotFoundException
*
*/
public RepositoryNotFoundException(Repository repository) {
- super("repository", repository.getName() + "/" + repository.getNamespace());
+ super(TYPE_REPOSITORY, repository.getName() + "/" + repository.getNamespace());
}
public RepositoryNotFoundException(String repositoryId) {
- super("repository", repositoryId);
+ super(TYPE_REPOSITORY, repositoryId);
+ }
+
+ public RepositoryNotFoundException(NamespaceAndName namespaceAndName) {
+ super(TYPE_REPOSITORY, namespaceAndName.toString());
}
}
diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java
index 1a5300ad21..cea8574d14 100644
--- a/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java
+++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java
@@ -6,13 +6,13 @@
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
+ * this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -26,35 +26,21 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
- *
*/
-
package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.throwingproviders.CheckedProvider;
-import sonia.scm.security.ScmSecurityException;
-
/**
*
* @author Sebastian Sdorra
* @since 1.10
*/
-public interface RepositoryProvider extends CheckedProvider
-{
-
- /**
- * Method description
- *
- *
- * @return
- *
- * @throws ScmSecurityException
- */
+public interface RepositoryProvider extends CheckedProvider {
@Override
- public Repository get() throws ScmSecurityException;
+ Repository get();
}
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java
index 9eb7427a4f..bdd6e4b320 100644
--- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java
+++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java
@@ -31,6 +31,7 @@
package sonia.scm.repository.api;
+import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.cache.CacheManager;
@@ -42,6 +43,8 @@ import sonia.scm.repository.spi.RepositoryServiceProvider;
import java.io.Closeable;
import java.io.IOException;
+import java.util.Set;
+import java.util.stream.Stream;
/**
* From the {@link RepositoryService} it is possible to access all commands for
@@ -78,30 +81,32 @@ import java.io.IOException;
* @apiviz.uses sonia.scm.repository.api.UnbundleCommandBuilder
* @since 1.17
*/
+@Slf4j
public final class RepositoryService implements Closeable {
- private CacheManager cacheManager;
- private PreProcessorUtil preProcessorUtil;
- private RepositoryServiceProvider provider;
- private Repository repository;
- private static final Logger logger =
- LoggerFactory.getLogger(RepositoryService.class);
+
+ private static final Logger logger = LoggerFactory.getLogger(RepositoryService.class);
+
+ private final CacheManager cacheManager;
+ private final PreProcessorUtil preProcessorUtil;
+ private final RepositoryServiceProvider provider;
+ private final Repository repository;
+ private final Set protocolProviders;
/**
* Constructs a new {@link RepositoryService}. This constructor should only
* be called from the {@link RepositoryServiceFactory}.
- *
- * @param cacheManager cache manager
+ * @param cacheManager cache manager
* @param provider implementation for {@link RepositoryServiceProvider}
* @param repository the repository
- * @param preProcessorUtil
*/
RepositoryService(CacheManager cacheManager,
- RepositoryServiceProvider provider, Repository repository,
- PreProcessorUtil preProcessorUtil) {
+ RepositoryServiceProvider provider, Repository repository,
+ PreProcessorUtil preProcessorUtil, Set protocolProviders) {
this.cacheManager = cacheManager;
this.provider = provider;
this.repository = repository;
this.preProcessorUtil = preProcessorUtil;
+ this.protocolProviders = protocolProviders;
}
/**
@@ -125,7 +130,7 @@ public final class RepositoryService implements Closeable {
try {
provider.close();
} catch (IOException ex) {
- logger.error("Could not close repository service provider", ex);
+ log.error("Could not close repository service provider", ex);
}
}
@@ -138,7 +143,7 @@ public final class RepositoryService implements Closeable {
*/
public BlameCommandBuilder getBlameCommand() {
logger.debug("create blame command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new BlameCommandBuilder(cacheManager, provider.getBlameCommand(),
repository, preProcessorUtil);
@@ -153,7 +158,7 @@ public final class RepositoryService implements Closeable {
*/
public BranchesCommandBuilder getBranchesCommand() {
logger.debug("create branches command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new BranchesCommandBuilder(cacheManager,
provider.getBranchesCommand(), repository);
@@ -168,7 +173,7 @@ public final class RepositoryService implements Closeable {
*/
public BrowseCommandBuilder getBrowseCommand() {
logger.debug("create browse command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new BrowseCommandBuilder(cacheManager, provider.getBrowseCommand(),
repository, preProcessorUtil);
@@ -184,7 +189,7 @@ public final class RepositoryService implements Closeable {
*/
public BundleCommandBuilder getBundleCommand() {
logger.debug("create bundle command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new BundleCommandBuilder(provider.getBundleCommand(), repository);
}
@@ -198,7 +203,7 @@ public final class RepositoryService implements Closeable {
*/
public CatCommandBuilder getCatCommand() {
logger.debug("create cat command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new CatCommandBuilder(provider.getCatCommand());
}
@@ -213,7 +218,7 @@ public final class RepositoryService implements Closeable {
*/
public DiffCommandBuilder getDiffCommand() {
logger.debug("create diff command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new DiffCommandBuilder(provider.getDiffCommand());
}
@@ -229,7 +234,7 @@ public final class RepositoryService implements Closeable {
*/
public IncomingCommandBuilder getIncomingCommand() {
logger.debug("create incoming command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new IncomingCommandBuilder(cacheManager,
provider.getIncomingCommand(), repository, preProcessorUtil);
@@ -244,7 +249,7 @@ public final class RepositoryService implements Closeable {
*/
public LogCommandBuilder getLogCommand() {
logger.debug("create log command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new LogCommandBuilder(cacheManager, provider.getLogCommand(),
repository, preProcessorUtil);
@@ -258,7 +263,7 @@ public final class RepositoryService implements Closeable {
* by the implementation of the repository service provider.
*/
public ModificationsCommandBuilder getModificationsCommand() {
- logger.debug("create modifications command for repository {}",repository.getNamespaceAndName());
+ logger.debug("create modifications command for repository {}", repository.getNamespaceAndName());
return new ModificationsCommandBuilder(provider.getModificationsCommand(),repository, cacheManager.getCache(ModificationsCommandBuilder.CACHE_NAME), preProcessorUtil);
}
@@ -272,7 +277,7 @@ public final class RepositoryService implements Closeable {
*/
public OutgoingCommandBuilder getOutgoingCommand() {
logger.debug("create outgoing command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new OutgoingCommandBuilder(cacheManager,
provider.getOutgoingCommand(), repository, preProcessorUtil);
@@ -288,7 +293,7 @@ public final class RepositoryService implements Closeable {
*/
public PullCommandBuilder getPullCommand() {
logger.debug("create pull command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new PullCommandBuilder(provider.getPullCommand(), repository);
}
@@ -303,7 +308,7 @@ public final class RepositoryService implements Closeable {
*/
public PushCommandBuilder getPushCommand() {
logger.debug("create push command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new PushCommandBuilder(provider.getPushCommand());
}
@@ -326,7 +331,7 @@ public final class RepositoryService implements Closeable {
*/
public TagsCommandBuilder getTagsCommand() {
logger.debug("create tags command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new TagsCommandBuilder(cacheManager, provider.getTagsCommand(),
repository);
@@ -342,7 +347,7 @@ public final class RepositoryService implements Closeable {
*/
public UnbundleCommandBuilder getUnbundleCommand() {
logger.debug("create unbundle command for repository {}",
- repository.getName());
+ repository.getNamespaceAndName());
return new UnbundleCommandBuilder(provider.getUnbundleCommand(),
repository);
@@ -369,5 +374,20 @@ public final class RepositoryService implements Closeable {
return provider.getSupportedFeatures().contains(feature);
}
+ public Stream getSupportedProtocols() {
+ return protocolProviders.stream()
+ .filter(protocolProvider -> protocolProvider.getType().equals(getRepository().getType()))
+ .map(this::createProviderInstanceForRepository);
+ }
+ private T createProviderInstanceForRepository(ScmProtocolProvider protocolProvider) {
+ return protocolProvider.get(repository);
+ }
+
+ public T getProtocol(Class clazz) {
+ return this.getSupportedProtocols()
+ .filter(scmProtocol -> clazz.isAssignableFrom(scmProtocol.getClass()))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(String.format("no implementation for %s and repository type %s", clazz.getName(),getRepository().getType())));
+ }
}
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java
index 627e57a797..fbb1ee6b58 100644
--- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java
+++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java
@@ -137,13 +137,15 @@ public final class RepositoryServiceFactory
@Inject
public RepositoryServiceFactory(ScmConfiguration configuration,
CacheManager cacheManager, RepositoryManager repositoryManager,
- Set resolvers, PreProcessorUtil preProcessorUtil)
+ Set resolvers, PreProcessorUtil preProcessorUtil,
+ Set protocolProviders)
{
this.configuration = configuration;
this.cacheManager = cacheManager;
this.repositoryManager = repositoryManager;
this.resolvers = resolvers;
this.preProcessorUtil = preProcessorUtil;
+ this.protocolProviders = protocolProviders;
ScmEventBus.getInstance().register(new CacheClearHook(cacheManager));
}
@@ -208,9 +210,7 @@ public final class RepositoryServiceFactory
if (repository == null)
{
- String msg = "could not find a repository with namespace/name " + namespaceAndName;
-
- throw new RepositoryNotFoundException(msg);
+ throw new RepositoryNotFoundException(namespaceAndName);
}
return create(repository);
@@ -254,7 +254,7 @@ public final class RepositoryServiceFactory
}
service = new RepositoryService(cacheManager, provider, repository,
- preProcessorUtil);
+ preProcessorUtil, protocolProviders);
break;
}
@@ -369,4 +369,6 @@ public final class RepositoryServiceFactory
/** service resolvers */
private final Set resolvers;
+
+ private Set protocolProviders;
}
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocol.java b/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocol.java
new file mode 100644
index 0000000000..c987510491
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocol.java
@@ -0,0 +1,19 @@
+package sonia.scm.repository.api;
+
+/**
+ * An ScmProtocol represents a concrete protocol provided by the SCM-Manager instance
+ * to interact with a repository depending on its type. There may be multiple protocols
+ * available for a repository type (eg. http and ssh).
+ */
+public interface ScmProtocol {
+
+ /**
+ * The type of the concrete protocol, eg. "http" or "ssh".
+ */
+ String getType();
+
+ /**
+ * The URL to access the repository providing this protocol.
+ */
+ String getUrl();
+}
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocolProvider.java b/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocolProvider.java
new file mode 100644
index 0000000000..597826676d
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocolProvider.java
@@ -0,0 +1,12 @@
+package sonia.scm.repository.api;
+
+import sonia.scm.plugin.ExtensionPoint;
+import sonia.scm.repository.Repository;
+
+@ExtensionPoint(multi = true)
+public interface ScmProtocolProvider {
+
+ String getType();
+
+ T get(Repository repository);
+}
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/HttpScmProtocol.java b/scm-core/src/main/java/sonia/scm/repository/spi/HttpScmProtocol.java
new file mode 100644
index 0000000000..b933abf559
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/HttpScmProtocol.java
@@ -0,0 +1,38 @@
+package sonia.scm.repository.spi;
+
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.api.ScmProtocol;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URI;
+
+public abstract class HttpScmProtocol implements ScmProtocol {
+
+ private final Repository repository;
+ private final String basePath;
+
+ public HttpScmProtocol(Repository repository, String basePath) {
+ this.repository = repository;
+ this.basePath = basePath;
+ }
+
+ @Override
+ public String getType() {
+ return "http";
+ }
+
+ @Override
+ public String getUrl() {
+ return URI.create(basePath + "/").resolve(String.format("repo/%s/%s", repository.getNamespace(), repository.getName())).toASCIIString();
+ }
+
+ public final void serve(HttpServletRequest request, HttpServletResponse response, ServletConfig config) throws ServletException, IOException {
+ serve(request, response, repository, config);
+ }
+
+ protected abstract void serve(HttpServletRequest request, HttpServletResponse response, Repository repository, ServletConfig config) throws ServletException, IOException;
+}
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapper.java b/scm-core/src/main/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapper.java
new file mode 100644
index 0000000000..c1b7229036
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapper.java
@@ -0,0 +1,91 @@
+package sonia.scm.repository.spi;
+
+import lombok.extern.slf4j.Slf4j;
+import sonia.scm.api.v2.resources.ScmPathInfoStore;
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.api.ScmProtocolProvider;
+
+import javax.inject.Provider;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Optional;
+
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
+
+@Slf4j
+public abstract class InitializingHttpScmProtocolWrapper implements ScmProtocolProvider {
+
+ private final Provider extends ScmProviderHttpServlet> delegateProvider;
+ private final Provider pathInfoStore;
+ private final ScmConfiguration scmConfiguration;
+
+ private volatile boolean isInitialized = false;
+
+
+ protected InitializingHttpScmProtocolWrapper(Provider extends ScmProviderHttpServlet> delegateProvider, Provider pathInfoStore, ScmConfiguration scmConfiguration) {
+ this.delegateProvider = delegateProvider;
+ this.pathInfoStore = pathInfoStore;
+ this.scmConfiguration = scmConfiguration;
+ }
+
+ protected void initializeServlet(ServletConfig config, ScmProviderHttpServlet httpServlet) throws ServletException {
+ httpServlet.init(config);
+ }
+
+ @Override
+ public HttpScmProtocol get(Repository repository) {
+ if (!repository.getType().equals(getType())) {
+ throw new IllegalArgumentException(String.format("cannot handle repository with type %s with protocol for type %s", repository.getType(), getType()));
+ }
+ return new ProtocolWrapper(repository, computeBasePath());
+ }
+
+ private String computeBasePath() {
+ return getPathFromScmPathInfoIfAvailable().orElse(getPathFromConfiguration());
+ }
+
+ private Optional getPathFromScmPathInfoIfAvailable() {
+ try {
+ ScmPathInfoStore scmPathInfoStore = pathInfoStore.get();
+ if (scmPathInfoStore != null && scmPathInfoStore.get() != null) {
+ return of(scmPathInfoStore.get().getRootUri().toASCIIString());
+ }
+ } catch (Exception e) {
+ log.debug("could not get ScmPathInfoStore from context", e);
+ }
+ return empty();
+ }
+
+ private String getPathFromConfiguration() {
+ log.debug("using base path from configuration: {}", scmConfiguration.getBaseUrl());
+ return scmConfiguration.getBaseUrl();
+ }
+
+ private class ProtocolWrapper extends HttpScmProtocol {
+
+ public ProtocolWrapper(Repository repository, String basePath) {
+ super(repository, basePath);
+ }
+
+ @Override
+ protected void serve(HttpServletRequest request, HttpServletResponse response, Repository repository, ServletConfig config) throws ServletException, IOException {
+ if (!isInitialized) {
+ synchronized (InitializingHttpScmProtocolWrapper.this) {
+ if (!isInitialized) {
+ ScmProviderHttpServlet httpServlet = delegateProvider.get();
+ initializeServlet(config, httpServlet);
+ isInitialized = true;
+ }
+ }
+ }
+
+ delegateProvider.get().service(request, response, repository);
+ }
+
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java b/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java
index 2f436836cd..c66c56c0f1 100644
--- a/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java
@@ -33,8 +33,6 @@
package sonia.scm.repository.spi;
-//~--- non-JDK imports --------------------------------------------------------
-
import sonia.scm.repository.Feature;
import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.CommandNotSupportedException;
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServlet.java b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServlet.java
new file mode 100644
index 0000000000..3a9dad52d6
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServlet.java
@@ -0,0 +1,16 @@
+package sonia.scm.repository.spi;
+
+import sonia.scm.repository.Repository;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public interface ScmProviderHttpServlet {
+
+ void service(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException;
+
+ void init(ServletConfig config) throws ServletException;
+}
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletDecorator.java b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletDecorator.java
new file mode 100644
index 0000000000..c5dd55d277
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletDecorator.java
@@ -0,0 +1,28 @@
+package sonia.scm.repository.spi;
+
+import sonia.scm.repository.Repository;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class ScmProviderHttpServletDecorator implements ScmProviderHttpServlet {
+
+ private final ScmProviderHttpServlet object;
+
+ public ScmProviderHttpServletDecorator(ScmProviderHttpServlet object) {
+ this.object = object;
+ }
+
+ @Override
+ public void service(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException {
+ object.service(request, response, repository);
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ object.init(config);
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletDecoratorFactory.java b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletDecoratorFactory.java
new file mode 100644
index 0000000000..531a25e91d
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletDecoratorFactory.java
@@ -0,0 +1,15 @@
+package sonia.scm.repository.spi;
+
+import sonia.scm.DecoratorFactory;
+import sonia.scm.plugin.ExtensionPoint;
+
+@ExtensionPoint
+public interface ScmProviderHttpServletDecoratorFactory extends DecoratorFactory {
+ /**
+ * Has to return true if this factory provides a decorator for the given scm type (eg. "git", "hg" or
+ * "svn").
+ * @param type The current scm type this factory can provide a decorator for.
+ * @return true when the provided decorator should be used for the given scm type.
+ */
+ boolean handlesScmType(String type);
+}
diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletProvider.java b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletProvider.java
new file mode 100644
index 0000000000..3793d4b935
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/repository/spi/ScmProviderHttpServletProvider.java
@@ -0,0 +1,33 @@
+package sonia.scm.repository.spi;
+
+import com.google.inject.Inject;
+import sonia.scm.util.Decorators;
+
+import javax.inject.Provider;
+import java.util.List;
+import java.util.Set;
+
+import static java.util.stream.Collectors.toList;
+
+public abstract class ScmProviderHttpServletProvider implements Provider {
+
+ @Inject(optional = true)
+ private Set decoratorFactories;
+
+ private final String type;
+
+ protected ScmProviderHttpServletProvider(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public ScmProviderHttpServlet get() {
+ return Decorators.decorate(getRootServlet(), getDecoratorsForType());
+ }
+
+ private List getDecoratorsForType() {
+ return decoratorFactories.stream().filter(d -> d.handlesScmType(type)).collect(toList());
+ }
+
+ protected abstract ScmProviderHttpServlet getRootServlet();
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/util/Decorators.java b/scm-core/src/main/java/sonia/scm/util/Decorators.java
similarity index 99%
rename from scm-webapp/src/main/java/sonia/scm/util/Decorators.java
rename to scm-core/src/main/java/sonia/scm/util/Decorators.java
index 41c75660a9..6465631d03 100644
--- a/scm-webapp/src/main/java/sonia/scm/util/Decorators.java
+++ b/scm-core/src/main/java/sonia/scm/util/Decorators.java
@@ -37,7 +37,6 @@ package sonia.scm.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import sonia.scm.DecoratorFactory;
/**
diff --git a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java
index dd3c82e800..328494a626 100644
--- a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java
+++ b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java
@@ -33,39 +33,32 @@
package sonia.scm.web.filter;
-//~--- non-JDK imports --------------------------------------------------------
-
-import com.google.common.base.Splitter;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sonia.scm.ArgumentIsInvalidException;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryPermissions;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.repository.spi.ScmProviderHttpServletDecorator;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.HttpUtil;
-import sonia.scm.util.Util;
-import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
-import java.util.Iterator;
-
-//~--- JDK imports ------------------------------------------------------------
/**
* Abstract http filter to check repository permissions.
*
* @author Sebastian Sdorra
*/
-public abstract class PermissionFilter extends HttpFilter
+public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
{
/** the logger for PermissionFilter */
@@ -81,23 +74,14 @@ public abstract class PermissionFilter extends HttpFilter
*
* @since 1.21
*/
- public PermissionFilter(ScmConfiguration configuration)
+ protected PermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate)
{
+ super(delegate);
this.configuration = configuration;
}
//~--- get methods ----------------------------------------------------------
- /**
- * Returns the requested repository.
- *
- *
- * @param request current http request
- *
- * @return requested repository
- */
- protected abstract Repository getRepository(HttpServletRequest request);
-
/**
* Returns true if the current request is a write request.
*
@@ -117,66 +101,38 @@ public abstract class PermissionFilter extends HttpFilter
*
* @param request http request
* @param response http response
- * @param chain filter chain
*
* @throws IOException
* @throws ServletException
*/
@Override
- protected void doFilter(HttpServletRequest request,
- HttpServletResponse response, FilterChain chain)
+ public void service(HttpServletRequest request,
+ HttpServletResponse response, Repository repository)
throws IOException, ServletException
{
Subject subject = SecurityUtils.getSubject();
try
{
- Repository repository = getRepository(request);
+ boolean writeRequest = isWriteRequest(request);
- if (repository != null)
+ if (hasPermission(repository, writeRequest))
{
- boolean writeRequest = isWriteRequest(request);
+ logger.trace("{} access to repository {} for user {} granted",
+ getActionAsString(writeRequest), repository.getName(),
+ getUserName(subject));
- if (hasPermission(repository, writeRequest))
- {
- logger.trace("{} access to repository {} for user {} granted",
- getActionAsString(writeRequest), repository.getName(),
- getUserName(subject));
-
- chain.doFilter(request, response);
- }
- else
- {
- logger.info("{} access to repository {} for user {} denied",
- getActionAsString(writeRequest), repository.getName(),
- getUserName(subject));
-
- sendAccessDenied(request, response, subject);
- }
+ super.service(request, response, repository);
}
else
{
- logger.debug("repository not found");
+ logger.info("{} access to repository {} for user {} denied",
+ getActionAsString(writeRequest), repository.getName(),
+ getUserName(subject));
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ sendAccessDenied(request, response, subject);
}
}
- catch (ArgumentIsInvalidException ex)
- {
- if (logger.isTraceEnabled())
- {
- logger.trace(
- "wrong request at ".concat(request.getRequestURI()).concat(
- " send redirect"), ex);
- }
- else if (logger.isWarnEnabled())
- {
- logger.warn("wrong request at {} send redirect",
- request.getRequestURI());
- }
-
- response.sendRedirect(getRepositoryRootHelpUrl(request));
- }
catch (ScmSecurityException | AuthorizationException ex)
{
logger.warn("user " + subject.getPrincipal() + " has not enough permissions", ex);
@@ -217,29 +173,6 @@ public abstract class PermissionFilter extends HttpFilter
HttpUtil.sendUnauthorized(response, configuration.getRealmDescription());
}
- /**
- * Extracts the type of the repositroy from url.
- *
- *
- * @param request http request
- *
- * @return type of repository
- */
- private String extractType(HttpServletRequest request)
- {
- Iterator it = Splitter.on(
- HttpUtil.SEPARATOR_PATH).omitEmptyStrings().split(
- request.getRequestURI()).iterator();
- String type = it.next();
-
- if (Util.isNotEmpty(request.getContextPath()))
- {
- type = it.next();
- }
-
- return type;
- }
-
/**
* Send access denied to the servlet response.
*
@@ -280,25 +213,6 @@ public abstract class PermissionFilter extends HttpFilter
: "read";
}
- /**
- * Returns the repository root help url.
- *
- *
- * @param request current http request
- *
- * @return repository root help url
- */
- private String getRepositoryRootHelpUrl(HttpServletRequest request)
- {
- String type = extractType(request);
- String helpUrl = HttpUtil.getCompleteUrl(request,
- "/api/rest/help/repository-root/");
-
- helpUrl = helpUrl.concat(type).concat(".html");
-
- return helpUrl;
- }
-
/**
* Returns the username from the given subject or anonymous.
*
diff --git a/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java
deleted file mode 100644
index ea0d90c915..0000000000
--- a/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * Copyright (c) 2010, Sebastian Sdorra
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of SCM-Manager; nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * http://bitbucket.org/sdorra/scm-manager
- *
- */
-
-
-
-package sonia.scm.web.filter;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import com.google.common.base.Throwables;
-import com.google.inject.ProvisionException;
-
-import org.apache.shiro.authz.AuthorizationException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import sonia.scm.config.ScmConfiguration;
-import sonia.scm.repository.Repository;
-import sonia.scm.repository.RepositoryProvider;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- *
- * @author Sebastian Sdorra
- * @since 1.9
- */
-public abstract class ProviderPermissionFilter extends PermissionFilter
-{
-
- /**
- * the logger for ProviderPermissionFilter
- */
- private static final Logger logger =
- LoggerFactory.getLogger(ProviderPermissionFilter.class);
-
- //~--- constructors ---------------------------------------------------------
-
- /**
- * Constructs ...
- *
- *
- * @param configuration
- * @param repositoryProvider
- * @since 1.21
- */
- public ProviderPermissionFilter(ScmConfiguration configuration,
- RepositoryProvider repositoryProvider)
- {
- super(configuration);
- this.repositoryProvider = repositoryProvider;
- }
-
- //~--- get methods ----------------------------------------------------------
-
- /**
- * Method description
- *
- *
- * @param request
- *
- * @return
- */
- @Override
- protected Repository getRepository(HttpServletRequest request)
- {
- Repository repository = null;
-
- try
- {
- repository = repositoryProvider.get();
- }
- catch (ProvisionException ex)
- {
- Throwables.propagateIfPossible(ex.getCause(),
- IllegalStateException.class, AuthorizationException.class);
- logger.error("could not get repository from request", ex);
- }
-
- return repository;
- }
-
- //~--- fields ---------------------------------------------------------------
-
- /** Field description */
- private final RepositoryProvider repositoryProvider;
-}
diff --git a/scm-core/src/test/java/sonia/scm/repository/RepositoryTest.java b/scm-core/src/test/java/sonia/scm/repository/RepositoryTest.java
deleted file mode 100644
index f13f4cbc67..0000000000
--- a/scm-core/src/test/java/sonia/scm/repository/RepositoryTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Copyright (c) 2010, Sebastian Sdorra
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of SCM-Manager; nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * http://bitbucket.org/sdorra/scm-manager
- *
- */
-
-
-package sonia.scm.repository;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- *
- * @author Sebastian Sdorra
- */
-public class RepositoryTest
-{
-
- /**
- * Method description
- *
- */
- @Test
- public void testCreateUrl()
- {
- Repository repository = new Repository("123", "hg", "test", "repo");
-
- assertEquals("http://localhost:8080/scm/hg/test/repo",
- repository.createUrl("http://localhost:8080/scm"));
- assertEquals("http://localhost:8080/scm/hg/test/repo",
- repository.createUrl("http://localhost:8080/scm/"));
- }
-}
diff --git a/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java
new file mode 100644
index 0000000000..2ceafc19bb
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java
@@ -0,0 +1,74 @@
+package sonia.scm.repository.api;
+
+import org.junit.Test;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.spi.HttpScmProtocol;
+import sonia.scm.repository.spi.RepositoryServiceProvider;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.util.IterableUtil.sizeOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+
+public class RepositoryServiceTest {
+
+ private final RepositoryServiceProvider provider = mock(RepositoryServiceProvider.class);
+ private final Repository repository = new Repository("", "git", "space", "repo");
+
+ @Test
+ public void shouldReturnMatchingProtocolsFromProvider() {
+ RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()));
+ Stream supportedProtocols = repositoryService.getSupportedProtocols();
+
+ assertThat(sizeOf(supportedProtocols.collect(Collectors.toList()))).isEqualTo(1);
+ }
+
+ @Test
+ public void shouldFindKnownProtocol() {
+ RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()));
+
+ HttpScmProtocol protocol = repositoryService.getProtocol(HttpScmProtocol.class);
+
+ assertThat(protocol).isNotNull();
+ }
+
+ @Test
+ public void shouldFailForUnknownProtocol() {
+ RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()));
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ repositoryService.getProtocol(UnknownScmProtocol.class);
+ });
+ }
+
+ private static class DummyHttpProtocol extends HttpScmProtocol {
+ public DummyHttpProtocol(Repository repository) {
+ super(repository, "");
+ }
+
+ @Override
+ public void serve(HttpServletRequest request, HttpServletResponse response, Repository repository, ServletConfig config) {
+ }
+ }
+
+ private static class DummyScmProtocolProvider implements ScmProtocolProvider {
+ @Override
+ public String getType() {
+ return "git";
+ }
+
+ @Override
+ public ScmProtocol get(Repository repository) {
+ return new DummyHttpProtocol(repository);
+ }
+ }
+
+ private interface UnknownScmProtocol extends ScmProtocol {}
+}
diff --git a/scm-core/src/test/java/sonia/scm/repository/spi/HttpScmProtocolTest.java b/scm-core/src/test/java/sonia/scm/repository/spi/HttpScmProtocolTest.java
new file mode 100644
index 0000000000..1fd772fee3
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/repository/spi/HttpScmProtocolTest.java
@@ -0,0 +1,40 @@
+package sonia.scm.repository.spi;
+
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.TestFactory;
+import sonia.scm.repository.Repository;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class HttpScmProtocolTest {
+
+ @TestFactory
+ Stream shouldCreateCorrectUrlsWithContextPath() {
+ return Stream.of("http://localhost/scm", "http://localhost/scm/")
+ .map(url -> assertResultingUrl(url, "http://localhost/scm/repo/space/name"));
+ }
+
+ @TestFactory
+ Stream shouldCreateCorrectUrlsWithPort() {
+ return Stream.of("http://localhost:8080", "http://localhost:8080/")
+ .map(url -> assertResultingUrl(url, "http://localhost:8080/repo/space/name"));
+ }
+
+ DynamicTest assertResultingUrl(String baseUrl, String expectedUrl) {
+ String actualUrl = createInstanceOfHttpScmProtocol(baseUrl).getUrl();
+ return DynamicTest.dynamicTest(baseUrl + " -> " + expectedUrl, () -> assertThat(actualUrl).isEqualTo(expectedUrl));
+ }
+
+ private HttpScmProtocol createInstanceOfHttpScmProtocol(String baseUrl) {
+ return new HttpScmProtocol(new Repository("", "", "space", "name"), baseUrl) {
+ @Override
+ protected void serve(HttpServletRequest request, HttpServletResponse response, Repository repository, ServletConfig config) {
+ }
+ };
+ }
+}
diff --git a/scm-core/src/test/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapperTest.java b/scm-core/src/test/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapperTest.java
new file mode 100644
index 0000000000..8c910f92a9
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapperTest.java
@@ -0,0 +1,120 @@
+package sonia.scm.repository.spi;
+
+import com.google.inject.ProvisionException;
+import com.google.inject.util.Providers;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.stubbing.OngoingStubbing;
+import sonia.scm.api.v2.resources.ScmPathInfo;
+import sonia.scm.api.v2.resources.ScmPathInfoStore;
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.repository.Repository;
+
+import javax.inject.Provider;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URI;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+public class InitializingHttpScmProtocolWrapperTest {
+
+ private static final Repository REPOSITORY = new Repository("", "git", "space", "name");
+
+ @Mock
+ private ScmProviderHttpServlet delegateServlet;
+ @Mock
+ private ScmPathInfoStore pathInfoStore;
+ @Mock
+ private ScmConfiguration scmConfiguration;
+ private Provider pathInfoStoreProvider;
+
+ @Mock
+ private HttpServletRequest request;
+ @Mock
+ private HttpServletResponse response;
+ @Mock
+ private ServletConfig servletConfig;
+
+ private InitializingHttpScmProtocolWrapper wrapper;
+
+ @Before
+ public void init() {
+ initMocks(this);
+ pathInfoStoreProvider = mock(Provider.class);
+ when(pathInfoStoreProvider.get()).thenReturn(pathInfoStore);
+
+ wrapper = new InitializingHttpScmProtocolWrapper(Providers.of(this.delegateServlet), pathInfoStoreProvider, scmConfiguration) {
+ @Override
+ public String getType() {
+ return "git";
+ }
+ };
+ when(scmConfiguration.getBaseUrl()).thenReturn("http://example.com/scm");
+ }
+
+ @Test
+ public void shouldUsePathFromPathInfo() {
+ mockSetPathInfo();
+
+ HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY);
+
+ assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl());
+ }
+
+ @Test
+ public void shouldUseConfigurationWhenPathInfoNotSet() {
+ HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY);
+
+ assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl());
+ }
+
+ @Test
+ public void shouldUseConfigurationWhenNotInRequestScope() {
+ when(pathInfoStoreProvider.get()).thenThrow(new ProvisionException("test"));
+
+ HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY);
+
+ assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl());
+ }
+
+ @Test
+ public void shouldInitializeAndDelegateRequestThroughFilter() throws ServletException, IOException {
+ HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY);
+
+ httpScmProtocol.serve(request, response, servletConfig);
+
+ verify(delegateServlet).init(servletConfig);
+ verify(delegateServlet).service(request, response, REPOSITORY);
+ }
+
+ @Test
+ public void shouldInitializeOnlyOnce() throws ServletException, IOException {
+ HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY);
+
+ httpScmProtocol.serve(request, response, servletConfig);
+ httpScmProtocol.serve(request, response, servletConfig);
+
+ verify(delegateServlet, times(1)).init(servletConfig);
+ verify(delegateServlet, times(2)).service(request, response, REPOSITORY);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldFailForIllegalScmType() {
+ HttpScmProtocol httpScmProtocol = wrapper.get(new Repository("", "other", "space", "name"));
+ }
+
+ private OngoingStubbing mockSetPathInfo() {
+ return when(pathInfoStore.get()).thenReturn(() -> URI.create("http://example.com/scm/api/rest/"));
+ }
+
+}
diff --git a/scm-core/src/test/java/sonia/scm/web/filter/PermissionFilterTest.java b/scm-core/src/test/java/sonia/scm/web/filter/PermissionFilterTest.java
new file mode 100644
index 0000000000..9fa65d51b8
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/web/filter/PermissionFilterTest.java
@@ -0,0 +1,74 @@
+package sonia.scm.web.filter;
+
+import com.github.sdorra.shiro.ShiroRule;
+import com.github.sdorra.shiro.SubjectAware;
+import org.junit.Rule;
+import org.junit.Test;
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+@SubjectAware(configuration = "classpath:sonia/scm/shiro.ini")
+public class PermissionFilterTest {
+
+ public static final Repository REPOSITORY = new Repository("1", "git", "space", "name");
+
+ @Rule
+ public final ShiroRule shiroRule = new ShiroRule();
+
+ private final ScmProviderHttpServlet delegateServlet = mock(ScmProviderHttpServlet.class);
+
+ private final PermissionFilter permissionFilter = new PermissionFilter(new ScmConfiguration(), delegateServlet) {
+ @Override
+ protected boolean isWriteRequest(HttpServletRequest request) {
+ return writeRequest;
+ }
+ };
+
+ private final HttpServletRequest request = mock(HttpServletRequest.class);
+ private final HttpServletResponse response = mock(HttpServletResponse.class);
+
+ private boolean writeRequest = false;
+
+ @Test
+ @SubjectAware(username = "reader", password = "secret")
+ public void shouldPassForReaderOnReadRequest() throws IOException, ServletException {
+ writeRequest = false;
+
+ permissionFilter.service(request, response, REPOSITORY);
+
+ verify(delegateServlet).service(request, response, REPOSITORY);
+ }
+
+ @Test
+ @SubjectAware(username = "reader", password = "secret")
+ public void shouldBlockForReaderOnWriteRequest() throws IOException, ServletException {
+ writeRequest = true;
+
+ permissionFilter.service(request, response, REPOSITORY);
+
+ verify(response).sendError(eq(401), anyString());
+ verify(delegateServlet, never()).service(request, response, REPOSITORY);
+ }
+
+ @Test
+ @SubjectAware(username = "writer", password = "secret")
+ public void shouldPassForWriterOnWriteRequest() throws IOException, ServletException {
+ writeRequest = true;
+
+ permissionFilter.service(request, response, REPOSITORY);
+
+ verify(delegateServlet).service(request, response, REPOSITORY);
+ }
+}
diff --git a/scm-core/src/test/resources/sonia/scm/shiro.ini b/scm-core/src/test/resources/sonia/scm/shiro.ini
index e87c81b097..fbdd35ba50 100644
--- a/scm-core/src/test/resources/sonia/scm/shiro.ini
+++ b/scm-core/src/test/resources/sonia/scm/shiro.ini
@@ -1,6 +1,12 @@
[users]
trillian = secret, user
+admin = secret, admin
+writer = secret, repo_write
+reader = secret, repo_read
+unpriv = secret
[roles]
admin = *
-user = something:*
\ No newline at end of file
+user = something:*
+repo_read = "repository:read:1"
+repo_write = "repository:push:1"
diff --git a/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java b/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java
index 2987780bcb..11db0200f1 100644
--- a/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java
+++ b/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java
@@ -31,7 +31,7 @@ public class RepositoryUtil {
public static RepositoryClient createRepositoryClient(String repositoryType, File folder, String username, String password) throws IOException {
String httpProtocolUrl = TestData.callRepository(username, password, repositoryType, HttpStatus.SC_OK)
.extract()
- .path("_links.httpProtocol.href");
+ .path("_links.protocol.find{it.name=='http'}.href");
return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, username, password, folder);
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapper.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapper.java
index 7163497487..7607b31faf 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapper.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapper.java
@@ -18,7 +18,7 @@ import static de.otto.edison.hal.Links.linkingTo;
public abstract class GitConfigToGitConfigDtoMapper extends BaseMapper {
@Inject
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@AfterMapping
void appendLinks(GitConfig config, @MappingTarget GitConfigDto target) {
@@ -30,12 +30,12 @@ public abstract class GitConfigToGitConfigDtoMapper extends BaseMapper webTokenGenerators)
- {
- super(configuration, webTokenGenerators);
- }
-}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java
index 1f07753f1e..e38f26a309 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java
@@ -33,38 +33,24 @@
package sonia.scm.web;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.common.annotations.VisibleForTesting;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
import org.eclipse.jgit.http.server.GitSmartHttpTools;
-
import sonia.scm.ClientMessages;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.GitUtil;
-import sonia.scm.repository.RepositoryProvider;
-import sonia.scm.web.filter.ProviderPermissionFilter;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.IOException;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.web.filter.PermissionFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import sonia.scm.Priority;
-import sonia.scm.filter.Filters;
-import sonia.scm.filter.WebElement;
+import java.io.IOException;
/**
* GitPermissionFilter decides if a git request requires write or read privileges.
*
* @author Sebastian Sdorra
*/
-@Priority(Filters.PRIORITY_AUTHORIZATION)
-@WebElement(value = GitServletModule.PATTERN_GIT)
-public class GitPermissionFilter extends ProviderPermissionFilter
+public class GitPermissionFilter extends PermissionFilter
{
private static final String PARAMETER_SERVICE = "service";
@@ -83,11 +69,9 @@ public class GitPermissionFilter extends ProviderPermissionFilter
* Constructs a new instance of the GitPermissionFilter.
*
* @param configuration scm main configuration
- * @param repositoryProvider repository provider
*/
- @Inject
- public GitPermissionFilter(ScmConfiguration configuration, RepositoryProvider repositoryProvider) {
- super(configuration, repositoryProvider);
+ public GitPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate) {
+ super(configuration, delegate);
}
@Override
@@ -103,7 +87,7 @@ public class GitPermissionFilter extends ProviderPermissionFilter
}
@Override
- protected boolean isWriteRequest(HttpServletRequest request) {
+ public boolean isWriteRequest(HttpServletRequest request) {
return isReceivePackRequest(request) ||
isReceiveServiceRequest(request) ||
isLfsFileUpload(request);
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilterFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilterFactory.java
new file mode 100644
index 0000000000..c358da5fb1
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilterFactory.java
@@ -0,0 +1,30 @@
+package sonia.scm.web;
+
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.plugin.Extension;
+import sonia.scm.repository.GitRepositoryHandler;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.repository.spi.ScmProviderHttpServletDecoratorFactory;
+
+import javax.inject.Inject;
+
+@Extension
+public class GitPermissionFilterFactory implements ScmProviderHttpServletDecoratorFactory {
+
+ private final ScmConfiguration configuration;
+
+ @Inject
+ public GitPermissionFilterFactory(ScmConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public boolean handlesScmType(String type) {
+ return GitRepositoryHandler.TYPE_NAME.equals(type);
+ }
+
+ @Override
+ public ScmProviderHttpServlet createDecorator(ScmProviderHttpServlet delegate) {
+ return new GitPermissionFilter(configuration, delegate);
+ }
+}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java
index 76e742a71a..7f04bb3a54 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java
@@ -125,8 +125,9 @@ public class GitRepositoryResolver implements RepositoryResolver uriInfoStore, ScmConfiguration scmConfiguration) {
+ super(servletProvider, uriInfoStore, scmConfiguration);
+ }
+
+ @Override
+ public String getType() {
+ return GitRepositoryHandler.TYPE_NAME;
+ }
+}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java
index bdad103c15..e731e01a62 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java
@@ -51,18 +51,6 @@ import sonia.scm.web.lfs.LfsBlobStoreFactory;
public class GitServletModule extends ServletModule
{
- public static final String GIT_PATH = "/git";
-
- /** Field description */
- public static final String PATTERN_GIT = GIT_PATH + "/*";
-
-
- //~--- methods --------------------------------------------------------------
-
- /**
- * Method description
- *
- */
@Override
protected void configureServlets()
{
@@ -75,8 +63,5 @@ public class GitServletModule extends ServletModule
bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass());
bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass());
-
- // serlvelts and filters
- serve(PATTERN_GIT).with(ScmGitServlet.class);
}
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java
index 5612a64652..2701764607 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java
@@ -33,8 +33,6 @@
package sonia.scm.web;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -42,8 +40,8 @@ import org.eclipse.jgit.http.server.GitServlet;
import org.eclipse.jgit.lfs.lib.Constants;
import org.slf4j.Logger;
import sonia.scm.repository.Repository;
-import sonia.scm.repository.RepositoryProvider;
import sonia.scm.repository.RepositoryRequestListenerUtil;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
import sonia.scm.util.HttpUtil;
import sonia.scm.web.lfs.servlet.LfsServletFactory;
@@ -57,19 +55,18 @@ import java.util.regex.Pattern;
import static org.eclipse.jgit.lfs.lib.Constants.CONTENT_TYPE_GIT_LFS_JSON;
import static org.slf4j.LoggerFactory.getLogger;
-//~--- JDK imports ------------------------------------------------------------
-
/**
*
* @author Sebastian Sdorra
*/
@Singleton
-public class ScmGitServlet extends GitServlet
+public class ScmGitServlet extends GitServlet implements ScmProviderHttpServlet
{
- /** Field description */
+ public static final String REPO_PATH = "/repo";
+
public static final Pattern REGEX_GITHTTPBACKEND = Pattern.compile(
- "(?x)^/git/(.*/(HEAD|info/refs|objects/(info/[^/]+|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\\.(pack|idx))|git-(upload|receive)-pack))$"
+ "(?x)^/repo/(.*/(HEAD|info/refs|objects/(info/[^/]+|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\\.(pack|idx))|git-(upload|receive)-pack))$"
);
/** Field description */
@@ -88,7 +85,6 @@ public class ScmGitServlet extends GitServlet
* @param repositoryResolver
* @param receivePackFactory
* @param repositoryViewer
- * @param repositoryProvider
* @param repositoryRequestListenerUtil
* @param lfsServletFactory
*/
@@ -96,11 +92,9 @@ public class ScmGitServlet extends GitServlet
public ScmGitServlet(GitRepositoryResolver repositoryResolver,
GitReceivePackFactory receivePackFactory,
GitRepositoryViewer repositoryViewer,
- RepositoryProvider repositoryProvider,
RepositoryRequestListenerUtil repositoryRequestListenerUtil,
LfsServletFactory lfsServletFactory)
{
- this.repositoryProvider = repositoryProvider;
this.repositoryViewer = repositoryViewer;
this.repositoryRequestListenerUtil = repositoryRequestListenerUtil;
this.lfsServletFactory = lfsServletFactory;
@@ -122,44 +116,9 @@ public class ScmGitServlet extends GitServlet
* @throws ServletException
*/
@Override
- protected void service(HttpServletRequest request,
- HttpServletResponse response)
+ public void service(HttpServletRequest request, HttpServletResponse response, Repository repository)
throws ServletException, IOException
{
- Repository repository = repositoryProvider.get();
- if (repository != null) {
- handleRequest(request, response, repository);
- } else {
- // logger
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- }
- }
-
- /**
- * Decides the type request being currently made and delegates it accordingly.
- *
- * - Batch API:
- *
- * - used to provide the client with information on how handle the large files of a repository.
- * - response contains the information where to perform the actual upload and download of the large objects.
- *
- * - Transfer API:
- *
- * - receives and provides the actual large objects (resolves the pointer placed in the file of the working copy).
- * - invoked only after the Batch API has been questioned about what to do with the large files
- *
- * - Regular Git Http API:
- *
- * - regular git http wire protocol, use by normal git clients.
- *
- * - Browser Overview:
-
- *
- * - short repository overview for browser clients.
- *
- *
- *
- */
- private void handleRequest(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException {
String repoPath = repository.getNamespace() + "/" + repository.getName();
logger.trace("handle git repository at {}", repoPath);
if (isLfsBatchApiRequest(request, repoPath)) {
@@ -210,7 +169,7 @@ public class ScmGitServlet extends GitServlet
* @throws IOException
* @throws ServletException
*/
- private void handleBrowserRequest(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException {
+ private void handleBrowserRequest(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException {
try {
repositoryViewer.handleRequest(request, response, repository);
} catch (IOException ex) {
@@ -229,7 +188,7 @@ public class ScmGitServlet extends GitServlet
*/
private static boolean isLfsFileTransferRequest(HttpServletRequest request, String repository) {
- String regex = String.format("^%s%s/%s(\\.git)?/info/lfs/objects/[a-z0-9]{64}$", request.getContextPath(), GitServletModule.GIT_PATH, repository);
+ String regex = String.format("^%s%s/%s(\\.git)?/info/lfs/objects/[a-z0-9]{64}$", request.getContextPath(), REPO_PATH, repository);
boolean pathMatches = request.getRequestURI().matches(regex);
boolean methodMatches = request.getMethod().equals("PUT") || request.getMethod().equals("GET");
@@ -248,7 +207,7 @@ public class ScmGitServlet extends GitServlet
*/
private static boolean isLfsBatchApiRequest(HttpServletRequest request, String repository) {
- String regex = String.format("^%s%s/%s(\\.git)?/info/lfs/objects/batch$", request.getContextPath(), GitServletModule.GIT_PATH, repository);
+ String regex = String.format("^%s%s/%s(\\.git)?/info/lfs/objects/batch$", request.getContextPath(), REPO_PATH, repository);
boolean pathMatches = request.getRequestURI().matches(regex);
boolean methodMatches = "POST".equals(request.getMethod());
@@ -284,12 +243,8 @@ public class ScmGitServlet extends GitServlet
return false;
}
-
//~--- fields ---------------------------------------------------------------
- /** Field description */
- private final RepositoryProvider repositoryProvider;
-
/** Field description */
private final RepositoryRequestListenerUtil repositoryRequestListenerUtil;
@@ -299,5 +254,4 @@ public class ScmGitServlet extends GitServlet
private final GitRepositoryViewer repositoryViewer;
private final LfsServletFactory lfsServletFactory;
-
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServletProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServletProvider.java
new file mode 100644
index 0000000000..56a9e358be
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServletProvider.java
@@ -0,0 +1,23 @@
+package sonia.scm.web;
+
+import com.google.inject.Inject;
+import sonia.scm.repository.GitRepositoryHandler;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.repository.spi.ScmProviderHttpServletProvider;
+
+import javax.inject.Provider;
+
+public class ScmGitServletProvider extends ScmProviderHttpServletProvider {
+
+ @Inject
+ private Provider servletProvider;
+
+ public ScmGitServletProvider() {
+ super(GitRepositoryHandler.TYPE_NAME);
+ }
+
+ @Override
+ protected ScmProviderHttpServlet getRootServlet() {
+ return servletProvider.get();
+ }
+}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java
index 58bdb2fcf1..f4eed34678 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java
@@ -70,7 +70,7 @@ public class LfsServletFactory {
*/
@VisibleForTesting
static String buildBaseUri(Repository repository, HttpServletRequest request) {
- return String.format("%s/git/%s/%s.git/info/lfs/objects/", HttpUtil.getCompleteUrl(request), repository.getNamespace(), repository.getName());
+ return String.format("%s/repo/%s/%s.git/info/lfs/objects/", HttpUtil.getCompleteUrl(request), repository.getNamespace(), repository.getName());
}
}
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java
index 42790ea7a4..1ebe7fc98b 100644
--- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java
@@ -53,7 +53,7 @@ public class GitConfigResourceTest {
private GitConfigDtoToGitConfigMapperImpl dtoToConfigMapper;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private GitConfigToGitConfigDtoMapperImpl configToDtoMapper;
@@ -67,7 +67,7 @@ public class GitConfigResourceTest {
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
GitConfigResource gitConfigResource = new GitConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler);
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
}
@Test
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java
index 51fded4839..82c85029a3 100644
--- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java
@@ -28,7 +28,7 @@ public class GitConfigToGitConfigDtoMapperTest {
private URI baseUri = URI.create("http://example.com/base/");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private GitConfigToGitConfigDtoMapperImpl mapper;
@@ -40,7 +40,7 @@ public class GitConfigToGitConfigDtoMapperTest {
@Before
public void init() {
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
expectedBaseUri = baseUri.resolve(GitConfigResource.GIT_CONFIG_PATH_V2);
subjectThreadState.bind();
ThreadContext.bind(subject);
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitPermissionFilterTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitPermissionFilterTest.java
index fede8842a9..831402261b 100644
--- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitPermissionFilterTest.java
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitPermissionFilterTest.java
@@ -5,7 +5,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.config.ScmConfiguration;
-import sonia.scm.repository.RepositoryProvider;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
import sonia.scm.util.HttpUtil;
import javax.servlet.ServletOutputStream;
@@ -29,12 +29,7 @@ import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class GitPermissionFilterTest {
- @Mock
- private RepositoryProvider repositoryProvider;
-
- private final GitPermissionFilter permissionFilter = new GitPermissionFilter(
- new ScmConfiguration(), repositoryProvider
- );
+ private final GitPermissionFilter permissionFilter = new GitPermissionFilter(new ScmConfiguration(), mock(ScmProviderHttpServlet.class));
@Mock
private HttpServletResponse response;
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/servlet/LfsServletFactoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/servlet/LfsServletFactoryTest.java
index f6f5803bb7..09a431ea43 100644
--- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/servlet/LfsServletFactoryTest.java
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/servlet/LfsServletFactoryTest.java
@@ -23,12 +23,12 @@ public class LfsServletFactoryTest {
String repositoryName = "git-lfs-demo";
String result = LfsServletFactory.buildBaseUri(new Repository("", "GIT", repositoryNamespace, repositoryName), RequestWithUri(repositoryName, true));
- assertThat(result, is(equalTo("http://localhost:8081/scm/git/space/git-lfs-demo.git/info/lfs/objects/")));
+ assertThat(result, is(equalTo("http://localhost:8081/scm/repo/space/git-lfs-demo.git/info/lfs/objects/")));
//result will be with dot-git suffix, ide
result = LfsServletFactory.buildBaseUri(new Repository("", "GIT", repositoryNamespace, repositoryName), RequestWithUri(repositoryName, false));
- assertThat(result, is(equalTo("http://localhost:8081/scm/git/space/git-lfs-demo.git/info/lfs/objects/")));
+ assertThat(result, is(equalTo("http://localhost:8081/scm/repo/space/git-lfs-demo.git/info/lfs/objects/")));
}
private HttpServletRequest RequestWithUri(String repositoryName, boolean withDotGitSuffix) {
@@ -44,12 +44,10 @@ public class LfsServletFactoryTest {
//build from valid live request data
when(mockedRequest.getRequestURL()).thenReturn(
- new StringBuffer(String.format("http://localhost:8081/scm/git/%s%s/info/lfs/objects/batch", repositoryName, suffix)));
- when(mockedRequest.getRequestURI()).thenReturn(String.format("/scm/git/%s%s/info/lfs/objects/batch", repositoryName, suffix));
+ new StringBuffer(String.format("http://localhost:8081/scm/repo/%s%s/info/lfs/objects/batch", repositoryName, suffix)));
+ when(mockedRequest.getRequestURI()).thenReturn(String.format("/scm/repo/%s%s/info/lfs/objects/batch", repositoryName, suffix));
when(mockedRequest.getContextPath()).thenReturn("/scm");
return mockedRequest;
}
-
-
}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapper.java
index d2f4aecf7e..da980f75c0 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapper.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapper.java
@@ -8,11 +8,11 @@ import static de.otto.edison.hal.Links.linkingTo;
public class HgConfigInstallationsToDtoMapper {
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@Inject
- public HgConfigInstallationsToDtoMapper(UriInfoStore uriInfoStore) {
- this.uriInfoStore = uriInfoStore;
+ public HgConfigInstallationsToDtoMapper(ScmPathInfoStore scmPathInfoStore) {
+ this.scmPathInfoStore = scmPathInfoStore;
}
public HgConfigInstallationsDto map(List installations, String path) {
@@ -20,7 +20,7 @@ public class HgConfigInstallationsToDtoMapper {
}
private String createSelfLink(String path) {
- LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), HgConfigResource.class);
+ LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class);
return linkBuilder.method("getInstallationsResource").parameters().href() + '/' + path;
}
}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapper.java
index 67d7e58dff..3ee87cef84 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapper.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapper.java
@@ -20,7 +20,7 @@ import static de.otto.edison.hal.Links.linkingTo;
public abstract class HgConfigPackagesToDtoMapper {
@Inject
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
public HgConfigPackagesDto map(HgPackages hgpackages) {
return map(new HgPackagesNonIterable(hgpackages));
@@ -40,7 +40,7 @@ public abstract class HgConfigPackagesToDtoMapper {
}
private String createSelfLink() {
- LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), HgConfigResource.class);
+ LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class);
return linkBuilder.method("getPackagesResource").parameters().href();
}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapper.java
index 98137aebd5..b2a67e2aa4 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapper.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapper.java
@@ -18,7 +18,7 @@ import static de.otto.edison.hal.Links.linkingTo;
public abstract class HgConfigToHgConfigDtoMapper extends BaseMapper {
@Inject
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@AfterMapping
void appendLinks(HgConfig config, @MappingTarget HgConfigDto target) {
@@ -30,12 +30,12 @@ public abstract class HgConfigToHgConfigDtoMapper extends BaseMapper webTokenGenerators)
- {
- super(configuration, webTokenGenerators);
- }
-
- //~--- methods --------------------------------------------------------------
-
- /**
- * Method description
- *
- *
- * @param request
- * @param response
- *
- * @throws IOException
- */
- @Override
- protected void sendFailedAuthenticationError(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException
- {
- if (HgUtil.isHgClient(request)
- && (configuration.isLoginAttemptLimitEnabled()))
- {
- response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
- }
- else
- {
- super.sendFailedAuthenticationError(request, response);
- }
- }
-}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java
index 6cb4d523f0..1821f92fa4 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java
@@ -33,8 +33,6 @@
package sonia.scm.web;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.inject.Inject;
@@ -49,8 +47,8 @@ import sonia.scm.repository.HgHookManager;
import sonia.scm.repository.HgPythonScript;
import sonia.scm.repository.HgRepositoryHandler;
import sonia.scm.repository.Repository;
-import sonia.scm.repository.RepositoryProvider;
import sonia.scm.repository.RepositoryRequestListenerUtil;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
import sonia.scm.security.CipherUtil;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.HttpUtil;
@@ -68,14 +66,12 @@ import java.io.IOException;
import java.util.Base64;
import java.util.Enumeration;
-//~--- JDK imports ------------------------------------------------------------
-
/**
*
* @author Sebastian Sdorra
*/
@Singleton
-public class HgCGIServlet extends HttpServlet
+public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
{
/** Field description */
@@ -108,20 +104,18 @@ public class HgCGIServlet extends HttpServlet
*
* @param cgiExecutorFactory
* @param configuration
- * @param repositoryProvider
* @param handler
* @param hookManager
* @param requestListenerUtil
*/
@Inject
public HgCGIServlet(CGIExecutorFactory cgiExecutorFactory,
- ScmConfiguration configuration, RepositoryProvider repositoryProvider,
+ ScmConfiguration configuration,
HgRepositoryHandler handler, HgHookManager hookManager,
RepositoryRequestListenerUtil requestListenerUtil)
{
this.cgiExecutorFactory = cgiExecutorFactory;
this.configuration = configuration;
- this.repositoryProvider = repositoryProvider;
this.handler = handler;
this.hookManager = hookManager;
this.requestListenerUtil = requestListenerUtil;
@@ -131,46 +125,11 @@ public class HgCGIServlet extends HttpServlet
//~--- methods --------------------------------------------------------------
- /**
- * Method description
- *
- *
- * @throws ServletException
- */
@Override
- public void init() throws ServletException
+ public void service(HttpServletRequest request,
+ HttpServletResponse response, Repository repository)
{
-
- super.init();
- }
-
- /**
- * Method description
- *
- *
- * @param request
- * @param response
- *
- * @throws IOException
- * @throws ServletException
- */
- @Override
- protected void service(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException
- {
- Repository repository = repositoryProvider.get();
-
- if (repository == null)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("no hg repository found at {}", request.getRequestURI());
- }
-
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);
- }
- else if (!handler.isConfigured())
+ if (!handler.isConfigured())
{
exceptionHandler.sendFormattedError(request, response,
HgCGIExceptionHandler.ERROR_NOT_CONFIGURED);
@@ -379,9 +338,6 @@ public class HgCGIServlet extends HttpServlet
/** Field description */
private final HgHookManager hookManager;
- /** Field description */
- private final RepositoryProvider repositoryProvider;
-
/** Field description */
private final RepositoryRequestListenerUtil requestListenerUtil;
}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServletProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServletProvider.java
new file mode 100644
index 0000000000..db7a6be7b3
--- /dev/null
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServletProvider.java
@@ -0,0 +1,23 @@
+package sonia.scm.web;
+
+import com.google.inject.Inject;
+import sonia.scm.repository.HgRepositoryHandler;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.repository.spi.ScmProviderHttpServletProvider;
+
+import javax.inject.Provider;
+
+public class HgCGIServletProvider extends ScmProviderHttpServletProvider {
+
+ @Inject
+ private Provider servletProvider;
+
+ public HgCGIServletProvider() {
+ super(HgRepositoryHandler.TYPE_NAME);
+ }
+
+ @Override
+ protected ScmProviderHttpServlet getRootServlet() {
+ return servletProvider.get();
+ }
+}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java
index de2835dd8f..7f92cc0357 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java
@@ -33,53 +33,31 @@
package sonia.scm.web;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.common.collect.ImmutableSet;
-import com.google.inject.Inject;
-
-import sonia.scm.Priority;
import sonia.scm.config.ScmConfiguration;
-import sonia.scm.filter.Filters;
-import sonia.scm.filter.WebElement;
-import sonia.scm.repository.RepositoryProvider;
-import sonia.scm.web.filter.ProviderPermissionFilter;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.util.Set;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.web.filter.PermissionFilter;
import javax.servlet.http.HttpServletRequest;
+import java.util.Set;
/**
* Permission filter for mercurial repositories.
*
* @author Sebastian Sdorra
*/
-@Priority(Filters.PRIORITY_AUTHORIZATION)
-@WebElement(value = HgServletModule.MAPPING_HG)
-public class HgPermissionFilter extends ProviderPermissionFilter
+public class HgPermissionFilter extends PermissionFilter
{
private static final Set READ_METHODS = ImmutableSet.of("GET", "HEAD", "OPTIONS", "TRACE");
- /**
- * Constructs a new instance.
- *
- * @param configuration scm configuration
- * @param repositoryProvider repository provider
- */
- @Inject
- public HgPermissionFilter(ScmConfiguration configuration,
- RepositoryProvider repositoryProvider)
+ public HgPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate)
{
- super(configuration, repositoryProvider);
+ super(configuration, delegate);
}
- //~--- get methods ----------------------------------------------------------
-
@Override
- protected boolean isWriteRequest(HttpServletRequest request)
+ public boolean isWriteRequest(HttpServletRequest request)
{
return !READ_METHODS.contains(request.getMethod());
}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilterFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilterFactory.java
new file mode 100644
index 0000000000..90f53a1fea
--- /dev/null
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilterFactory.java
@@ -0,0 +1,30 @@
+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;
+import sonia.scm.repository.spi.ScmProviderHttpServletDecoratorFactory;
+
+import javax.inject.Inject;
+
+@Extension
+public class HgPermissionFilterFactory implements ScmProviderHttpServletDecoratorFactory {
+
+ private final ScmConfiguration configuration;
+
+ @Inject
+ public HgPermissionFilterFactory(ScmConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public boolean handlesScmType(String type) {
+ return HgRepositoryHandler.TYPE_NAME.equals(type);
+ }
+
+ @Override
+ public ScmProviderHttpServlet createDecorator(ScmProviderHttpServlet delegate) {
+ return new HgPermissionFilter(configuration, delegate);
+ }
+}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgScmProtocolProviderWrapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgScmProtocolProviderWrapper.java
new file mode 100644
index 0000000000..37360a5845
--- /dev/null
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgScmProtocolProviderWrapper.java
@@ -0,0 +1,25 @@
+package sonia.scm.web;
+
+import sonia.scm.api.v2.resources.ScmPathInfoStore;
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.plugin.Extension;
+import sonia.scm.repository.HgRepositoryHandler;
+import sonia.scm.repository.spi.InitializingHttpScmProtocolWrapper;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+@Singleton
+@Extension
+public class HgScmProtocolProviderWrapper extends InitializingHttpScmProtocolWrapper {
+ @Inject
+ public HgScmProtocolProviderWrapper(HgCGIServletProvider servletProvider, Provider uriInfoStore, ScmConfiguration scmConfiguration) {
+ super(servletProvider, uriInfoStore, scmConfiguration);
+ }
+
+ @Override
+ public String getType() {
+ return HgRepositoryHandler.TYPE_NAME;
+ }
+}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java
index 357995483d..ba9ae3a0b9 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java
@@ -81,8 +81,5 @@ public class HgServletModule extends ServletModule
// bind servlets
serve(MAPPING_HOOK).with(HgHookCallbackServlet.class);
-
- // register hg cgi servlet
- serve(MAPPING_HG).with(HgCGIServlet.class);
}
}
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java
index 540a5b6757..65b9c262cb 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java
@@ -43,7 +43,7 @@ public class HgConfigInstallationsResourceTest {
private final URI baseUri = URI.create("/");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private HgConfigInstallationsToDtoMapper mapper;
@@ -61,7 +61,7 @@ public class HgConfigInstallationsResourceTest {
new HgConfigResource(null, null, null, null,
null, resourceProvider));
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
}
@Test
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java
index 34048f80b2..7cae1d9f7e 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java
@@ -23,7 +23,7 @@ public class HgConfigInstallationsToDtoMapperTest {
private URI baseUri = URI.create("http://example.com/base/");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private HgConfigInstallationsToDtoMapper mapper;
@@ -34,7 +34,7 @@ public class HgConfigInstallationsToDtoMapperTest {
@Before
public void init() {
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2 + "/installations/" + expectedPath);
}
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java
index 044897ad80..f1558b6efb 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java
@@ -57,7 +57,7 @@ public class HgConfigPackageResourceTest {
private final URI baseUri = java.net.URI.create("/");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private HgConfigPackagesToDtoMapperImpl mapper;
@@ -81,7 +81,7 @@ public class HgConfigPackageResourceTest {
public void prepareEnvironment() {
setupResources();
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
when(hgPackageReader.getPackages().getPackages()).thenReturn(createPackages());
}
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java
index 671d9fb7e1..c4431da6d5 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java
@@ -1,6 +1,5 @@
package sonia.scm.api.v2.resources;
-import de.otto.edison.hal.HalRepresentation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -13,12 +12,10 @@ import sonia.scm.installer.HgPackages;
import java.net.URI;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import static sonia.scm.api.v2.resources.HgConfigTests.assertEqualsPackage;
import static sonia.scm.api.v2.resources.HgConfigTests.createPackage;
@@ -29,7 +26,7 @@ public class HgConfigPackagesToDtoMapperTest {
private URI baseUri = URI.create("http://example.com/base/");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private HgConfigPackagesToDtoMapperImpl mapper;
@@ -38,7 +35,7 @@ public class HgConfigPackagesToDtoMapperTest {
@Before
public void init() {
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2 + "/packages");
}
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java
index 11a0fb55f9..9cd04a0789 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java
@@ -54,7 +54,7 @@ public class HgConfigResourceTest {
private HgConfigDtoToHgConfigMapperImpl dtoToConfigMapper;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private HgConfigToHgConfigDtoMapperImpl configToDtoMapper;
@@ -79,7 +79,7 @@ public class HgConfigResourceTest {
new HgConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, packagesResource,
autoconfigResource, installationsResource);
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
}
@Test
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java
index a12e95926c..81c50f3d58 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java
@@ -29,7 +29,7 @@ public class HgConfigToHgConfigDtoMapperTest {
private URI baseUri = URI.create("http://example.com/base/");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private HgConfigToHgConfigDtoMapperImpl mapper;
@@ -41,7 +41,7 @@ public class HgConfigToHgConfigDtoMapperTest {
@Before
public void init() {
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2);
subjectThreadState.bind();
ThreadContext.bind(subject);
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapper.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapper.java
index a71d75151d..c160280822 100644
--- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapper.java
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapper.java
@@ -18,7 +18,7 @@ import static de.otto.edison.hal.Links.linkingTo;
public abstract class SvnConfigToSvnConfigDtoMapper extends BaseMapper {
@Inject
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@AfterMapping
void appendLinks(SvnConfig config, @MappingTarget SvnConfigDto target) {
@@ -30,12 +30,12 @@ public abstract class SvnConfigToSvnConfigDtoMapper extends BaseMapper webTokenGenerators) {
- super(configuration, webTokenGenerators);
- }
-
- //~--- methods --------------------------------------------------------------
-
- /**
- * Sends unauthorized instead of forbidden for svn clients, because the
- * svn client prompts again for authentication.
- *
- *
- * @param request http request
- * @param response http response
- *
- * @throws IOException
- */
- @Override
- protected void sendFailedAuthenticationError(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException
- {
- if (SvnUtil.isSvnClient(request))
- {
- HttpUtil.sendUnauthorized(response, configuration.getRealmDescription());
- }
- else
- {
- super.sendFailedAuthenticationError(request, response);
- }
- }
-}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java
index c811179255..92d01db5a1 100644
--- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java
@@ -33,8 +33,6 @@
package sonia.scm.web;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.slf4j.Logger;
@@ -45,6 +43,7 @@ import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryProvider;
import sonia.scm.repository.RepositoryRequestListenerUtil;
import sonia.scm.repository.SvnRepositoryHandler;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.HttpUtil;
@@ -54,14 +53,12 @@ import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
-//~--- JDK imports ------------------------------------------------------------
-
/**
*
* @author Sebastian Sdorra
*/
@Singleton
-public class SvnDAVServlet extends DAVServlet
+public class SvnDAVServlet extends DAVServlet implements ScmProviderHttpServlet
{
/** Field description */
@@ -110,28 +107,18 @@ public class SvnDAVServlet extends DAVServlet
* @throws ServletException
*/
@Override
- public void service(HttpServletRequest request, HttpServletResponse response)
+ public void service(HttpServletRequest request, HttpServletResponse response, Repository repository)
throws ServletException, IOException
{
- Repository repository = repositoryProvider.get();
-
- if (repository != null)
- {
- if (repositoryRequestListenerUtil.callListeners(request, response,
- repository))
- {
- super.service(new SvnHttpServletRequestWrapper(request,
- repositoryProvider), response);
- }
- else if (logger.isDebugEnabled())
- {
- logger.debug("request aborted by repository request listener");
- }
- }
- else
+ if (repositoryRequestListenerUtil.callListeners(request, response,
+ repository))
{
super.service(new SvnHttpServletRequestWrapper(request,
- repositoryProvider), response);
+ repository), response);
+ }
+ else if (logger.isDebugEnabled())
+ {
+ logger.debug("request aborted by repository request listener");
}
}
@@ -163,18 +150,11 @@ public class SvnDAVServlet extends DAVServlet
extends HttpServletRequestWrapper
{
- /**
- * Constructs ...
- *
- *
- * @param request
- * @param repositoryProvider
- */
public SvnHttpServletRequestWrapper(HttpServletRequest request,
- RepositoryProvider repositoryProvider)
+ Repository repository)
{
super(request);
- this.repositoryProvider = repositoryProvider;
+ this.repository = repository;
}
//~--- get methods --------------------------------------------------------
@@ -211,8 +191,6 @@ public class SvnDAVServlet extends DAVServlet
AssertUtil.assertIsNotEmpty(pathInfo);
- Repository repository = repositoryProvider.get();
-
if (repository != null)
{
if (pathInfo.startsWith(HttpUtil.SEPARATOR_PATH))
@@ -236,7 +214,6 @@ public class SvnDAVServlet extends DAVServlet
public String getServletPath()
{
String servletPath = super.getServletPath();
- Repository repository = repositoryProvider.get();
if (repository != null)
{
@@ -280,10 +257,9 @@ public class SvnDAVServlet extends DAVServlet
//~--- fields -------------------------------------------------------------
/** Field description */
- private final RepositoryProvider repositoryProvider;
+ private final Repository repository;
}
-
//~--- fields ---------------------------------------------------------------
/** Field description */
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServletProvider.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServletProvider.java
new file mode 100644
index 0000000000..d221504256
--- /dev/null
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServletProvider.java
@@ -0,0 +1,23 @@
+package sonia.scm.web;
+
+import com.google.inject.Inject;
+import sonia.scm.repository.SvnRepositoryHandler;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.repository.spi.ScmProviderHttpServletProvider;
+
+import javax.inject.Provider;
+
+public class SvnDAVServletProvider extends ScmProviderHttpServletProvider {
+
+ @Inject
+ private Provider servletProvider;
+
+ public SvnDAVServletProvider() {
+ super(SvnRepositoryHandler.TYPE_NAME);
+ }
+
+ @Override
+ protected ScmProviderHttpServlet getRootServlet() {
+ return servletProvider.get();
+ }
+}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnGZipFilter.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnGZipFilter.java
index 65afefbb28..7cc78180ff 100644
--- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnGZipFilter.java
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnGZipFilter.java
@@ -34,38 +34,34 @@ package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import sonia.scm.filter.GZipFilter;
+import sonia.scm.repository.Repository;
import sonia.scm.repository.SvnRepositoryHandler;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.IOException;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
+import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+//~--- JDK imports ------------------------------------------------------------
/**
*
* @author Sebastian Sdorra
*/
-@Singleton
-public class SvnGZipFilter extends GZipFilter
+public class SvnGZipFilter extends GZipFilter implements ScmProviderHttpServlet
{
- /**
- * the logger for SvnGZipFilter
- */
- private static final Logger logger =
- LoggerFactory.getLogger(SvnGZipFilter.class);
+ private static final Logger logger = LoggerFactory.getLogger(SvnGZipFilter.class);
+
+ private final SvnRepositoryHandler handler;
+ private final ScmProviderHttpServlet delegate;
//~--- constructors ---------------------------------------------------------
@@ -75,10 +71,10 @@ public class SvnGZipFilter extends GZipFilter
*
* @param handler
*/
- @Inject
- public SvnGZipFilter(SvnRepositoryHandler handler)
+ public SvnGZipFilter(SvnRepositoryHandler handler, ScmProviderHttpServlet delegate)
{
this.handler = handler;
+ this.delegate = delegate;
}
//~--- methods --------------------------------------------------------------
@@ -134,8 +130,30 @@ public class SvnGZipFilter extends GZipFilter
}
}
- //~--- fields ---------------------------------------------------------------
+ @Override
+ public void service(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException {
+ if (handler.getConfig().isEnabledGZip())
+ {
+ if (logger.isTraceEnabled())
+ {
+ logger.trace("encode svn request with gzip");
+ }
- /** Field description */
- private SvnRepositoryHandler handler;
+ super.doFilter(request, response, (servletRequest, servletResponse) -> delegate.service((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse, repository));
+ }
+ else
+ {
+ if (logger.isTraceEnabled())
+ {
+ logger.trace("skip gzip encoding");
+ }
+
+ delegate.service(request, response, repository);
+ }
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ delegate.init(config);
+ }
}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnGZipFilterFactory.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnGZipFilterFactory.java
new file mode 100644
index 0000000000..e2774106fa
--- /dev/null
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnGZipFilterFactory.java
@@ -0,0 +1,28 @@
+package sonia.scm.web;
+
+import com.google.inject.Inject;
+import sonia.scm.plugin.Extension;
+import sonia.scm.repository.SvnRepositoryHandler;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.repository.spi.ScmProviderHttpServletDecoratorFactory;
+
+@Extension
+public class SvnGZipFilterFactory implements ScmProviderHttpServletDecoratorFactory {
+
+ private final SvnRepositoryHandler handler;
+
+ @Inject
+ public SvnGZipFilterFactory(SvnRepositoryHandler handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public boolean handlesScmType(String type) {
+ return SvnRepositoryHandler.TYPE_NAME.equals(type);
+ }
+
+ @Override
+ public ScmProviderHttpServlet createDecorator(ScmProviderHttpServlet delegate) {
+ return new SvnGZipFilter(handler, delegate);
+ }
+}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java
index 30ef3e94c0..2c0a1e65ff 100644
--- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java
@@ -33,37 +33,24 @@
package sonia.scm.web;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.common.collect.ImmutableSet;
-import com.google.inject.Inject;
-
import sonia.scm.ClientMessages;
-import sonia.scm.Priority;
import sonia.scm.config.ScmConfiguration;
-import sonia.scm.filter.Filters;
-import sonia.scm.filter.WebElement;
-import sonia.scm.repository.RepositoryProvider;
import sonia.scm.repository.ScmSvnErrorCode;
import sonia.scm.repository.SvnUtil;
-import sonia.scm.web.filter.ProviderPermissionFilter;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.IOException;
-
-import java.util.Set;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.web.filter.PermissionFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Set;
/**
*
* @author Sebastian Sdorra
*/
-@Priority(Filters.PRIORITY_AUTHORIZATION)
-@WebElement(value = SvnServletModule.PATTERN_SVN)
-public class SvnPermissionFilter extends ProviderPermissionFilter
+public class SvnPermissionFilter extends PermissionFilter
{
/** Field description */
@@ -77,13 +64,10 @@ public class SvnPermissionFilter extends ProviderPermissionFilter
* Constructs ...
*
* @param configuration
- * @param repository
*/
- @Inject
- public SvnPermissionFilter(ScmConfiguration configuration,
- RepositoryProvider repository)
+ public SvnPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate)
{
- super(configuration, repository);
+ super(configuration, delegate);
}
//~--- methods --------------------------------------------------------------
@@ -132,7 +116,7 @@ public class SvnPermissionFilter extends ProviderPermissionFilter
* @return
*/
@Override
- protected boolean isWriteRequest(HttpServletRequest request)
+ public boolean isWriteRequest(HttpServletRequest request)
{
return WRITEMETHOD_SET.contains(request.getMethod().toUpperCase());
}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilterFactory.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilterFactory.java
new file mode 100644
index 0000000000..882cb8c54f
--- /dev/null
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilterFactory.java
@@ -0,0 +1,30 @@
+package sonia.scm.web;
+
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.plugin.Extension;
+import sonia.scm.repository.SvnRepositoryHandler;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+import sonia.scm.repository.spi.ScmProviderHttpServletDecoratorFactory;
+
+import javax.inject.Inject;
+
+@Extension
+public class SvnPermissionFilterFactory implements ScmProviderHttpServletDecoratorFactory {
+
+ private final ScmConfiguration configuration;
+
+ @Inject
+ public SvnPermissionFilterFactory(ScmConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public boolean handlesScmType(String type) {
+ return SvnRepositoryHandler.TYPE_NAME.equals(type);
+ }
+
+ @Override
+ public ScmProviderHttpServlet createDecorator(ScmProviderHttpServlet delegate) {
+ return new SvnPermissionFilter(configuration, delegate);
+ }
+}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnScmProtocolProviderWrapper.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnScmProtocolProviderWrapper.java
new file mode 100644
index 0000000000..ba7d5e875a
--- /dev/null
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnScmProtocolProviderWrapper.java
@@ -0,0 +1,74 @@
+package sonia.scm.web;
+
+import sonia.scm.api.v2.resources.ScmPathInfoStore;
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.plugin.Extension;
+import sonia.scm.repository.SvnRepositoryHandler;
+import sonia.scm.repository.spi.InitializingHttpScmProtocolWrapper;
+import sonia.scm.repository.spi.ScmProviderHttpServlet;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import java.util.Enumeration;
+
+@Singleton
+@Extension
+public class SvnScmProtocolProviderWrapper extends InitializingHttpScmProtocolWrapper {
+
+ public static final String PARAMETER_SVN_PARENTPATH = "SVNParentPath";
+
+ @Override
+ public String getType() {
+ return SvnRepositoryHandler.TYPE_NAME;
+ }
+
+ @Inject
+ public SvnScmProtocolProviderWrapper(SvnDAVServletProvider servletProvider, Provider uriInfoStore, ScmConfiguration scmConfiguration) {
+ super(servletProvider, uriInfoStore, scmConfiguration);
+ }
+
+ @Override
+ protected void initializeServlet(ServletConfig config, ScmProviderHttpServlet httpServlet) throws ServletException {
+
+ super.initializeServlet(new SvnConfigEnhancer(config), httpServlet);
+ }
+
+ private static class SvnConfigEnhancer implements ServletConfig {
+
+ private final ServletConfig originalConfig;
+
+ private SvnConfigEnhancer(ServletConfig originalConfig) {
+ this.originalConfig = originalConfig;
+ }
+
+ @Override
+ public String getServletName() {
+ return originalConfig.getServletName();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return originalConfig.getServletContext();
+ }
+
+ @Override
+ /**
+ * Overridden to return the systems temp directory for the key {@link PARAMETER_SVN_PARENTPATH}.
+ */
+ public String getInitParameter(String key) {
+ if (PARAMETER_SVN_PARENTPATH.equals(key)) {
+ return System.getProperty("java.io.tmpdir");
+ }
+ return originalConfig.getInitParameter(key);
+ }
+
+ @Override
+ public Enumeration getInitParameterNames() {
+ return originalConfig.getInitParameterNames();
+ }
+ }
+}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java
index 9b5c8ae556..8526d6380a 100644
--- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java
@@ -33,53 +33,22 @@
package sonia.scm.web;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.inject.servlet.ServletModule;
import org.mapstruct.factory.Mappers;
import sonia.scm.api.v2.resources.SvnConfigDtoToSvnConfigMapper;
import sonia.scm.api.v2.resources.SvnConfigToSvnConfigDtoMapper;
import sonia.scm.plugin.Extension;
-import java.util.HashMap;
-import java.util.Map;
-
-//~--- JDK imports ------------------------------------------------------------
-
/**
*
* @author Sebastian Sdorra
*/
@Extension
-public class SvnServletModule extends ServletModule
-{
+public class SvnServletModule extends ServletModule {
- /** Field description */
- public static final String PARAMETER_SVN_PARENTPATH = "SVNParentPath";
-
- /** Field description */
- public static final String PATTERN_SVN = "/svn/*";
-
- //~--- methods --------------------------------------------------------------
-
- /**
- * Method description
- *
- */
@Override
- protected void configureServlets()
- {
- filter(PATTERN_SVN).through(SvnGZipFilter.class);
- filter(PATTERN_SVN).through(SvnBasicAuthenticationFilter.class);
- filter(PATTERN_SVN).through(SvnPermissionFilter.class);
-
+ protected void configureServlets() {
bind(SvnConfigDtoToSvnConfigMapper.class).to(Mappers.getMapper(SvnConfigDtoToSvnConfigMapper.class).getClass());
bind(SvnConfigToSvnConfigDtoMapper.class).to(Mappers.getMapper(SvnConfigToSvnConfigDtoMapper.class).getClass());
-
- Map parameters = new HashMap();
-
- parameters.put(PARAMETER_SVN_PARENTPATH,
- System.getProperty("java.io.tmpdir"));
- serve(PATTERN_SVN).with(SvnDAVServlet.class, parameters);
}
}
diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java
index de4d654910..28dbe09e92 100644
--- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java
+++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java
@@ -53,7 +53,7 @@ public class SvnConfigResourceTest {
private SvnConfigDtoToSvnConfigMapperImpl dtoToConfigMapper;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private SvnConfigToSvnConfigDtoMapperImpl configToDtoMapper;
@@ -67,7 +67,7 @@ public class SvnConfigResourceTest {
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
SvnConfigResource gitConfigResource = new SvnConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler);
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
}
@Test
diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java
index de7b0ecd31..5184aa3d41 100644
--- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java
+++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java
@@ -30,7 +30,7 @@ public class SvnConfigToSvnConfigDtoMapperTest {
private URI baseUri = URI.create("http://example.com/base/");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@InjectMocks
private SvnConfigToSvnConfigDtoMapperImpl mapper;
@@ -42,7 +42,7 @@ public class SvnConfigToSvnConfigDtoMapperTest {
@Before
public void init() {
- when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
+ when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
expectedBaseUri = baseUri.resolve(SvnConfigResource.SVN_CONFIG_PATH_V2);
subjectThreadState.bind();
ThreadContext.bind(subject);
diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml
index da01814152..644a1bc0f9 100644
--- a/scm-webapp/pom.xml
+++ b/scm-webapp/pom.xml
@@ -49,7 +49,7 @@
scm-core
2.0.0-SNAPSHOT
-
+
sonia.scm
scm-dao-xml
diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java
index 1eaef333a1..5a087a1c70 100644
--- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java
+++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java
@@ -33,8 +33,6 @@
package sonia.scm;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.inject.Injector;
@@ -63,8 +61,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
-//~--- JDK imports ------------------------------------------------------------
-
/**
*
* @author Sebastian Sdorra
@@ -135,7 +131,7 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
moduleList.add(new EagerSingletonModule());
moduleList.add(ShiroWebModule.guiceFilterModule());
moduleList.add(new WebElementModule(pluginLoader));
- moduleList.add(new ScmServletModule(context, pluginLoader, overrides, pluginLoader.getExtensionProcessor()));
+ moduleList.add(new ScmServletModule(context, pluginLoader, overrides));
moduleList.add(
new ScmSecurityModule(context, pluginLoader.getExtensionProcessor())
);
diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
index 1d627de8ed..e9ec9e4a39 100644
--- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
+++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
@@ -33,8 +33,6 @@
package sonia.scm;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Provider;
import com.google.inject.multibindings.Multibinder;
@@ -56,17 +54,48 @@ import sonia.scm.group.xml.XmlGroupDAO;
import sonia.scm.io.DefaultFileSystem;
import sonia.scm.io.FileSystem;
import sonia.scm.net.SSLContextProvider;
-import sonia.scm.net.ahc.*;
-import sonia.scm.plugin.*;
-import sonia.scm.repository.*;
+import sonia.scm.net.ahc.AdvancedHttpClient;
+import sonia.scm.net.ahc.ContentTransformer;
+import sonia.scm.net.ahc.DefaultAdvancedHttpClient;
+import sonia.scm.net.ahc.JsonContentTransformer;
+import sonia.scm.net.ahc.XmlContentTransformer;
+import sonia.scm.plugin.DefaultPluginLoader;
+import sonia.scm.plugin.DefaultPluginManager;
+import sonia.scm.plugin.PluginLoader;
+import sonia.scm.plugin.PluginManager;
+import sonia.scm.repository.DefaultRepositoryManager;
+import sonia.scm.repository.DefaultRepositoryProvider;
+import sonia.scm.repository.HealthCheckContextListener;
+import sonia.scm.repository.NamespaceStrategy;
+import sonia.scm.repository.NamespaceStrategyProvider;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.RepositoryDAO;
+import sonia.scm.repository.RepositoryManager;
+import sonia.scm.repository.RepositoryManagerProvider;
+import sonia.scm.repository.RepositoryProvider;
import sonia.scm.repository.api.HookContextFactory;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.repository.spi.HookEventFacade;
import sonia.scm.repository.xml.XmlRepositoryDAO;
import sonia.scm.schedule.QuartzScheduler;
import sonia.scm.schedule.Scheduler;
-import sonia.scm.security.*;
-import sonia.scm.store.*;
+import sonia.scm.security.AuthorizationChangedEventProducer;
+import sonia.scm.security.CipherHandler;
+import sonia.scm.security.CipherUtil;
+import sonia.scm.security.ConfigurableLoginAttemptHandler;
+import sonia.scm.security.DefaultKeyGenerator;
+import sonia.scm.security.DefaultSecuritySystem;
+import sonia.scm.security.KeyGenerator;
+import sonia.scm.security.LoginAttemptHandler;
+import sonia.scm.security.SecuritySystem;
+import sonia.scm.store.BlobStoreFactory;
+import sonia.scm.store.ConfigurationEntryStoreFactory;
+import sonia.scm.store.ConfigurationStoreFactory;
+import sonia.scm.store.DataStoreFactory;
+import sonia.scm.store.FileBlobStoreFactory;
+import sonia.scm.store.JAXBConfigurationEntryStoreFactory;
+import sonia.scm.store.JAXBConfigurationStoreFactory;
+import sonia.scm.store.JAXBDataStoreFactory;
import sonia.scm.template.MustacheTemplateEngine;
import sonia.scm.template.TemplateEngine;
import sonia.scm.template.TemplateEngineFactory;
@@ -81,14 +110,16 @@ import sonia.scm.util.ScmConfigurationUtil;
import sonia.scm.web.UserAgentParser;
import sonia.scm.web.cgi.CGIExecutorFactory;
import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
+import sonia.scm.web.filter.AuthenticationFilter;
import sonia.scm.web.filter.LoggingFilter;
+import sonia.scm.web.protocol.HttpProtocolServlet;
import sonia.scm.web.security.AdministrationContext;
import sonia.scm.web.security.DefaultAdministrationContext;
import javax.net.ssl.SSLContext;
import javax.servlet.ServletContext;
-//~--- JDK imports ------------------------------------------------------------
+import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
/**
*
@@ -99,14 +130,14 @@ public class ScmServletModule extends ServletModule
/** Field description */
public static final String[] PATTERN_ADMIN = new String[] {
- "/api/rest/groups*",
- "/api/rest/users*", "/api/rest/plguins*" };
+ REST_API_PATH + "/groups*",
+ REST_API_PATH + "/users*", REST_API_PATH + "/plguins*" };
/** Field description */
public static final String PATTERN_ALL = "/*";
/** Field description */
- public static final String PATTERN_CONFIG = "/api/rest/config*";
+ public static final String PATTERN_CONFIG = REST_API_PATH + "/config*";
/** Field description */
public static final String PATTERN_DEBUG = "/debug.html";
@@ -155,22 +186,11 @@ public class ScmServletModule extends ServletModule
//~--- constructors ---------------------------------------------------------
- /**
- * Constructs ...
- *
- *
- * @param servletContext
- * @param pluginLoader
- * @param overrides
- * @param extensionProcessor
- */
- ScmServletModule(ServletContext servletContext,
- DefaultPluginLoader pluginLoader, ClassOverrides overrides, ExtensionProcessor extensionProcessor)
+ ScmServletModule(ServletContext servletContext, DefaultPluginLoader pluginLoader, ClassOverrides overrides)
{
this.servletContext = servletContext;
this.pluginLoader = pluginLoader;
this.overrides = overrides;
- this.extensionProcessor = extensionProcessor;
}
//~--- methods --------------------------------------------------------------
@@ -293,6 +313,8 @@ public class ScmServletModule extends ServletModule
bind(TemplateEngineFactory.class);
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
+ filter(HttpProtocolServlet.PATTERN).through(AuthenticationFilter.class);
+
// bind events
// bind(LastModifiedUpdateListener.class);
@@ -389,11 +411,6 @@ public class ScmServletModule extends ServletModule
/**
* Load ScmConfiguration with JAXB
- *
- *
- * @param context
- *
- * @return
*/
private ScmConfiguration getScmConfiguration()
{
@@ -414,6 +431,4 @@ public class ScmServletModule extends ServletModule
/** Field description */
private final ServletContext servletContext;
-
- private final ExtensionProcessor extensionProcessor;
}
diff --git a/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java b/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java
index 98117851db..059c990718 100644
--- a/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java
+++ b/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java
@@ -33,7 +33,7 @@ public class WebResourceServlet extends HttpServlet {
* TODO remove old protocol servlets and hook. Move /hook/hg to api?
*/
@VisibleForTesting
- static final String PATTERN = "/(?!api/|git/|hg/|svn/|hook/).*";
+ static final String PATTERN = "/(?!api/|git/|hg/|svn/|hook/|repo/).*";
private static final Logger LOG = LoggerFactory.getLogger(WebResourceServlet.class);
diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/UriInfoFilter.java b/scm-webapp/src/main/java/sonia/scm/api/rest/UriInfoFilter.java
index de0f1dd626..b602e918ea 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/rest/UriInfoFilter.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/rest/UriInfoFilter.java
@@ -1,24 +1,26 @@
package sonia.scm.api.rest;
-import sonia.scm.api.v2.resources.UriInfoStore;
+import sonia.scm.api.v2.resources.ScmPathInfoStore;
import javax.inject.Inject;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
@Provider
public class UriInfoFilter implements ContainerRequestFilter {
- private final javax.inject.Provider storeProvider;
+ private final javax.inject.Provider storeProvider;
@Inject
- public UriInfoFilter(javax.inject.Provider storeProvider) {
+ public UriInfoFilter(javax.inject.Provider storeProvider) {
this.storeProvider = storeProvider;
}
@Override
public void filter(ContainerRequestContext requestContext) {
- storeProvider.get().set(requestContext.getUriInfo());
+ UriInfo uriInfo = requestContext.getUriInfo();
+ storeProvider.get().set(uriInfo::getBaseUri);
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryRootResource.java
index cec1894764..d5ea6c88de 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryRootResource.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryRootResource.java
@@ -33,37 +33,28 @@
package sonia.scm.api.rest.resources;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.inject.Inject;
-
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryTypePredicate;
-import sonia.scm.util.HttpUtil;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.IOException;
-
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
+import sonia.scm.template.Viewable;
import javax.servlet.http.HttpServletRequest;
-
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
-import sonia.scm.template.Viewable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
/**
*
@@ -106,13 +97,12 @@ public class RepositoryRootResource
@Produces(MediaType.TEXT_HTML)
public Viewable renderRepositoriesRoot(@Context HttpServletRequest request, @PathParam("type") final String type)
{
- String baseUrl = HttpUtil.getCompleteUrl(request);
//J-
Collection unsortedRepositories =
Collections2.transform(
Collections2.filter(
repositoryManager.getAll(), new RepositoryTypePredicate(type))
- , new RepositoryTransformFunction(baseUrl)
+ , new RepositoryTransformFunction()
);
List repositories = Ordering.from(
@@ -138,17 +128,9 @@ public class RepositoryRootResource
public static class RepositoryTemplateElement
{
- /**
- * Constructs ...
- *
- *
- * @param repository
- * @param baseUrl
- */
- public RepositoryTemplateElement(Repository repository, String baseUrl)
+ public RepositoryTemplateElement(Repository repository)
{
this.repository = repository;
- this.baseUrl = baseUrl;
}
//~--- get methods --------------------------------------------------------
@@ -175,22 +157,8 @@ public class RepositoryRootResource
return repository;
}
- /**
- * Method description
- *
- *
- * @return
- */
- public String getUrl()
- {
- return repository.createUrl(baseUrl);
- }
-
//~--- fields -------------------------------------------------------------
- /** Field description */
- private String baseUrl;
-
/** Field description */
private Repository repository;
@@ -236,31 +204,10 @@ public class RepositoryRootResource
private static class RepositoryTransformFunction
implements Function
{
-
- public RepositoryTransformFunction(String baseUrl)
- {
- this.baseUrl = baseUrl;
- }
-
- //~--- methods ------------------------------------------------------------
-
- /**
- * Method description
- *
- *
- * @param repository
- *
- * @return
- */
@Override
public RepositoryTemplateElement apply(Repository repository)
{
- return new RepositoryTemplateElement(repository, baseUrl);
+ return new RepositoryTemplateElement(repository);
}
-
- //~--- fields -------------------------------------------------------------
-
- /** Field description */
- private String baseUrl;
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java
index a600d1f45a..03b5728627 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java
@@ -41,6 +41,6 @@ public class MapperModule extends AbstractModule {
bind(UIPluginDtoMapper.class);
bind(UIPluginDtoCollectionMapper.class);
- bind(UriInfoStore.class).in(ServletScopes.REQUEST);
+ bind(ScmPathInfoStore.class).in(ServletScopes.REQUEST);
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeToUserDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeToUserDtoMapper.java
index b79dd9a766..a4586fab36 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeToUserDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeToUserDtoMapper.java
@@ -24,7 +24,7 @@ public abstract class MeToUserDtoMapper extends UserToUserDtoMapper{
@AfterMapping
- void appendLinks(User user, @MappingTarget UserDto target) {
+ protected void appendLinks(User user, @MappingTarget UserDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self());
if (UserPermissions.delete(user).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName())));
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java
index 90e9b3daed..2a587bc2fe 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java
@@ -238,8 +238,9 @@ public class PermissionRootResource {
* @throws RepositoryNotFoundException if the repository does not exists
*/
private Repository load(String namespace, String name) throws RepositoryNotFoundException {
- return Optional.ofNullable(manager.get(new NamespaceAndName(namespace, name)))
- .orElseThrow(() -> new RepositoryNotFoundException(name));
+ NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
+ return Optional.ofNullable(manager.get(namespaceAndName))
+ .orElseThrow(() -> new RepositoryNotFoundException(namespaceAndName));
}
/**
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java
index c597e12d4f..ddfe432d73 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java
@@ -24,7 +24,7 @@ public class RepositoryDto extends HalRepresentation {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Instant lastModified;
private String namespace;
- @Pattern(regexp = "(?!^\\.\\.$)(?!^\\.$)(?!.*[\\\\\\[\\]])^[A-z0-9\\.][A-z0-9\\.\\-_/]*$")
+ @Pattern(regexp = "^[A-z0-9\\-_]+$")
private String name;
private boolean archived = false;
@NotEmpty
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java
index f76669fbb9..29a4107aad 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java
@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
import com.google.inject.Inject;
+import de.otto.edison.hal.Link;
import de.otto.edison.hal.Links;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
@@ -11,9 +12,13 @@ import sonia.scm.repository.RepositoryPermissions;
import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
+import sonia.scm.repository.api.ScmProtocol;
+
+import java.util.List;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
+import static java.util.stream.Collectors.toList;
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
@SuppressWarnings("squid:S3306")
@@ -30,7 +35,6 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper protocolLinks = repositoryService.getSupportedProtocols()
+ .map(this::createProtocolLink)
+ .collect(toList());
+ linksBuilder.array(protocolLinks);
+ }
if (repositoryService.isSupported(Command.TAGS)) {
linksBuilder.single(link("tags", resourceLinks.tag().all(target.getNamespace(), target.getName())));
}
@@ -50,4 +60,8 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper {
}
@AfterMapping
- void appendLinks(User user, @MappingTarget UserDto target) {
+ protected void appendLinks(User user, @MappingTarget UserDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(target.getName()));
if (UserPermissions.delete(user).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.user().delete(target.getName())));
diff --git a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java
index 0d59d77027..de0d689c52 100644
--- a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java
+++ b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java
@@ -37,10 +37,8 @@ package sonia.scm.filter;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
-
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
-
import sonia.scm.Priority;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
@@ -48,14 +46,15 @@ import sonia.scm.security.SecurityRequests;
import sonia.scm.web.filter.HttpFilter;
import sonia.scm.web.filter.SecurityHttpServletRequestWrapper;
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.IOException;
-
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
+
+//~--- JDK imports ------------------------------------------------------------
/**
*
@@ -63,7 +62,8 @@ import javax.servlet.http.HttpServletResponse;
*/
@Priority(Filters.PRIORITY_AUTHORIZATION)
// TODO find a better way for unprotected resources
-@WebElement(value = "/api/rest/(?!v2/ui).*", regex = true)
+@WebElement(value = REST_API_PATH + "" +
+ "/(?!v2/ui).*", regex = true)
public class SecurityFilter extends HttpFilter
{
diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java
index 02ae67719b..8cb325b818 100644
--- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java
+++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java
@@ -31,10 +31,7 @@
package sonia.scm.repository;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.github.sdorra.ssp.PermissionActionCheck;
-import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Inject;
@@ -43,7 +40,6 @@ import org.apache.shiro.concurrent.SubjectAwareExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.AlreadyExistsException;
-import sonia.scm.ArgumentIsInvalidException;
import sonia.scm.ConfigurationException;
import sonia.scm.HandlerEventType;
import sonia.scm.ManagerDaoAdapter;
@@ -54,11 +50,9 @@ import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.KeyGenerator;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.CollectionAppender;
-import sonia.scm.util.HttpUtil;
import sonia.scm.util.IOUtil;
import sonia.scm.util.Util;
-import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -71,8 +65,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
-//~--- JDK imports ------------------------------------------------------------
-
/**
* Default implementation of {@link RepositoryManager}.
*
@@ -90,7 +82,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
private final KeyGenerator keyGenerator;
private final RepositoryDAO repositoryDAO;
private final Set types;
- private RepositoryMatcher repositoryMatcher;
private NamespaceStrategy namespaceStrategy;
private final ManagerDaoAdapter managerDaoAdapter;
@@ -99,12 +90,10 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
public DefaultRepositoryManager(ScmConfiguration configuration,
SCMContextProvider contextProvider, KeyGenerator keyGenerator,
RepositoryDAO repositoryDAO, Set handlerSet,
- RepositoryMatcher repositoryMatcher,
NamespaceStrategy namespaceStrategy) {
this.configuration = configuration;
this.keyGenerator = keyGenerator;
this.repositoryDAO = repositoryDAO;
- this.repositoryMatcher = repositoryMatcher;
this.namespaceStrategy = namespaceStrategy;
ThreadFactory factory = new ThreadFactoryBuilder()
@@ -317,71 +306,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
return validTypes;
}
- @Override
- public Repository getFromRequest(HttpServletRequest request) {
- AssertUtil.assertIsNotNull(request);
-
- return getFromUri(HttpUtil.getStrippedURI(request));
- }
-
- @Override
- public Repository getFromUri(String uri) {
- AssertUtil.assertIsNotEmpty(uri);
-
- if (uri.startsWith(HttpUtil.SEPARATOR_PATH)) {
- uri = uri.substring(1);
- }
-
- int typeSeparator = uri.indexOf(HttpUtil.SEPARATOR_PATH);
- Repository repository = null;
-
- if (typeSeparator > 0) {
- String type = uri.substring(0, typeSeparator);
-
- uri = uri.substring(typeSeparator + 1);
- repository = getFromTypeAndUri(type, uri);
- }
-
- return repository;
- }
-
- private Repository getFromTypeAndUri(String type, String uri) {
- if (Strings.isNullOrEmpty(type)) {
- throw new ArgumentIsInvalidException("argument type is required");
- }
-
- if (Strings.isNullOrEmpty(uri)) {
- throw new ArgumentIsInvalidException("argument uri is required");
- }
-
- // remove ;jsessionid, jetty bug?
- uri = HttpUtil.removeMatrixParameter(uri);
-
- Repository repository = null;
-
- if (handlerMap.containsKey(type)) {
- Collection repositories = repositoryDAO.getAll();
-
- PermissionActionCheck check = RepositoryPermissions.read();
-
- for (Repository r : repositories) {
- if (repositoryMatcher.matches(r, type, uri)) {
- check.check(r);
- repository = r.clone();
-
- break;
- }
- }
- }
-
- if ((repository == null) && logger.isDebugEnabled()) {
- logger.debug("could not find repository with type {} and uri {}", type,
- uri);
- }
-
- return repository;
- }
-
@Override
public RepositoryHandler getHandler(String type) {
return handlerMap.get(type);
diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java
index c3341bbd22..62067db172 100644
--- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java
+++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java
@@ -33,86 +33,32 @@
package sonia.scm.repository;
-//~--- non-JDK imports --------------------------------------------------------
-
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.servlet.RequestScoped;
-import sonia.scm.security.ScmSecurityException;
-
-//~--- JDK imports ------------------------------------------------------------
-
import javax.servlet.http.HttpServletRequest;
-/**
- *
- * @author Sebastian Sdorra
- */
@RequestScoped
-public class DefaultRepositoryProvider implements RepositoryProvider
-{
+public class DefaultRepositoryProvider implements RepositoryProvider {
- /** Field description */
public static final String ATTRIBUTE_NAME = "scm.request.repository";
- //~--- constructors ---------------------------------------------------------
+ private final Provider requestProvider;
- /**
- * Constructs ...
- *
- *
- * @param requestProvider
- * @param manager
- */
@Inject
- public DefaultRepositoryProvider(
- Provider requestProvider,
- RepositoryManager manager)
- {
+ public DefaultRepositoryProvider(Provider requestProvider) {
this.requestProvider = requestProvider;
- this.manager = manager;
}
- //~--- get methods ----------------------------------------------------------
-
- /**
- * Method description
- *
- *
- * @return
- *
- * @throws ScmSecurityException
- */
@Override
- public Repository get() throws ScmSecurityException
- {
- Repository repository = null;
+ public Repository get() {
HttpServletRequest request = requestProvider.get();
- if (request != null)
- {
- repository = (Repository) request.getAttribute(ATTRIBUTE_NAME);
-
- if (repository == null)
- {
- repository = manager.getFromRequest(request);
-
- if (repository != null)
- {
- request.setAttribute(ATTRIBUTE_NAME, repository);
- }
- }
+ if (request != null) {
+ return (Repository) request.getAttribute(ATTRIBUTE_NAME);
}
- return repository;
+ throw new IllegalStateException("request not found");
}
-
- //~--- fields ---------------------------------------------------------------
-
- /** Field description */
- private final RepositoryManager manager;
-
- /** Field description */
- private final Provider requestProvider;
}
diff --git a/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java b/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java
index 0d27c6d250..52dea4223b 100644
--- a/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java
+++ b/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java
@@ -61,8 +61,7 @@ public final class HealthChecker {
Repository repository = repositoryManager.get(id);
if (repository == null) {
- throw new RepositoryNotFoundException(
- "could not find repository with id ".concat(id));
+ throw new RepositoryNotFoundException(id);
}
doCheck(repository);
diff --git a/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java
index 225767cd3b..81bb2092c9 100644
--- a/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java
+++ b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java
@@ -3,12 +3,14 @@ package sonia.scm.security;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Pattern;
+import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
+
/**
* Created by masuewer on 04.07.18.
*/
public final class SecurityRequests {
- private static final Pattern URI_LOGIN_PATTERN = Pattern.compile("/api/rest(?:/v2)?/auth/access_token");
+ private static final Pattern URI_LOGIN_PATTERN = Pattern.compile(REST_API_PATH + "(?:/v2)?/auth/access_token");
private SecurityRequests() {}
diff --git a/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java b/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java
new file mode 100644
index 0000000000..bc085d2cc7
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java
@@ -0,0 +1,79 @@
+package sonia.scm.web.protocol;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpStatus;
+import sonia.scm.PushStateDispatcher;
+import sonia.scm.filter.WebElement;
+import sonia.scm.repository.DefaultRepositoryProvider;
+import sonia.scm.repository.NamespaceAndName;
+import sonia.scm.repository.RepositoryNotFoundException;
+import sonia.scm.repository.api.RepositoryService;
+import sonia.scm.repository.api.RepositoryServiceFactory;
+import sonia.scm.repository.spi.HttpScmProtocol;
+import sonia.scm.web.UserAgent;
+import sonia.scm.web.UserAgentParser;
+
+import javax.inject.Provider;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Optional;
+
+@Singleton
+@WebElement(value = HttpProtocolServlet.PATTERN)
+@Slf4j
+public class HttpProtocolServlet extends HttpServlet {
+
+ public static final String PATH = "/repo";
+ public static final String PATTERN = PATH + "/*";
+
+ private final RepositoryServiceFactory serviceFactory;
+
+ private final Provider requestProvider;
+
+ private final PushStateDispatcher dispatcher;
+ private final UserAgentParser userAgentParser;
+
+
+ @Inject
+ public HttpProtocolServlet(RepositoryServiceFactory serviceFactory, Provider requestProvider, PushStateDispatcher dispatcher, UserAgentParser userAgentParser) {
+ this.serviceFactory = serviceFactory;
+ this.requestProvider = requestProvider;
+ this.dispatcher = dispatcher;
+ this.userAgentParser = userAgentParser;
+ }
+
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ UserAgent userAgent = userAgentParser.parse(request);
+ if (userAgent.isBrowser()) {
+ log.trace("dispatch browser request for user agent {}", userAgent);
+ dispatcher.dispatch(request, response, request.getRequestURI());
+ } else {
+
+ String pathInfo = request.getPathInfo();
+ Optional namespaceAndName = NamespaceAndNameFromPathExtractor.fromUri(pathInfo);
+ if (namespaceAndName.isPresent()) {
+ service(request, response, namespaceAndName.get());
+ } else {
+ log.debug("namespace and name not found in request path {}", pathInfo);
+ response.setStatus(HttpStatus.SC_BAD_REQUEST);
+ }
+ }
+ }
+
+ private void service(HttpServletRequest req, HttpServletResponse resp, NamespaceAndName namespaceAndName) throws IOException, ServletException {
+ try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
+ requestProvider.get().setAttribute(DefaultRepositoryProvider.ATTRIBUTE_NAME, repositoryService.getRepository());
+ HttpScmProtocol protocol = repositoryService.getProtocol(HttpScmProtocol.class);
+ protocol.serve(req, resp, getServletConfig());
+ } catch (RepositoryNotFoundException e) {
+ log.debug("Repository not found for namespace and name {}", namespaceAndName, e);
+ resp.setStatus(HttpStatus.SC_NOT_FOUND);
+ }
+ }
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/web/protocol/NamespaceAndNameFromPathExtractor.java b/scm-webapp/src/main/java/sonia/scm/web/protocol/NamespaceAndNameFromPathExtractor.java
new file mode 100644
index 0000000000..22e2433561
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/web/protocol/NamespaceAndNameFromPathExtractor.java
@@ -0,0 +1,41 @@
+package sonia.scm.web.protocol;
+
+import sonia.scm.repository.NamespaceAndName;
+import sonia.scm.util.HttpUtil;
+
+import java.util.Optional;
+
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
+
+final class NamespaceAndNameFromPathExtractor {
+
+ private NamespaceAndNameFromPathExtractor() {}
+
+ static Optional fromUri(String uri) {
+ if (uri.startsWith(HttpUtil.SEPARATOR_PATH)) {
+ uri = uri.substring(1);
+ }
+
+ int endOfNamespace = uri.indexOf(HttpUtil.SEPARATOR_PATH);
+ if (endOfNamespace < 1) {
+ return empty();
+ }
+
+ String namespace = uri.substring(0, endOfNamespace);
+ int nameSeparatorIndex = uri.indexOf(HttpUtil.SEPARATOR_PATH, endOfNamespace + 1);
+ int nameIndex = nameSeparatorIndex > 0 ? nameSeparatorIndex : uri.length();
+ if (nameIndex == endOfNamespace + 1) {
+ return empty();
+ }
+
+ String name = uri.substring(endOfNamespace + 1, nameIndex);
+
+ int nameDotIndex = name.indexOf('.');
+ if (nameDotIndex >= 0) {
+ return of(new NamespaceAndName(namespace, name.substring(0, nameDotIndex)));
+ } else {
+ return of(new NamespaceAndName(namespace, name));
+ }
+ }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java
index c8d25ae82d..13ed9cddcd 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java
@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
@@ -92,7 +93,7 @@ public class BranchRootResourceTest extends RepositoryTestBase {
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper, changesetCollectionToDtoMapper);
- super.branchRootResource = MockProvider.of(branchRootResource);
+ super.branchRootResource = Providers.of(branchRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java
index e4e991a7d4..b80b62167b 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java
@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
@@ -79,7 +80,7 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
public void prepareEnvironment() throws Exception {
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
changesetRootResource = new ChangesetRootResource(serviceFactory, changesetCollectionToDtoMapper, changesetToChangesetDtoMapper);
- super.changesetRootResource = MockProvider.of(changesetRootResource);
+ super.changesetRootResource = Providers.of(changesetRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java
index f6a8fa4e09..fe22c944c4 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java
@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
@@ -63,7 +64,7 @@ public class DiffResourceTest extends RepositoryTestBase {
@Before
public void prepareEnvironment() throws Exception {
diffRootResource = new DiffRootResource(serviceFactory);
- super.diffRootResource = MockProvider.of(diffRootResource);
+ super.diffRootResource = Providers.of(diffRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java
index 778682f62d..934e05d0d1 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java
@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
@@ -81,7 +82,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
public void prepareEnvironment() throws Exception {
fileHistoryCollectionToDtoMapper = new FileHistoryCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
fileHistoryRootResource = new FileHistoryRootResource(serviceFactory, fileHistoryCollectionToDtoMapper);
- super.fileHistoryRootResource = MockProvider.of(fileHistoryRootResource);
+ super.fileHistoryRootResource = Providers.of(fileHistoryRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupCollectionToDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupCollectionToDtoMapperTest.java
index 5b457ce4e9..5066f56ff7 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupCollectionToDtoMapperTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupCollectionToDtoMapperTest.java
@@ -11,7 +11,6 @@ import org.junit.Test;
import sonia.scm.PageResult;
import sonia.scm.group.Group;
-import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
@@ -28,9 +27,9 @@ import static sonia.scm.PageResult.createPage;
public class GroupCollectionToDtoMapperTest {
- private final UriInfo uriInfo = mock(UriInfo.class);
- private final UriInfoStore uriInfoStore = new UriInfoStore();
- private final ResourceLinks resourceLinks = new ResourceLinks(uriInfoStore);
+ private final ScmPathInfo uriInfo = mock(ScmPathInfo.class);
+ private final ScmPathInfoStore scmPathInfoStore = new ScmPathInfoStore();
+ private final ResourceLinks resourceLinks = new ResourceLinks(scmPathInfoStore);
private final GroupToGroupDtoMapper groupToDtoMapper = mock(GroupToGroupDtoMapper.class);
private final Subject subject = mock(Subject.class);
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
@@ -41,10 +40,10 @@ public class GroupCollectionToDtoMapperTest {
@Before
public void init() throws URISyntaxException {
- uriInfoStore.set(uriInfo);
+ scmPathInfoStore.set(uriInfo);
URI baseUri = new URI("http://example.com/base/");
expectedBaseUri = baseUri.resolve(GroupRootResource.GROUPS_PATH_V2 + "/");
- when(uriInfo.getBaseUri()).thenReturn(baseUri);
+ when(uriInfo.getApiRestUri()).thenReturn(baseUri);
subjectThreadState.bind();
ThreadContext.bind(subject);
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java
index 1e42016ace..f662542ff7 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java
@@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
+import com.google.inject.util.Providers;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
@@ -73,7 +74,7 @@ public class GroupRootResourceTest {
GroupCollectionToDtoMapper groupCollectionToDtoMapper = new GroupCollectionToDtoMapper(groupToDtoMapper, resourceLinks);
GroupCollectionResource groupCollectionResource = new GroupCollectionResource(groupManager, dtoToGroupMapper, groupCollectionToDtoMapper, resourceLinks);
GroupResource groupResource = new GroupResource(groupManager, groupToDtoMapper, dtoToGroupMapper);
- GroupRootResource groupRootResource = new GroupRootResource(MockProvider.of(groupCollectionResource), MockProvider.of(groupResource));
+ GroupRootResource groupRootResource = new GroupRootResource(Providers.of(groupCollectionResource), Providers.of(groupResource));
dispatcher = createDispatcher(groupRootResource);
dispatcher.getProviderFactory().registerProviderInstance(new JSONContextResolver(new ObjectMapperProvider().get()));
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java
index 37c584dfdf..c84e1a21a7 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java
@@ -4,7 +4,6 @@ import org.junit.Before;
import org.junit.Test;
import javax.ws.rs.Path;
-import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.net.URISyntaxException;
@@ -37,7 +36,7 @@ public class LinkBuilderTest {
}
}
- private UriInfo uriInfo = mock(UriInfo.class);
+ private ScmPathInfo uriInfo = mock(ScmPathInfo.class);
@Test
public void shouldBuildSimplePath() {
@@ -94,6 +93,6 @@ public class LinkBuilderTest {
@Before
public void setBaseUri() throws URISyntaxException {
- when(uriInfo.getBaseUri()).thenReturn(new URI("http://example.com/"));
+ when(uriInfo.getApiRestUri()).thenReturn(new URI("http://example.com/"));
}
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java
index b0109d86b7..1dc5049fd9 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java
@@ -17,7 +17,6 @@ import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.net.URISyntaxException;
@@ -45,9 +44,9 @@ public class MeResourceTest {
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
@Mock
- private UriInfo uriInfo;
+ private ScmPathInfo uriInfo;
@Mock
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@Mock
private UserManager userManager;
@@ -73,9 +72,9 @@ public class MeResourceTest {
when(userManager.getDefaultType()).thenReturn("xml");
userToDtoMapper.setResourceLinks(resourceLinks);
MeResource meResource = new MeResource(userToDtoMapper, userManager, passwordService);
+ when(uriInfo.getApiRestUri()).thenReturn(URI.create("/"));
+ when(scmPathInfoStore.get()).thenReturn(uriInfo);
dispatcher = createDispatcher(meResource);
- when(uriInfo.getBaseUri()).thenReturn(URI.create("/"));
- when(uriInfoStore.get()).thenReturn(uriInfo);
}
@Test
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MockProvider.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MockProvider.java
deleted file mode 100644
index bf84e4fe15..0000000000
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MockProvider.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package sonia.scm.api.v2.resources;
-
-import javax.inject.Provider;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * A mockito implementation of CDI {@link javax.inject.Provider}.
- */
-class MockProvider {
-
- private MockProvider() {}
-
- static Provider of(I instance) {
- @SuppressWarnings("unchecked") // Can't make mockito return typed provider
- Provider provider = mock(Provider.class);
- when(provider.get()).thenReturn(instance);
- return provider;
- }
-
-}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MockScmProtocol.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MockScmProtocol.java
new file mode 100644
index 0000000000..1ae5344849
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MockScmProtocol.java
@@ -0,0 +1,23 @@
+package sonia.scm.api.v2.resources;
+
+import sonia.scm.repository.api.ScmProtocol;
+
+class MockScmProtocol implements ScmProtocol {
+ private final String type;
+ private final String protocol;
+
+ public MockScmProtocol(String type, String protocol) {
+ this.type = type;
+ this.protocol = protocol;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String getUrl() {
+ return protocol;
+ }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java
index f8b555f180..64942fb84b 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java
@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
@@ -71,7 +72,7 @@ public class ModificationsResourceTest extends RepositoryTestBase {
@Before
public void prepareEnvironment() throws Exception {
modificationsRootResource = new ModificationsRootResource(serviceFactory, modificationsToDtoMapper);
- super.modificationsRootResource = MockProvider.of(modificationsRootResource);
+ super.modificationsRootResource = Providers.of(modificationsRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java
index ac5ad07cf8..563e9bdbc0 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.collect.ImmutableList;
+import com.google.inject.util.Providers;
import de.otto.edison.hal.HalRepresentation;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
@@ -137,7 +138,7 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
initMocks(this);
permissionCollectionToDtoMapper = new PermissionCollectionToDtoMapper(permissionToPermissionDtoMapper, resourceLinks);
permissionRootResource = new PermissionRootResource(permissionDtoToPermissionMapper, permissionToPermissionDtoMapper, permissionCollectionToDtoMapper, resourceLinks, repositoryManager);
- super.permissionRootResource = MockProvider.of(permissionRootResource);
+ super.permissionRootResource = Providers.of(permissionRootResource);
dispatcher = createDispatcher(getRepositoryRootResource());
subjectThreadState.bind();
ThreadContext.bind(subject);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java
index 7c6deedf65..0b2e50b6d9 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java
@@ -3,13 +3,13 @@ package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
+import com.google.inject.util.Providers;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
@@ -20,6 +20,7 @@ import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryIsNotArchivedException;
import sonia.scm.repository.RepositoryManager;
+import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.web.VndMediaType;
@@ -30,6 +31,7 @@ import java.net.URISyntaxException;
import java.net.URL;
import static java.util.Collections.singletonList;
+import static java.util.stream.Stream.of;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
@@ -64,8 +66,14 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
@Mock
private RepositoryManager repositoryManager;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ @Mock
private RepositoryServiceFactory serviceFactory;
+ @Mock
+ private RepositoryService service;
+ @Mock
+ private ScmPathInfoStore scmPathInfoStore;
+ @Mock
+ private ScmPathInfo uriInfo;
private final URI baseUri = URI.create("/");
@@ -83,8 +91,11 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
super.dtoToRepositoryMapper = dtoToRepositoryMapper;
super.manager = repositoryManager;
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
- super.repositoryCollectionResource = MockProvider.of(new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks));
+ super.repositoryCollectionResource = Providers.of(new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks));
dispatcher = createDispatcher(getRepositoryRootResource());
+ when(serviceFactory.create(any(Repository.class))).thenReturn(service);
+ when(scmPathInfoStore.get()).thenReturn(uriInfo);
+ when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
}
@Test
@@ -268,6 +279,20 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
assertFalse(modifiedRepositoryCaptor.getValue().getPermissions().isEmpty());
}
+ @Test
+ public void shouldCreateArrayOfProtocolUrls() throws Exception {
+ mockRepository("space", "repo");
+ when(service.getSupportedProtocols()).thenReturn(of(new MockScmProtocol("http", "http://"), new MockScmProtocol("ssh", "ssh://")));
+
+ MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
+ MockHttpResponse response = new MockHttpResponse();
+
+ dispatcher.invoke(request, response);
+
+ assertEquals(SC_OK, response.getStatus());
+ assertTrue(response.getContentAsString().contains("\"protocol\":[{\"href\":\"http://\",\"name\":\"http\"},{\"href\":\"ssh://\",\"name\":\"ssh\"}]"));
+ }
+
private PageResult createSingletonPageResult(Repository repository) {
return new PageResult<>(singletonList(repository), 0);
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java
index c3cc56958a..3d3b28ae51 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTestBase.java
@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import sonia.scm.repository.RepositoryManager;
import javax.inject.Provider;
@@ -23,7 +24,7 @@ public abstract class RepositoryTestBase {
RepositoryRootResource getRepositoryRootResource() {
- return new RepositoryRootResource(MockProvider.of(new RepositoryResource(
+ return new RepositoryRootResource(Providers.of(new RepositoryResource(
repositoryToDtoMapper,
dtoToRepositoryMapper,
manager,
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java
index 0c77d40023..2e6048d6b8 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java
@@ -7,7 +7,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.repository.HealthCheckFailure;
@@ -15,13 +14,17 @@ import sonia.scm.repository.Permission;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository;
import sonia.scm.repository.api.Command;
+import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
+import sonia.scm.repository.api.ScmProtocol;
import java.net.URI;
import static java.util.Collections.singletonList;
+import static java.util.stream.Stream.of;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -39,8 +42,14 @@ public class RepositoryToRepositoryDtoMapperTest {
private final URI baseUri = URI.create("http://example.com/base/");
@SuppressWarnings("unused") // Is injected
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ @Mock
private RepositoryServiceFactory serviceFactory;
+ @Mock
+ private RepositoryService repositoryService;
+ @Mock
+ private ScmPathInfoStore scmPathInfoStore;
+ @Mock
+ private ScmPathInfo uriInfo;
@InjectMocks
private RepositoryToRepositoryDtoMapperImpl mapper;
@@ -48,7 +57,11 @@ public class RepositoryToRepositoryDtoMapperTest {
@Before
public void init() {
initMocks(this);
- when(serviceFactory.create(any(Repository.class)).isSupported(any(Command.class))).thenReturn(true);
+ when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
+ when(repositoryService.isSupported(any(Command.class))).thenReturn(true);
+ when(repositoryService.getSupportedProtocols()).thenReturn(of());
+ when(scmPathInfoStore.get()).thenReturn(uriInfo);
+ when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
}
@After
@@ -129,14 +142,14 @@ public class RepositoryToRepositoryDtoMapperTest {
@Test
public void shouldNotCreateTagsLink_ifNotSupported() {
- when(serviceFactory.create(any(Repository.class)).isSupported(Command.TAGS)).thenReturn(false);
+ when(repositoryService.isSupported(Command.TAGS)).thenReturn(false);
RepositoryDto dto = mapper.map(createTestRepository());
assertFalse(dto.getLinks().getLinkBy("tags").isPresent());
}
@Test
public void shouldNotCreateBranchesLink_ifNotSupported() {
- when(serviceFactory.create(any(Repository.class)).isSupported(Command.BRANCHES)).thenReturn(false);
+ when(repositoryService.isSupported(Command.BRANCHES)).thenReturn(false);
RepositoryDto dto = mapper.map(createTestRepository());
assertFalse(dto.getLinks().getLinkBy("branches").isPresent());
}
@@ -165,6 +178,43 @@ public class RepositoryToRepositoryDtoMapperTest {
dto.getLinks().getLinkBy("permissions").get().getHref());
}
+ @Test
+ public void shouldCreateCorrectProtocolLinks() {
+ when(repositoryService.getSupportedProtocols()).thenReturn(
+ of(mockProtocol("http", "http://scm"), mockProtocol("other", "some://protocol"))
+ );
+
+ RepositoryDto dto = mapper.map(createTestRepository());
+ assertTrue("should contain http link", dto.getLinks().stream().anyMatch(l -> l.getName().equals("http") && l.getHref().equals("http://scm")));
+ assertTrue("should contain other link", dto.getLinks().stream().anyMatch(l -> l.getName().equals("other") && l.getHref().equals("some://protocol")));
+ }
+
+ @Test
+ @SubjectAware(username = "community")
+ public void shouldCreateProtocolLinksForPullPermission() {
+ when(repositoryService.getSupportedProtocols()).thenReturn(
+ of(mockProtocol("http", "http://scm"), mockProtocol("other", "some://protocol"))
+ );
+
+ RepositoryDto dto = mapper.map(createTestRepository());
+ assertEquals(2, dto.getLinks().getLinksBy("protocol").size());
+ }
+
+ @Test
+ @SubjectAware(username = "unpriv")
+ public void shouldNotCreateProtocolLinksWithoutPullPermission() {
+ when(repositoryService.getSupportedProtocols()).thenReturn(
+ of(mockProtocol("http", "http://scm"), mockProtocol("other", "some://protocol"))
+ );
+
+ RepositoryDto dto = mapper.map(createTestRepository());
+ assertTrue(dto.getLinks().getLinksBy("protocol").isEmpty());
+ }
+
+ private ScmProtocol mockProtocol(String type, String protocol) {
+ return new MockScmProtocol(type, protocol);
+ }
+
private Repository createTestRepository() {
Repository repository = new Repository();
repository.setNamespace("testspace");
@@ -179,4 +229,5 @@ public class RepositoryToRepositoryDtoMapperTest {
return repository;
}
+
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java
index 9adca13225..2476785d70 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java
@@ -2,6 +2,7 @@ package sonia.scm.api.v2.resources;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.inject.util.Providers;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
@@ -22,8 +23,10 @@ import java.util.List;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_OK;
-import static org.junit.Assert.*;
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class)
@@ -52,7 +55,7 @@ public class RepositoryTypeRootResourceTest {
RepositoryTypeCollectionToDtoMapper collectionMapper = new RepositoryTypeCollectionToDtoMapper(mapper, resourceLinks);
RepositoryTypeCollectionResource collectionResource = new RepositoryTypeCollectionResource(repositoryManager, collectionMapper);
RepositoryTypeResource resource = new RepositoryTypeResource(repositoryManager, mapper);
- RepositoryTypeRootResource rootResource = new RepositoryTypeRootResource(MockProvider.of(collectionResource), MockProvider.of(resource));
+ RepositoryTypeRootResource rootResource = new RepositoryTypeRootResource(Providers.of(collectionResource), Providers.of(resource));
dispatcher.getRegistry().addSingletonResource(rootResource);
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java
index 5e5897eb2a..c70510fe39 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java
@@ -1,6 +1,5 @@
package sonia.scm.api.v2.resources;
-import javax.ws.rs.core.UriInfo;
import java.net.URI;
import static org.mockito.Mockito.mock;
@@ -10,8 +9,8 @@ public class ResourceLinksMock {
public static ResourceLinks createMock(URI baseUri) {
ResourceLinks resourceLinks = mock(ResourceLinks.class);
- UriInfo uriInfo = mock(UriInfo.class);
- when(uriInfo.getBaseUri()).thenReturn(baseUri);
+ ScmPathInfo uriInfo = mock(ScmPathInfo.class);
+ when(uriInfo.getApiRestUri()).thenReturn(baseUri);
ResourceLinks.UserLinks userLinks = new ResourceLinks.UserLinks(uriInfo);
when(resourceLinks.user()).thenReturn(userLinks);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java
index a9209828c3..0544bf6a0d 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java
@@ -6,7 +6,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.repository.NamespaceAndName;
-import javax.ws.rs.core.UriInfo;
import java.net.URI;
import static org.junit.Assert.assertEquals;
@@ -18,9 +17,9 @@ public class ResourceLinksTest {
private static final String BASE_URL = "http://example.com/";
@Mock
- private UriInfoStore uriInfoStore;
+ private ScmPathInfoStore scmPathInfoStore;
@Mock
- private UriInfo uriInfo;
+ private ScmPathInfo uriInfo;
@InjectMocks
private ResourceLinks resourceLinks;
@@ -177,7 +176,7 @@ public class ResourceLinksTest {
@Before
public void initUriInfo() {
initMocks(this);
- when(uriInfoStore.get()).thenReturn(uriInfo);
- when(uriInfo.getBaseUri()).thenReturn(URI.create(BASE_URL));
+ when(scmPathInfoStore.get()).thenReturn(uriInfo);
+ when(uriInfo.getApiRestUri()).thenReturn(URI.create(BASE_URL));
}
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ScmPathInfoStoreTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ScmPathInfoStoreTest.java
new file mode 100644
index 0000000000..544a918b8b
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ScmPathInfoStoreTest.java
@@ -0,0 +1,35 @@
+package sonia.scm.api.v2.resources;
+
+import org.junit.Test;
+
+import java.net.URI;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ScmPathInfoStoreTest {
+
+ @Test
+ public void shouldReturnSetInfo() {
+ URI someUri = URI.create("/anything");
+
+ ScmPathInfo uriInfo = mock(ScmPathInfo.class);
+ ScmPathInfoStore scmPathInfoStore = new ScmPathInfoStore();
+
+ when(uriInfo.getApiRestUri()).thenReturn(someUri);
+
+ scmPathInfoStore.set(uriInfo);
+
+ assertSame(someUri, scmPathInfoStore.get().getApiRestUri());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void shouldFailIfSetTwice() {
+ ScmPathInfo uriInfo = mock(ScmPathInfo.class);
+ ScmPathInfoStore scmPathInfoStore = new ScmPathInfoStore();
+
+ scmPathInfoStore.set(uriInfo);
+ scmPathInfoStore.set(uriInfo);
+ }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java
index 4759e1ebd7..c84a74bc92 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java
@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
@@ -63,7 +64,7 @@ public class SourceRootResourceTest extends RepositoryTestBase {
when(fileObjectToFileObjectDtoMapper.map(any(FileObject.class), any(NamespaceAndName.class), anyString())).thenReturn(dto);
SourceRootResource sourceRootResource = new SourceRootResource(serviceFactory, browserResultToBrowserResultDtoMapper);
- super.sourceRootResource = MockProvider.of(sourceRootResource);
+ super.sourceRootResource = Providers.of(sourceRootResource);
dispatcher = createDispatcher(getRepositoryRootResource());
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java
index 3149182d98..5f49f31183 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java
@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
+import com.google.inject.util.Providers;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
@@ -71,7 +72,7 @@ public class TagRootResourceTest extends RepositoryTestBase {
public void prepareEnvironment() throws Exception {
tagCollectionToDtoMapper = new TagCollectionToDtoMapper(resourceLinks, tagToTagDtoMapper);
tagRootResource = new TagRootResource(serviceFactory, tagCollectionToDtoMapper, tagToTagDtoMapper);
- super.tagRootResource = MockProvider.of(tagRootResource);
+ super.tagRootResource = Providers.of(tagRootResource);
dispatcher = createDispatcher(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UriInfoStoreTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UriInfoStoreTest.java
deleted file mode 100644
index 559e701ae3..0000000000
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UriInfoStoreTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package sonia.scm.api.v2.resources;
-
-import org.junit.Test;
-
-import javax.ws.rs.core.UriInfo;
-
-import static org.junit.Assert.assertSame;
-import static org.mockito.Mockito.mock;
-
-public class UriInfoStoreTest {
-
- @Test
- public void shouldReturnSetInfo() {
- UriInfo uriInfo = mock(UriInfo.class);
- UriInfoStore uriInfoStore = new UriInfoStore();
-
- uriInfoStore.set(uriInfo);
-
- assertSame(uriInfo, uriInfoStore.get());
- }
-
- @Test(expected = IllegalStateException.class)
- public void shouldFailIfSetTwice() {
- UriInfo uriInfo = mock(UriInfo.class);
- UriInfoStore uriInfoStore = new UriInfoStore();
-
- uriInfoStore.set(uriInfo);
- uriInfoStore.set(uriInfo);
- }
-}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java
index 35cd755a5d..2cd1e01989 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java
@@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
+import com.google.inject.util.Providers;
import org.apache.shiro.authc.credential.PasswordService;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockHttpRequest;
@@ -78,8 +79,8 @@ public class UserRootResourceTest {
UserCollectionResource userCollectionResource = new UserCollectionResource(userManager, dtoToUserMapper,
userCollectionToDtoMapper, resourceLinks, passwordService);
UserResource userResource = new UserResource(dtoToUserMapper, userToDtoMapper, userManager, passwordService);
- UserRootResource userRootResource = new UserRootResource(MockProvider.of(userCollectionResource),
- MockProvider.of(userResource));
+ UserRootResource userRootResource = new UserRootResource(Providers.of(userCollectionResource),
+ Providers.of(userResource));
dispatcher = createDispatcher(userRootResource);
}
diff --git a/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java b/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java
index 2452afb909..c55a33c39a 100644
--- a/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java
+++ b/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java
@@ -36,33 +36,38 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import com.google.common.base.Charsets;
+import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import sonia.scm.api.rest.ObjectMapperProvider;
import sonia.scm.api.v2.resources.RepositoryDto;
+import sonia.scm.api.v2.resources.UserDto;
+import sonia.scm.api.v2.resources.UserToUserDtoMapperImpl;
import sonia.scm.repository.PermissionType;
-import sonia.scm.repository.Repository;
import sonia.scm.user.User;
import sonia.scm.user.UserTestData;
+import sonia.scm.util.HttpUtil;
+import sonia.scm.web.VndMediaType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.IOException;
+import java.net.URI;
import java.util.UUID;
import static org.junit.Assert.assertArrayEquals;
import static sonia.scm.it.IntegrationTestUtil.BASE_URL;
import static sonia.scm.it.IntegrationTestUtil.REST_BASE_URL;
import static sonia.scm.it.IntegrationTestUtil.createAdminClient;
+import static sonia.scm.it.IntegrationTestUtil.createResource;
import static sonia.scm.it.IntegrationTestUtil.readJson;
import static sonia.scm.it.RepositoryITUtil.createRepository;
import static sonia.scm.it.RepositoryITUtil.deleteRepository;
@@ -111,7 +116,6 @@ public class GitLfsITCase {
}
@Test
- @Ignore("permissions not yet implemented")
public void testLfsAPIWithOwnerPermissions() throws IOException {
uploadAndDownloadAsUser(PermissionType.OWNER);
}
@@ -122,9 +126,11 @@ public class GitLfsITCase {
createUser(trillian);
try {
- // TODO enable when permissions are implemented in v2
-// repository.getPermissions().add(new Permission(trillian.getId(), permissionType));
-// modifyRepository(repository);
+ String permissionsUrl = repository.getLinks().getLinkBy("permissions").get().getHref();
+ IntegrationTestUtil.createResource(adminClient, URI.create(permissionsUrl))
+ .accept("*/*")
+ .type(VndMediaType.PERMISSION)
+ .post(ClientResponse.class, "{\"name\": \""+ trillian.getId() +"\", \"type\":\"WRITE\"}");
ScmClient client = new ScmClient(trillian.getId(), "secret123");
@@ -135,25 +141,27 @@ public class GitLfsITCase {
}
@Test
- @Ignore("permissions not yet implemented")
public void testLfsAPIWithWritePermissions() throws IOException {
uploadAndDownloadAsUser(PermissionType.WRITE);
}
private void createUser(User user) {
- adminClient.resource(REST_BASE_URL + "users.json").post(user);
- }
-
- private void modifyRepository(Repository repository) {
- adminClient.resource(REST_BASE_URL + "repositories/" + repository.getId() + ".json").put(repository);
+ UserDto dto = new UserToUserDtoMapperImpl(){
+ @Override
+ protected void appendLinks(User user, UserDto target) {}
+ }.map(user);
+ dto.setPassword(user.getPassword());
+ createResource(adminClient, "users")
+ .accept("*/*")
+ .type(VndMediaType.USER)
+ .post(ClientResponse.class, dto);
}
private void removeUser(User user) {
- adminClient.resource(REST_BASE_URL + "users/" + user.getId() + ".json").delete();
+ adminClient.resource(REST_BASE_URL + "users/" + user.getId()).delete();
}
@Test
- @Ignore("permissions not yet implemented")
public void testLfsAPIWithoutWritePermissions() throws IOException {
User trillian = UserTestData.createTrillian();
trillian.setPassword("secret123");
@@ -164,9 +172,11 @@ public class GitLfsITCase {
try {
- // TODO enable when permissions are implemented in v2
-// repository.getPermissions().add(new Permission(trillian.getId(), PermissionType.READ));
-// modifyRepository(repository);
+ String permissionsUrl = repository.getLinks().getLinkBy("permissions").get().getHref();
+ IntegrationTestUtil.createResource(adminClient, URI.create(permissionsUrl))
+ .accept("*/*")
+ .type(VndMediaType.PERMISSION)
+ .post(ClientResponse.class, "{\"name\": \""+ trillian.getId() +"\", \"type\":\"READ\"}");
ScmClient client = new ScmClient(trillian.getId(), "secret123");
uploadAndDownload(client);
@@ -176,7 +186,6 @@ public class GitLfsITCase {
}
@Test
- @Ignore("permissions not yet implemented")
public void testLfsDownloadWithReadPermissions() throws IOException {
User trillian = UserTestData.createTrillian();
trillian.setPassword("secret123");
@@ -184,9 +193,11 @@ public class GitLfsITCase {
try {
- // TODO enable when permissions are implemented in v2
-// repository.getPermissions().add(new Permission(trillian.getId(), PermissionType.READ));
-// modifyRepository(repository);
+ String permissionsUrl = repository.getLinks().getLinkBy("permissions").get().getHref();
+ IntegrationTestUtil.createResource(adminClient, URI.create(permissionsUrl))
+ .accept("*/*")
+ .type(VndMediaType.PERMISSION)
+ .post(ClientResponse.class, "{\"name\": \""+ trillian.getId() +"\", \"type\":\"READ\"}");
// upload data as admin
String data = UUID.randomUUID().toString();
@@ -221,7 +232,7 @@ public class GitLfsITCase {
LfsResponseBody response = request(client, request);
String uploadURL = response.objects[0].actions.upload.href;
- client.resource(uploadURL).put(data);
+ client.resource(uploadURL).header(HttpUtil.HEADER_USERAGENT, "git-lfs/z").put(data);
return lfsObject;
}
@@ -233,14 +244,14 @@ public class GitLfsITCase {
String json = client
.resource(batchUrl)
.accept("application/vnd.git-lfs+json")
+ .header(HttpUtil.HEADER_USERAGENT, "git-lfs/z")
.header("Content-Type", "application/vnd.git-lfs+json")
.post(String.class, requestAsString);
return new ObjectMapperProvider().get().readValue(json, LfsResponseBody.class);
}
private String createBatchUrl() {
- String url = BASE_URL + "git/" + repository.getNamespace() + "/" + repository.getName();
- return url + "/info/lfs/objects/batch";
+ return String.format("%srepo/%s/%s/info/lfs/objects/batch", BASE_URL, repository.getNamespace(), repository.getName());
}
private byte[] download(ScmClient client, LfsObject lfsObject) throws IOException {
@@ -248,7 +259,7 @@ public class GitLfsITCase {
LfsResponseBody response = request(client, request);
String downloadUrl = response.objects[0].actions.download.href;
- return client.resource(downloadUrl).get(byte[].class);
+ return client.resource(downloadUrl).header(HttpUtil.HEADER_USERAGENT, "git-lfs/z").get(byte[].class);
}
private LfsObject createLfsObject(byte[] data) {
diff --git a/scm-webapp/src/test/java/sonia/scm/it/GitRepositoryPathMatcherITCase.java b/scm-webapp/src/test/java/sonia/scm/it/GitRepositoryPathMatcherITCase.java
index 13cae15907..9e75857f08 100644
--- a/scm-webapp/src/test/java/sonia/scm/it/GitRepositoryPathMatcherITCase.java
+++ b/scm-webapp/src/test/java/sonia/scm/it/GitRepositoryPathMatcherITCase.java
@@ -100,7 +100,7 @@ public class GitRepositoryPathMatcherITCase {
// tests end
private String createUrl() {
- return BASE_URL + "git/" + repository.getNamespace() + "/" + repository.getName();
+ return BASE_URL + "repo/" + repository.getNamespace() + "/" + repository.getName();
}
private void cloneAndPush( String url ) throws IOException {
diff --git a/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java b/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java
index dae80cd00d..d74769bf39 100644
--- a/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java
+++ b/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java
@@ -172,7 +172,7 @@ public class RepositoryHookITCase extends AbstractAdminITCaseBase
Thread.sleep(WAIT_TIME);
// check debug servlet that only one commit is present
- WebResource.Builder wr = createResource(client, "../debug/" + repository.getNamespace() + "/" + repository.getName() + "/post-receive/last");
+ WebResource.Builder wr = createResource(client, String.format("../debug/%s/%s/post-receive/last", repository.getNamespace(), repository.getName()));
DebugHookData data = wr.get(DebugHookData.class);
assertNotNull(data);
assertThat(data.getChangesets(), allOf(
@@ -195,8 +195,8 @@ public class RepositoryHookITCase extends AbstractAdminITCaseBase
private RepositoryClient createRepositoryClient() throws IOException
{
- return REPOSITORY_CLIENT_FACTORY.create(repositoryType,
- IntegrationTestUtil.BASE_URL + repositoryType + "/" + repository.getNamespace() + "/" + repository.getName(),
+ return REPOSITORY_CLIENT_FACTORY.create(repositoryType,
+ String.format("%srepo/%s/%s", IntegrationTestUtil.BASE_URL, repository.getNamespace(), repository.getName()),
IntegrationTestUtil.ADMIN_USERNAME, IntegrationTestUtil.ADMIN_PASSWORD, workingCopy
);
}
diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java
index 5f4ea2fa72..448c2561f3 100644
--- a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java
@@ -54,7 +54,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.SCMContextProvider;
-import sonia.scm.Type;
import sonia.scm.cache.GuavaCacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.AuthorizationCollector;
@@ -120,7 +119,6 @@ public class DefaultRepositoryManagerPerfTest {
keyGenerator,
repositoryDAO,
handlerSet,
- repositoryMatcher,
namespaceStrategy
);
diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java
index efd3f673b0..b7d231cf38 100644
--- a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java
@@ -61,7 +61,6 @@ import sonia.scm.store.ConfigurationStoreFactory;
import sonia.scm.store.JAXBConfigurationStoreFactory;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
@@ -383,69 +382,6 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase {
assertEquals("default_namespace", repository.getNamespace());
}
- @Test
- public void getRepositoryFromRequestUri_withoutLeadingSlash() throws AlreadyExistsException {
- RepositoryManager m = createManager();
- m.init(contextProvider);
-
- createUriTestRepositories(m);
-
- assertEquals("scm-test", m.getFromUri("hg/namespace/scm-test").getName());
- assertEquals("namespace", m.getFromUri("hg/namespace/scm-test").getNamespace());
- }
-
- @Test
- public void getRepositoryFromRequestUri_withLeadingSlash() throws AlreadyExistsException {
- RepositoryManager m = createManager();
- m.init(contextProvider);
-
- createUriTestRepositories(m);
-
- assertEquals("scm-test", m.getFromUri("/hg/namespace/scm-test").getName());
- assertEquals("namespace", m.getFromUri("/hg/namespace/scm-test").getNamespace());
- }
-
- @Test
- public void getRepositoryFromRequestUri_withPartialName() throws AlreadyExistsException {
- RepositoryManager m = createManager();
- m.init(contextProvider);
-
- createUriTestRepositories(m);
-
- assertEquals("scm", m.getFromUri("hg/namespace/scm").getName());
- assertEquals("namespace", m.getFromUri("hg/namespace/scm").getNamespace());
- }
-
- @Test
- public void getRepositoryFromRequestUri_withTrailingFilePath() throws AlreadyExistsException {
- RepositoryManager m = createManager();
- m.init(contextProvider);
-
- createUriTestRepositories(m);
-
- assertEquals("test-1", m.getFromUri("/git/namespace/test-1/ka/some/path").getName());
- }
-
- @Test
- public void getRepositoryFromRequestUri_forNotExistingRepositoryName() throws AlreadyExistsException {
- RepositoryManager m = createManager();
- m.init(contextProvider);
-
- createUriTestRepositories(m);
-
- assertNull(m.getFromUri("/git/namespace/test-3/ka/some/path"));
- }
-
- @Test
- public void getRepositoryFromRequestUri_forWrongNamespace() throws AlreadyExistsException {
- RepositoryManager m = createManager();
- m.init(contextProvider);
-
- createUriTestRepositories(m);
-
- assertNull(m.getFromUri("/git/other/other/test-2"));
- }
-
@Test
public void shouldSetNamespace() throws AlreadyExistsException {
Repository repository = new Repository(null, "hg", null, "scm");
@@ -504,7 +440,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase {
when(namespaceStrategy.createNamespace(Mockito.any(Repository.class))).thenAnswer(invocation -> mockedNamespace);
return new DefaultRepositoryManager(configuration, contextProvider,
- keyGenerator, repositoryDAO, handlerSet, createRepositoryMatcher(), namespaceStrategy);
+ keyGenerator, repositoryDAO, handlerSet, namespaceStrategy);
}
private void createRepository(RepositoryManager m, Repository repository) throws AlreadyExistsException {
@@ -530,10 +466,6 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase {
assertEquals(repo.getLastModified(), other.getLastModified());
}
- private RepositoryMatcher createRepositoryMatcher() {
- return new RepositoryMatcher(Collections.emptySet());
- }
-
private Repository createRepository(Repository repository) throws AlreadyExistsException {
manager.create(repository);
assertNotNull(repository.getId());
diff --git a/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java b/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java
new file mode 100644
index 0000000000..077020f60c
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java
@@ -0,0 +1,114 @@
+package sonia.scm.web.protocol;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import sonia.scm.PushStateDispatcher;
+import sonia.scm.repository.DefaultRepositoryProvider;
+import sonia.scm.repository.NamespaceAndName;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.RepositoryNotFoundException;
+import sonia.scm.repository.api.RepositoryService;
+import sonia.scm.repository.api.RepositoryServiceFactory;
+import sonia.scm.repository.spi.HttpScmProtocol;
+import sonia.scm.web.UserAgent;
+import sonia.scm.web.UserAgentParser;
+
+import javax.inject.Provider;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+public class HttpProtocolServletTest {
+
+
+ @Mock
+ private RepositoryServiceFactory serviceFactory;
+ @Mock
+ private HttpServletRequest httpServletRequest;
+ @Mock
+ private PushStateDispatcher dispatcher;
+ @Mock
+ private UserAgentParser userAgentParser;
+ @Mock
+ private Provider requestProvider;
+
+ @InjectMocks
+ private HttpProtocolServlet servlet;
+
+ @Mock
+ private RepositoryService repositoryService;
+ @Mock
+ private UserAgent userAgent;
+
+ @Mock
+ private HttpServletRequest request;
+ @Mock
+ private HttpServletResponse response;
+ @Mock
+ private HttpScmProtocol protocol;
+
+ @Before
+ public void init() throws RepositoryNotFoundException {
+ initMocks(this);
+ when(userAgentParser.parse(request)).thenReturn(userAgent);
+ when(userAgent.isBrowser()).thenReturn(false);
+ NamespaceAndName existingRepo = new NamespaceAndName("space", "repo");
+ when(serviceFactory.create(not(eq(existingRepo)))).thenThrow(RepositoryNotFoundException.class);
+ when(serviceFactory.create(existingRepo)).thenReturn(repositoryService);
+ when(requestProvider.get()).thenReturn(httpServletRequest);
+ }
+
+ @Test
+ public void shouldDispatchBrowserRequests() throws ServletException, IOException {
+ when(userAgent.isBrowser()).thenReturn(true);
+ when(request.getRequestURI()).thenReturn("uri");
+
+ servlet.service(request, response);
+
+ verify(dispatcher).dispatch(request, response, "uri");
+ }
+
+ @Test
+ public void shouldHandleBadPaths() throws IOException, ServletException {
+ when(request.getPathInfo()).thenReturn("/illegal");
+
+ servlet.service(request, response);
+
+ verify(response).setStatus(400);
+ }
+
+ @Test
+ public void shouldHandleNotExistingRepository() throws IOException, ServletException {
+ when(request.getPathInfo()).thenReturn("/not/exists");
+
+ servlet.service(request, response);
+
+ verify(response).setStatus(404);
+ }
+
+ @Test
+ public void shouldDelegateToProvider() throws RepositoryNotFoundException, IOException, ServletException {
+ when(request.getPathInfo()).thenReturn("/space/name");
+ NamespaceAndName namespaceAndName = new NamespaceAndName("space", "name");
+ doReturn(repositoryService).when(serviceFactory).create(namespaceAndName);
+ Repository repository = new Repository();
+ when(repositoryService.getRepository()).thenReturn(repository);
+ when(repositoryService.getProtocol(HttpScmProtocol.class)).thenReturn(protocol);
+
+ servlet.service(request, response);
+
+ verify(httpServletRequest).setAttribute(DefaultRepositoryProvider.ATTRIBUTE_NAME, repository);
+ verify(protocol).serve(request, response, null);
+ verify(repositoryService).close();
+ }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/web/protocol/NamespaceAndNameFromPathExtractorTest.java b/scm-webapp/src/test/java/sonia/scm/web/protocol/NamespaceAndNameFromPathExtractorTest.java
new file mode 100644
index 0000000000..0998010069
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/web/protocol/NamespaceAndNameFromPathExtractorTest.java
@@ -0,0 +1,68 @@
+package sonia.scm.web.protocol;
+
+import org.junit.jupiter.api.DynamicNode;
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.TestFactory;
+import sonia.scm.repository.NamespaceAndName;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.DynamicTest.dynamicTest;
+
+public class NamespaceAndNameFromPathExtractorTest {
+ @TestFactory
+ Stream shouldExtractCorrectNamespaceAndName() {
+ return Stream.of(
+ "/space/repo",
+ "/space/repo/",
+ "/space/repo/here",
+ "/space/repo/here/there",
+ "space/repo",
+ "space/repo/",
+ "space/repo/here/there"
+ ).map(this::createCorrectTest);
+ }
+
+ @TestFactory
+ Stream shouldHandleTrailingDotSomethings() {
+ return Stream.of(
+ "/space/repo.git",
+ "/space/repo.and.more",
+ "/space/repo."
+ ).map(this::createCorrectTest);
+ }
+
+ private DynamicTest createCorrectTest(String path) {
+ return dynamicTest(
+ "should extract correct namespace and name for path " + path,
+ () -> {
+ Optional namespaceAndName = NamespaceAndNameFromPathExtractor.fromUri(path);
+
+ assertThat(namespaceAndName.get()).isEqualTo(new NamespaceAndName("space", "repo"));
+ }
+ );
+ }
+
+ @TestFactory
+ Stream shouldHandleMissingParts() {
+ return Stream.of(
+ "",
+ "/",
+ "/space",
+ "/space/"
+ ).map(this::createFailureTest);
+ }
+
+ private DynamicTest createFailureTest(String path) {
+ return dynamicTest(
+ "should not fail for wrong path " + path,
+ () -> {
+ Optional namespaceAndName = NamespaceAndNameFromPathExtractor.fromUri(path);
+
+ assertThat(namespaceAndName.isPresent()).isFalse();
+ }
+ );
+ }
+}
diff --git a/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini b/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini
index 5073bf398d..9a39a2d46c 100644
--- a/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini
+++ b/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini
@@ -3,9 +3,11 @@ trillian = secret, admin
dent = secret, creator, heartOfGold, puzzle42
unpriv = secret
crato = secret, creator
+community = secret, oss
[roles]
admin = *
creator = repository:create
heartOfGold = "repository:read,modify,delete:hof"
puzzle42 = "repository:read,write:p42"
+oss = "repository:pull"