mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
merge
This commit is contained in:
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ package sonia.scm.api.v2.resources;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ import java.util.Arrays;
|
|||||||
* builder for each method.
|
* builder for each method.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* LinkBuilder builder = new LinkBuilder(uriInfo, MainResource.class, SubResource.class);
|
* LinkBuilder builder = new LinkBuilder(pathInfo, MainResource.class, SubResource.class);
|
||||||
* Link link = builder
|
* Link link = builder
|
||||||
* .method("sub")
|
* .method("sub")
|
||||||
* .parameters("param")
|
* .parameters("param")
|
||||||
@@ -25,16 +24,16 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess") // Non-public will result in IllegalAccessError for plugins
|
@SuppressWarnings("WeakerAccess") // Non-public will result in IllegalAccessError for plugins
|
||||||
public class LinkBuilder {
|
public class LinkBuilder {
|
||||||
private final UriInfo uriInfo;
|
private final ScmPathInfo pathInfo;
|
||||||
private final Class[] classes;
|
private final Class[] classes;
|
||||||
private final ImmutableList<Call> calls;
|
private final ImmutableList<Call> calls;
|
||||||
|
|
||||||
public LinkBuilder(UriInfo uriInfo, Class... classes) {
|
public LinkBuilder(ScmPathInfo pathInfo, Class... classes) {
|
||||||
this(uriInfo, classes, ImmutableList.of());
|
this(pathInfo, classes, ImmutableList.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkBuilder(UriInfo uriInfo, Class[] classes, ImmutableList<Call> calls) {
|
private LinkBuilder(ScmPathInfo pathInfo, Class[] classes, ImmutableList<Call> calls) {
|
||||||
this.uriInfo = uriInfo;
|
this.pathInfo = pathInfo;
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
this.calls = calls;
|
this.calls = calls;
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,7 @@ public class LinkBuilder {
|
|||||||
throw new IllegalStateException("not enough methods for all classes");
|
throw new IllegalStateException("not enough methods for all classes");
|
||||||
}
|
}
|
||||||
|
|
||||||
URI baseUri = uriInfo.getBaseUri();
|
URI baseUri = pathInfo.getApiRestUri();
|
||||||
URI relativeUri = createRelativeUri();
|
URI relativeUri = createRelativeUri();
|
||||||
return baseUri.resolve(relativeUri);
|
return baseUri.resolve(relativeUri);
|
||||||
}
|
}
|
||||||
@@ -61,7 +60,7 @@ public class LinkBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private LinkBuilder add(String method, String[] parameters) {
|
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<Call> appendNewCall(String method, String[] parameters) {
|
private ImmutableList<Call> appendNewCall(String method, String[] parameters) {
|
||||||
|
|||||||
@@ -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("../..");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
package sonia.scm.filter;
|
package sonia.scm.filter;
|
||||||
|
|
||||||
|
import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Useful constants for filter implementations.
|
* Useful constants for filter implementations.
|
||||||
*
|
*
|
||||||
@@ -44,26 +46,26 @@ public final class Filters
|
|||||||
public static final String PATTERN_ALL = "/*";
|
public static final String PATTERN_ALL = "/*";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_CONFIG = "/api/rest/config*";
|
public static final String PATTERN_CONFIG = REST_API_PATH + "/config*";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_DEBUG = "/debug.html";
|
public static final String PATTERN_DEBUG = "/debug.html";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_GROUPS = "/api/rest/groups*";
|
public static final String PATTERN_GROUPS = REST_API_PATH + "/groups*";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_PLUGINS = "/api/rest/plugins*";
|
public static final String PATTERN_PLUGINS = REST_API_PATH + "/plugins*";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_RESOURCE_REGEX =
|
public static final String PATTERN_RESOURCE_REGEX =
|
||||||
"^/(?:resources|api|plugins|index)[\\./].*(?:html|\\.css|\\.js|\\.xml|\\.json|\\.txt)";
|
"^/(?:resources|api|plugins|index)[\\./].*(?:html|\\.css|\\.js|\\.xml|\\.json|\\.txt)";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_RESTAPI = "/api/rest/*";
|
public static final String PATTERN_RESTAPI = REST_API_PATH + "/*";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_USERS = "/api/rest/users*";
|
public static final String PATTERN_USERS = REST_API_PATH + "/users*";
|
||||||
|
|
||||||
/** authentication priority */
|
/** authentication priority */
|
||||||
public static final int PRIORITY_AUTHENTICATION = 5000;
|
public static final int PRIORITY_AUTHENTICATION = 5000;
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import com.google.common.base.Objects;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import sonia.scm.BasicPropertiesAware;
|
import sonia.scm.BasicPropertiesAware;
|
||||||
import sonia.scm.ModelObject;
|
import sonia.scm.ModelObject;
|
||||||
import sonia.scm.util.HttpUtil;
|
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
import sonia.scm.util.ValidationUtil;
|
import sonia.scm.util.ValidationUtil;
|
||||||
|
|
||||||
@@ -349,17 +348,6 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
|
|||||||
// do not copy health check results
|
// 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.
|
* Returns true if the {@link Repository} is the same as the obj argument.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ package sonia.scm.repository;
|
|||||||
import sonia.scm.AlreadyExistsException;
|
import sonia.scm.AlreadyExistsException;
|
||||||
import sonia.scm.TypeManager;
|
import sonia.scm.TypeManager;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@@ -99,29 +98,6 @@ public interface RepositoryManager
|
|||||||
*/
|
*/
|
||||||
public Collection<RepositoryType> getConfiguredTypes();
|
public Collection<RepositoryType> 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 ...).
|
* Returns a {@link RepositoryHandler} by the given type (hg, git, svn ...).
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import sonia.scm.AlreadyExistsException;
|
|||||||
import sonia.scm.ManagerDecorator;
|
import sonia.scm.ManagerDecorator;
|
||||||
import sonia.scm.Type;
|
import sonia.scm.Type;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@@ -120,34 +119,6 @@ public class RepositoryManagerDecorator
|
|||||||
return decorated;
|
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}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ import sonia.scm.NotFoundException;
|
|||||||
public class RepositoryNotFoundException extends NotFoundException
|
public class RepositoryNotFoundException extends NotFoundException
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private static final long serialVersionUID = -6583078808900520166L;
|
private static final long serialVersionUID = -6583078808900520166L;
|
||||||
|
private static final String TYPE_REPOSITORY = "repository";
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
@@ -55,10 +55,14 @@ public class RepositoryNotFoundException extends NotFoundException
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public RepositoryNotFoundException(Repository repository) {
|
public RepositoryNotFoundException(Repository repository) {
|
||||||
super("repository", repository.getName() + "/" + repository.getNamespace());
|
super(TYPE_REPOSITORY, repository.getName() + "/" + repository.getNamespace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public RepositoryNotFoundException(String repositoryId) {
|
public RepositoryNotFoundException(String repositoryId) {
|
||||||
super("repository", repositoryId);
|
super(TYPE_REPOSITORY, repositoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepositoryNotFoundException(NamespaceAndName namespaceAndName) {
|
||||||
|
super(TYPE_REPOSITORY, namespaceAndName.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
* 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,
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from this
|
* contributors may be used to endorse or promote products derived from this
|
||||||
* software without specific prior written permission.
|
* software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* 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.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* http://bitbucket.org/sdorra/scm-manager
|
* http://bitbucket.org/sdorra/scm-manager
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.inject.throwingproviders.CheckedProvider;
|
import com.google.inject.throwingproviders.CheckedProvider;
|
||||||
|
|
||||||
import sonia.scm.security.ScmSecurityException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
* @since 1.10
|
* @since 1.10
|
||||||
*/
|
*/
|
||||||
public interface RepositoryProvider extends CheckedProvider<Repository>
|
public interface RepositoryProvider extends CheckedProvider<Repository> {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*
|
|
||||||
* @throws ScmSecurityException
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Repository get() throws ScmSecurityException;
|
Repository get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.api;
|
package sonia.scm.repository.api;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.cache.CacheManager;
|
import sonia.scm.cache.CacheManager;
|
||||||
@@ -42,6 +43,8 @@ import sonia.scm.repository.spi.RepositoryServiceProvider;
|
|||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
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
|
* 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
|
* @apiviz.uses sonia.scm.repository.api.UnbundleCommandBuilder
|
||||||
* @since 1.17
|
* @since 1.17
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public final class RepositoryService implements Closeable {
|
public final class RepositoryService implements Closeable {
|
||||||
private CacheManager cacheManager;
|
|
||||||
private PreProcessorUtil preProcessorUtil;
|
private static final Logger logger = LoggerFactory.getLogger(RepositoryService.class);
|
||||||
private RepositoryServiceProvider provider;
|
|
||||||
private Repository repository;
|
private final CacheManager cacheManager;
|
||||||
private static final Logger logger =
|
private final PreProcessorUtil preProcessorUtil;
|
||||||
LoggerFactory.getLogger(RepositoryService.class);
|
private final RepositoryServiceProvider provider;
|
||||||
|
private final Repository repository;
|
||||||
|
private final Set<ScmProtocolProvider> protocolProviders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link RepositoryService}. This constructor should only
|
* Constructs a new {@link RepositoryService}. This constructor should only
|
||||||
* be called from the {@link RepositoryServiceFactory}.
|
* be called from the {@link RepositoryServiceFactory}.
|
||||||
*
|
* @param cacheManager cache manager
|
||||||
* @param cacheManager cache manager
|
|
||||||
* @param provider implementation for {@link RepositoryServiceProvider}
|
* @param provider implementation for {@link RepositoryServiceProvider}
|
||||||
* @param repository the repository
|
* @param repository the repository
|
||||||
* @param preProcessorUtil
|
|
||||||
*/
|
*/
|
||||||
RepositoryService(CacheManager cacheManager,
|
RepositoryService(CacheManager cacheManager,
|
||||||
RepositoryServiceProvider provider, Repository repository,
|
RepositoryServiceProvider provider, Repository repository,
|
||||||
PreProcessorUtil preProcessorUtil) {
|
PreProcessorUtil preProcessorUtil, Set<ScmProtocolProvider> protocolProviders) {
|
||||||
this.cacheManager = cacheManager;
|
this.cacheManager = cacheManager;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.preProcessorUtil = preProcessorUtil;
|
this.preProcessorUtil = preProcessorUtil;
|
||||||
|
this.protocolProviders = protocolProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,7 +130,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
try {
|
try {
|
||||||
provider.close();
|
provider.close();
|
||||||
} catch (IOException ex) {
|
} 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() {
|
public BlameCommandBuilder getBlameCommand() {
|
||||||
logger.debug("create blame command for repository {}",
|
logger.debug("create blame command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new BlameCommandBuilder(cacheManager, provider.getBlameCommand(),
|
return new BlameCommandBuilder(cacheManager, provider.getBlameCommand(),
|
||||||
repository, preProcessorUtil);
|
repository, preProcessorUtil);
|
||||||
@@ -153,7 +158,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public BranchesCommandBuilder getBranchesCommand() {
|
public BranchesCommandBuilder getBranchesCommand() {
|
||||||
logger.debug("create branches command for repository {}",
|
logger.debug("create branches command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new BranchesCommandBuilder(cacheManager,
|
return new BranchesCommandBuilder(cacheManager,
|
||||||
provider.getBranchesCommand(), repository);
|
provider.getBranchesCommand(), repository);
|
||||||
@@ -168,7 +173,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public BrowseCommandBuilder getBrowseCommand() {
|
public BrowseCommandBuilder getBrowseCommand() {
|
||||||
logger.debug("create browse command for repository {}",
|
logger.debug("create browse command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new BrowseCommandBuilder(cacheManager, provider.getBrowseCommand(),
|
return new BrowseCommandBuilder(cacheManager, provider.getBrowseCommand(),
|
||||||
repository, preProcessorUtil);
|
repository, preProcessorUtil);
|
||||||
@@ -184,7 +189,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public BundleCommandBuilder getBundleCommand() {
|
public BundleCommandBuilder getBundleCommand() {
|
||||||
logger.debug("create bundle command for repository {}",
|
logger.debug("create bundle command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new BundleCommandBuilder(provider.getBundleCommand(), repository);
|
return new BundleCommandBuilder(provider.getBundleCommand(), repository);
|
||||||
}
|
}
|
||||||
@@ -198,7 +203,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public CatCommandBuilder getCatCommand() {
|
public CatCommandBuilder getCatCommand() {
|
||||||
logger.debug("create cat command for repository {}",
|
logger.debug("create cat command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new CatCommandBuilder(provider.getCatCommand());
|
return new CatCommandBuilder(provider.getCatCommand());
|
||||||
}
|
}
|
||||||
@@ -213,7 +218,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public DiffCommandBuilder getDiffCommand() {
|
public DiffCommandBuilder getDiffCommand() {
|
||||||
logger.debug("create diff command for repository {}",
|
logger.debug("create diff command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new DiffCommandBuilder(provider.getDiffCommand());
|
return new DiffCommandBuilder(provider.getDiffCommand());
|
||||||
}
|
}
|
||||||
@@ -229,7 +234,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public IncomingCommandBuilder getIncomingCommand() {
|
public IncomingCommandBuilder getIncomingCommand() {
|
||||||
logger.debug("create incoming command for repository {}",
|
logger.debug("create incoming command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new IncomingCommandBuilder(cacheManager,
|
return new IncomingCommandBuilder(cacheManager,
|
||||||
provider.getIncomingCommand(), repository, preProcessorUtil);
|
provider.getIncomingCommand(), repository, preProcessorUtil);
|
||||||
@@ -244,7 +249,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public LogCommandBuilder getLogCommand() {
|
public LogCommandBuilder getLogCommand() {
|
||||||
logger.debug("create log command for repository {}",
|
logger.debug("create log command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new LogCommandBuilder(cacheManager, provider.getLogCommand(),
|
return new LogCommandBuilder(cacheManager, provider.getLogCommand(),
|
||||||
repository, preProcessorUtil);
|
repository, preProcessorUtil);
|
||||||
@@ -258,7 +263,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
* by the implementation of the repository service provider.
|
* by the implementation of the repository service provider.
|
||||||
*/
|
*/
|
||||||
public ModificationsCommandBuilder getModificationsCommand() {
|
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);
|
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() {
|
public OutgoingCommandBuilder getOutgoingCommand() {
|
||||||
logger.debug("create outgoing command for repository {}",
|
logger.debug("create outgoing command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new OutgoingCommandBuilder(cacheManager,
|
return new OutgoingCommandBuilder(cacheManager,
|
||||||
provider.getOutgoingCommand(), repository, preProcessorUtil);
|
provider.getOutgoingCommand(), repository, preProcessorUtil);
|
||||||
@@ -288,7 +293,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public PullCommandBuilder getPullCommand() {
|
public PullCommandBuilder getPullCommand() {
|
||||||
logger.debug("create pull command for repository {}",
|
logger.debug("create pull command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new PullCommandBuilder(provider.getPullCommand(), repository);
|
return new PullCommandBuilder(provider.getPullCommand(), repository);
|
||||||
}
|
}
|
||||||
@@ -303,7 +308,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public PushCommandBuilder getPushCommand() {
|
public PushCommandBuilder getPushCommand() {
|
||||||
logger.debug("create push command for repository {}",
|
logger.debug("create push command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new PushCommandBuilder(provider.getPushCommand());
|
return new PushCommandBuilder(provider.getPushCommand());
|
||||||
}
|
}
|
||||||
@@ -326,7 +331,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public TagsCommandBuilder getTagsCommand() {
|
public TagsCommandBuilder getTagsCommand() {
|
||||||
logger.debug("create tags command for repository {}",
|
logger.debug("create tags command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new TagsCommandBuilder(cacheManager, provider.getTagsCommand(),
|
return new TagsCommandBuilder(cacheManager, provider.getTagsCommand(),
|
||||||
repository);
|
repository);
|
||||||
@@ -342,7 +347,7 @@ public final class RepositoryService implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public UnbundleCommandBuilder getUnbundleCommand() {
|
public UnbundleCommandBuilder getUnbundleCommand() {
|
||||||
logger.debug("create unbundle command for repository {}",
|
logger.debug("create unbundle command for repository {}",
|
||||||
repository.getName());
|
repository.getNamespaceAndName());
|
||||||
|
|
||||||
return new UnbundleCommandBuilder(provider.getUnbundleCommand(),
|
return new UnbundleCommandBuilder(provider.getUnbundleCommand(),
|
||||||
repository);
|
repository);
|
||||||
@@ -369,5 +374,20 @@ public final class RepositoryService implements Closeable {
|
|||||||
return provider.getSupportedFeatures().contains(feature);
|
return provider.getSupportedFeatures().contains(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T extends ScmProtocol> Stream<T> getSupportedProtocols() {
|
||||||
|
return protocolProviders.stream()
|
||||||
|
.filter(protocolProvider -> protocolProvider.getType().equals(getRepository().getType()))
|
||||||
|
.map(this::<T>createProviderInstanceForRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends ScmProtocol> T createProviderInstanceForRepository(ScmProtocolProvider<T> protocolProvider) {
|
||||||
|
return protocolProvider.get(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends ScmProtocol> T getProtocol(Class<T> clazz) {
|
||||||
|
return this.<T>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())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,13 +137,15 @@ public final class RepositoryServiceFactory
|
|||||||
@Inject
|
@Inject
|
||||||
public RepositoryServiceFactory(ScmConfiguration configuration,
|
public RepositoryServiceFactory(ScmConfiguration configuration,
|
||||||
CacheManager cacheManager, RepositoryManager repositoryManager,
|
CacheManager cacheManager, RepositoryManager repositoryManager,
|
||||||
Set<RepositoryServiceResolver> resolvers, PreProcessorUtil preProcessorUtil)
|
Set<RepositoryServiceResolver> resolvers, PreProcessorUtil preProcessorUtil,
|
||||||
|
Set<ScmProtocolProvider> protocolProviders)
|
||||||
{
|
{
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.cacheManager = cacheManager;
|
this.cacheManager = cacheManager;
|
||||||
this.repositoryManager = repositoryManager;
|
this.repositoryManager = repositoryManager;
|
||||||
this.resolvers = resolvers;
|
this.resolvers = resolvers;
|
||||||
this.preProcessorUtil = preProcessorUtil;
|
this.preProcessorUtil = preProcessorUtil;
|
||||||
|
this.protocolProviders = protocolProviders;
|
||||||
|
|
||||||
ScmEventBus.getInstance().register(new CacheClearHook(cacheManager));
|
ScmEventBus.getInstance().register(new CacheClearHook(cacheManager));
|
||||||
}
|
}
|
||||||
@@ -208,9 +210,7 @@ public final class RepositoryServiceFactory
|
|||||||
|
|
||||||
if (repository == null)
|
if (repository == null)
|
||||||
{
|
{
|
||||||
String msg = "could not find a repository with namespace/name " + namespaceAndName;
|
throw new RepositoryNotFoundException(namespaceAndName);
|
||||||
|
|
||||||
throw new RepositoryNotFoundException(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return create(repository);
|
return create(repository);
|
||||||
@@ -254,7 +254,7 @@ public final class RepositoryServiceFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
service = new RepositoryService(cacheManager, provider, repository,
|
service = new RepositoryService(cacheManager, provider, repository,
|
||||||
preProcessorUtil);
|
preProcessorUtil, protocolProviders);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -369,4 +369,6 @@ public final class RepositoryServiceFactory
|
|||||||
|
|
||||||
/** service resolvers */
|
/** service resolvers */
|
||||||
private final Set<RepositoryServiceResolver> resolvers;
|
private final Set<RepositoryServiceResolver> resolvers;
|
||||||
|
|
||||||
|
private Set<ScmProtocolProvider> protocolProviders;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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<T extends ScmProtocol> {
|
||||||
|
|
||||||
|
String getType();
|
||||||
|
|
||||||
|
T get(Repository repository);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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<HttpScmProtocol> {
|
||||||
|
|
||||||
|
private final Provider<? extends ScmProviderHttpServlet> delegateProvider;
|
||||||
|
private final Provider<ScmPathInfoStore> pathInfoStore;
|
||||||
|
private final ScmConfiguration scmConfiguration;
|
||||||
|
|
||||||
|
private volatile boolean isInitialized = false;
|
||||||
|
|
||||||
|
|
||||||
|
protected InitializingHttpScmProtocolWrapper(Provider<? extends ScmProviderHttpServlet> delegateProvider, Provider<ScmPathInfoStore> 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<String> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import sonia.scm.repository.Feature;
|
import sonia.scm.repository.Feature;
|
||||||
import sonia.scm.repository.api.Command;
|
import sonia.scm.repository.api.Command;
|
||||||
import sonia.scm.repository.api.CommandNotSupportedException;
|
import sonia.scm.repository.api.CommandNotSupportedException;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
|
import sonia.scm.DecoratorFactory;
|
||||||
|
import sonia.scm.plugin.ExtensionPoint;
|
||||||
|
|
||||||
|
@ExtensionPoint
|
||||||
|
public interface ScmProviderHttpServletDecoratorFactory extends DecoratorFactory<ScmProviderHttpServlet> {
|
||||||
|
/**
|
||||||
|
* Has to return <code>true</code> 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 <code>true</code> when the provided decorator should be used for the given scm type.
|
||||||
|
*/
|
||||||
|
boolean handlesScmType(String type);
|
||||||
|
}
|
||||||
@@ -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<ScmProviderHttpServlet> {
|
||||||
|
|
||||||
|
@Inject(optional = true)
|
||||||
|
private Set<ScmProviderHttpServletDecoratorFactory> decoratorFactories;
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
protected ScmProviderHttpServletProvider(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScmProviderHttpServlet get() {
|
||||||
|
return Decorators.decorate(getRootServlet(), getDecoratorsForType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ScmProviderHttpServletDecoratorFactory> getDecoratorsForType() {
|
||||||
|
return decoratorFactories.stream().filter(d -> d.handlesScmType(type)).collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract ScmProviderHttpServlet getRootServlet();
|
||||||
|
}
|
||||||
@@ -37,7 +37,6 @@ package sonia.scm.util;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sonia.scm.DecoratorFactory;
|
import sonia.scm.DecoratorFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,39 +33,32 @@
|
|||||||
|
|
||||||
package sonia.scm.web.filter;
|
package sonia.scm.web.filter;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import org.apache.shiro.SecurityUtils;
|
import org.apache.shiro.SecurityUtils;
|
||||||
import org.apache.shiro.authz.AuthorizationException;
|
import org.apache.shiro.authz.AuthorizationException;
|
||||||
import org.apache.shiro.subject.Subject;
|
import org.apache.shiro.subject.Subject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.ArgumentIsInvalidException;
|
|
||||||
import sonia.scm.SCMContext;
|
import sonia.scm.SCMContext;
|
||||||
import sonia.scm.config.ScmConfiguration;
|
import sonia.scm.config.ScmConfiguration;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryPermissions;
|
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.Role;
|
||||||
import sonia.scm.security.ScmSecurityException;
|
import sonia.scm.security.ScmSecurityException;
|
||||||
import sonia.scm.util.HttpUtil;
|
import sonia.scm.util.HttpUtil;
|
||||||
import sonia.scm.util.Util;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract http filter to check repository permissions.
|
* Abstract http filter to check repository permissions.
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
public abstract class PermissionFilter extends HttpFilter
|
public abstract class PermissionFilter extends ScmProviderHttpServletDecorator
|
||||||
{
|
{
|
||||||
|
|
||||||
/** the logger for PermissionFilter */
|
/** the logger for PermissionFilter */
|
||||||
@@ -81,23 +74,14 @@ public abstract class PermissionFilter extends HttpFilter
|
|||||||
*
|
*
|
||||||
* @since 1.21
|
* @since 1.21
|
||||||
*/
|
*/
|
||||||
public PermissionFilter(ScmConfiguration configuration)
|
protected PermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate)
|
||||||
{
|
{
|
||||||
|
super(delegate);
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- 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.
|
* 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 request http request
|
||||||
* @param response http response
|
* @param response http response
|
||||||
* @param chain filter chain
|
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws ServletException
|
* @throws ServletException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void doFilter(HttpServletRequest request,
|
public void service(HttpServletRequest request,
|
||||||
HttpServletResponse response, FilterChain chain)
|
HttpServletResponse response, Repository repository)
|
||||||
throws IOException, ServletException
|
throws IOException, ServletException
|
||||||
{
|
{
|
||||||
Subject subject = SecurityUtils.getSubject();
|
Subject subject = SecurityUtils.getSubject();
|
||||||
|
|
||||||
try
|
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))
|
super.service(request, response, repository);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
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)
|
catch (ScmSecurityException | AuthorizationException ex)
|
||||||
{
|
{
|
||||||
logger.warn("user " + subject.getPrincipal() + " has not enough permissions", 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());
|
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<String> 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.
|
* Send access denied to the servlet response.
|
||||||
*
|
*
|
||||||
@@ -280,25 +213,6 @@ public abstract class PermissionFilter extends HttpFilter
|
|||||||
: "read";
|
: "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.
|
* Returns the username from the given subject or anonymous.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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/"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<ScmProtocol> 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 {}
|
||||||
|
}
|
||||||
@@ -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<DynamicTest> shouldCreateCorrectUrlsWithContextPath() {
|
||||||
|
return Stream.of("http://localhost/scm", "http://localhost/scm/")
|
||||||
|
.map(url -> assertResultingUrl(url, "http://localhost/scm/repo/space/name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
Stream<DynamicTest> 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) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ScmPathInfoStore> 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<ScmPathInfo> mockSetPathInfo() {
|
||||||
|
return when(pathInfoStore.get()).thenReturn(() -> URI.create("http://example.com/scm/api/rest/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
[users]
|
[users]
|
||||||
trillian = secret, user
|
trillian = secret, user
|
||||||
|
admin = secret, admin
|
||||||
|
writer = secret, repo_write
|
||||||
|
reader = secret, repo_read
|
||||||
|
unpriv = secret
|
||||||
|
|
||||||
[roles]
|
[roles]
|
||||||
admin = *
|
admin = *
|
||||||
user = something:*
|
user = something:*
|
||||||
|
repo_read = "repository:read:1"
|
||||||
|
repo_write = "repository:push:1"
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public class RepositoryUtil {
|
|||||||
public static RepositoryClient createRepositoryClient(String repositoryType, File folder, String username, String password) throws IOException {
|
public static RepositoryClient createRepositoryClient(String repositoryType, File folder, String username, String password) throws IOException {
|
||||||
String httpProtocolUrl = TestData.callRepository(username, password, repositoryType, HttpStatus.SC_OK)
|
String httpProtocolUrl = TestData.callRepository(username, password, repositoryType, HttpStatus.SC_OK)
|
||||||
.extract()
|
.extract()
|
||||||
.path("_links.httpProtocol.href");
|
.path("_links.protocol.find{it.name=='http'}.href");
|
||||||
|
|
||||||
return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, username, password, folder);
|
return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, username, password, folder);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import static de.otto.edison.hal.Links.linkingTo;
|
|||||||
public abstract class GitConfigToGitConfigDtoMapper extends BaseMapper<GitConfig, GitConfigDto> {
|
public abstract class GitConfigToGitConfigDtoMapper extends BaseMapper<GitConfig, GitConfigDto> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
void appendLinks(GitConfig config, @MappingTarget GitConfigDto target) {
|
void appendLinks(GitConfig config, @MappingTarget GitConfigDto target) {
|
||||||
@@ -30,12 +30,12 @@ public abstract class GitConfigToGitConfigDtoMapper extends BaseMapper<GitConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String self() {
|
private String self() {
|
||||||
LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), GitConfigResource.class);
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), GitConfigResource.class);
|
||||||
return linkBuilder.method("get").parameters().href();
|
return linkBuilder.method("get").parameters().href();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String update() {
|
private String update() {
|
||||||
LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), GitConfigResource.class);
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), GitConfigResource.class);
|
||||||
return linkBuilder.method("update").parameters().href();
|
return linkBuilder.method("update").parameters().href();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
@@ -71,19 +69,10 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository) {
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param handler
|
|
||||||
* @param repository
|
|
||||||
*/
|
|
||||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler,
|
|
||||||
Repository repository)
|
|
||||||
{
|
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
context = new GitContext(handler.getDirectory(repository));
|
this.context = new GitContext(handler.getDirectory(repository));
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ package sonia.scm.repository.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
@@ -45,51 +44,23 @@ import sonia.scm.repository.Repository;
|
|||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Extension
|
@Extension
|
||||||
public class GitRepositoryServiceResolver implements RepositoryServiceResolver
|
public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||||
{
|
|
||||||
|
|
||||||
/** Field description */
|
private final GitRepositoryHandler handler;
|
||||||
public static final String TYPE = "git";
|
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param handler
|
|
||||||
*/
|
|
||||||
@Inject
|
@Inject
|
||||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler)
|
public GitRepositoryServiceResolver(GitRepositoryHandler handler) {
|
||||||
{
|
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public GitRepositoryServiceProvider resolve(Repository repository)
|
public GitRepositoryServiceProvider resolve(Repository repository) {
|
||||||
{
|
|
||||||
GitRepositoryServiceProvider provider = null;
|
GitRepositoryServiceProvider provider = null;
|
||||||
|
|
||||||
if (TYPE.equalsIgnoreCase(repository.getType()))
|
if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
||||||
{
|
|
||||||
provider = new GitRepositoryServiceProvider(handler, repository);
|
provider = new GitRepositoryServiceProvider(handler, repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private GitRepositoryHandler handler;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +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;
|
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
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.web.filter.AuthenticationFilter;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles git specific basic authentication.
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
@Priority(Filters.PRIORITY_AUTHENTICATION)
|
|
||||||
@WebElement(value = GitServletModule.PATTERN_GIT)
|
|
||||||
public class GitBasicAuthenticationFilter extends AuthenticationFilter
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new instance.
|
|
||||||
*
|
|
||||||
* @param configuration scm-manager main configuration
|
|
||||||
* @param webTokenGenerators web token generators
|
|
||||||
*/
|
|
||||||
@Inject
|
|
||||||
public GitBasicAuthenticationFilter(ScmConfiguration configuration,
|
|
||||||
Set<WebTokenGenerator> webTokenGenerators)
|
|
||||||
{
|
|
||||||
super(configuration, webTokenGenerators);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,38 +33,24 @@
|
|||||||
|
|
||||||
package sonia.scm.web;
|
package sonia.scm.web;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
|
|
||||||
import org.eclipse.jgit.http.server.GitSmartHttpTools;
|
import org.eclipse.jgit.http.server.GitSmartHttpTools;
|
||||||
|
|
||||||
import sonia.scm.ClientMessages;
|
import sonia.scm.ClientMessages;
|
||||||
import sonia.scm.config.ScmConfiguration;
|
import sonia.scm.config.ScmConfiguration;
|
||||||
import sonia.scm.repository.GitUtil;
|
import sonia.scm.repository.GitUtil;
|
||||||
import sonia.scm.repository.RepositoryProvider;
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
import sonia.scm.web.filter.ProviderPermissionFilter;
|
import sonia.scm.web.filter.PermissionFilter;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import sonia.scm.Priority;
|
import java.io.IOException;
|
||||||
import sonia.scm.filter.Filters;
|
|
||||||
import sonia.scm.filter.WebElement;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitPermissionFilter decides if a git request requires write or read privileges.
|
* GitPermissionFilter decides if a git request requires write or read privileges.
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Priority(Filters.PRIORITY_AUTHORIZATION)
|
public class GitPermissionFilter extends PermissionFilter
|
||||||
@WebElement(value = GitServletModule.PATTERN_GIT)
|
|
||||||
public class GitPermissionFilter extends ProviderPermissionFilter
|
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final String PARAMETER_SERVICE = "service";
|
private static final String PARAMETER_SERVICE = "service";
|
||||||
@@ -83,11 +69,9 @@ public class GitPermissionFilter extends ProviderPermissionFilter
|
|||||||
* Constructs a new instance of the GitPermissionFilter.
|
* Constructs a new instance of the GitPermissionFilter.
|
||||||
*
|
*
|
||||||
* @param configuration scm main configuration
|
* @param configuration scm main configuration
|
||||||
* @param repositoryProvider repository provider
|
|
||||||
*/
|
*/
|
||||||
@Inject
|
public GitPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate) {
|
||||||
public GitPermissionFilter(ScmConfiguration configuration, RepositoryProvider repositoryProvider) {
|
super(configuration, delegate);
|
||||||
super(configuration, repositoryProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -103,7 +87,7 @@ public class GitPermissionFilter extends ProviderPermissionFilter
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isWriteRequest(HttpServletRequest request) {
|
public boolean isWriteRequest(HttpServletRequest request) {
|
||||||
return isReceivePackRequest(request) ||
|
return isReceivePackRequest(request) ||
|
||||||
isReceiveServiceRequest(request) ||
|
isReceiveServiceRequest(request) ||
|
||||||
isLfsFileUpload(request);
|
isLfsFileUpload(request);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,8 +125,9 @@ public class GitRepositoryResolver implements RepositoryResolver<HttpServletRequ
|
|||||||
throw new ServiceNotEnabledException();
|
throw new ServiceNotEnabledException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (RuntimeException | IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
// REVIEW
|
||||||
throw new RepositoryNotFoundException(repositoryName, e);
|
throw new RepositoryNotFoundException(repositoryName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.GitRepositoryHandler;
|
||||||
|
import sonia.scm.repository.spi.InitializingHttpScmProtocolWrapper;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Extension
|
||||||
|
public class GitScmProtocolProviderWrapper extends InitializingHttpScmProtocolWrapper {
|
||||||
|
@Inject
|
||||||
|
public GitScmProtocolProviderWrapper(ScmGitServletProvider servletProvider, Provider<ScmPathInfoStore> uriInfoStore, ScmConfiguration scmConfiguration) {
|
||||||
|
super(servletProvider, uriInfoStore, scmConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return GitRepositoryHandler.TYPE_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,18 +51,6 @@ import sonia.scm.web.lfs.LfsBlobStoreFactory;
|
|||||||
public class GitServletModule extends ServletModule
|
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
|
@Override
|
||||||
protected void configureServlets()
|
protected void configureServlets()
|
||||||
{
|
{
|
||||||
@@ -75,8 +63,5 @@ public class GitServletModule extends ServletModule
|
|||||||
|
|
||||||
bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass());
|
bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass());
|
||||||
bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass());
|
bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass());
|
||||||
|
|
||||||
// serlvelts and filters
|
|
||||||
serve(PATTERN_GIT).with(ScmGitServlet.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
package sonia.scm.web;
|
package sonia.scm.web;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
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.eclipse.jgit.lfs.lib.Constants;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryProvider;
|
|
||||||
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
||||||
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
import sonia.scm.util.HttpUtil;
|
import sonia.scm.util.HttpUtil;
|
||||||
import sonia.scm.web.lfs.servlet.LfsServletFactory;
|
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.eclipse.jgit.lfs.lib.Constants.CONTENT_TYPE_GIT_LFS_JSON;
|
||||||
import static org.slf4j.LoggerFactory.getLogger;
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@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(
|
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 */
|
/** Field description */
|
||||||
@@ -88,7 +85,6 @@ public class ScmGitServlet extends GitServlet
|
|||||||
* @param repositoryResolver
|
* @param repositoryResolver
|
||||||
* @param receivePackFactory
|
* @param receivePackFactory
|
||||||
* @param repositoryViewer
|
* @param repositoryViewer
|
||||||
* @param repositoryProvider
|
|
||||||
* @param repositoryRequestListenerUtil
|
* @param repositoryRequestListenerUtil
|
||||||
* @param lfsServletFactory
|
* @param lfsServletFactory
|
||||||
*/
|
*/
|
||||||
@@ -96,11 +92,9 @@ public class ScmGitServlet extends GitServlet
|
|||||||
public ScmGitServlet(GitRepositoryResolver repositoryResolver,
|
public ScmGitServlet(GitRepositoryResolver repositoryResolver,
|
||||||
GitReceivePackFactory receivePackFactory,
|
GitReceivePackFactory receivePackFactory,
|
||||||
GitRepositoryViewer repositoryViewer,
|
GitRepositoryViewer repositoryViewer,
|
||||||
RepositoryProvider repositoryProvider,
|
|
||||||
RepositoryRequestListenerUtil repositoryRequestListenerUtil,
|
RepositoryRequestListenerUtil repositoryRequestListenerUtil,
|
||||||
LfsServletFactory lfsServletFactory)
|
LfsServletFactory lfsServletFactory)
|
||||||
{
|
{
|
||||||
this.repositoryProvider = repositoryProvider;
|
|
||||||
this.repositoryViewer = repositoryViewer;
|
this.repositoryViewer = repositoryViewer;
|
||||||
this.repositoryRequestListenerUtil = repositoryRequestListenerUtil;
|
this.repositoryRequestListenerUtil = repositoryRequestListenerUtil;
|
||||||
this.lfsServletFactory = lfsServletFactory;
|
this.lfsServletFactory = lfsServletFactory;
|
||||||
@@ -122,44 +116,9 @@ public class ScmGitServlet extends GitServlet
|
|||||||
* @throws ServletException
|
* @throws ServletException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void service(HttpServletRequest request,
|
public void service(HttpServletRequest request, HttpServletResponse response, Repository repository)
|
||||||
HttpServletResponse response)
|
|
||||||
throws ServletException, IOException
|
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.
|
|
||||||
* <ul>
|
|
||||||
* <li>Batch API:</li>
|
|
||||||
* <ul>
|
|
||||||
* <li>used to provide the client with information on how handle the large files of a repository.</li>
|
|
||||||
* <li>response contains the information where to perform the actual upload and download of the large objects.</li>
|
|
||||||
* </ul>
|
|
||||||
* <li>Transfer API:</li>
|
|
||||||
* <ul>
|
|
||||||
* <li>receives and provides the actual large objects (resolves the pointer placed in the file of the working copy).</li>
|
|
||||||
* <li>invoked only after the Batch API has been questioned about what to do with the large files</li>
|
|
||||||
* </ul>
|
|
||||||
* <li>Regular Git Http API:</li>
|
|
||||||
* <ul>
|
|
||||||
* <li>regular git http wire protocol, use by normal git clients.</li>
|
|
||||||
* </ul>
|
|
||||||
* <li>Browser Overview:<li>
|
|
||||||
* <ul>
|
|
||||||
* <li>short repository overview for browser clients.</li>
|
|
||||||
* </ul>
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
private void handleRequest(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException {
|
|
||||||
String repoPath = repository.getNamespace() + "/" + repository.getName();
|
String repoPath = repository.getNamespace() + "/" + repository.getName();
|
||||||
logger.trace("handle git repository at {}", repoPath);
|
logger.trace("handle git repository at {}", repoPath);
|
||||||
if (isLfsBatchApiRequest(request, repoPath)) {
|
if (isLfsBatchApiRequest(request, repoPath)) {
|
||||||
@@ -210,7 +169,7 @@ public class ScmGitServlet extends GitServlet
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws ServletException
|
* @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 {
|
try {
|
||||||
repositoryViewer.handleRequest(request, response, repository);
|
repositoryViewer.handleRequest(request, response, repository);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@@ -229,7 +188,7 @@ public class ScmGitServlet extends GitServlet
|
|||||||
*/
|
*/
|
||||||
private static boolean isLfsFileTransferRequest(HttpServletRequest request, String repository) {
|
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 pathMatches = request.getRequestURI().matches(regex);
|
||||||
|
|
||||||
boolean methodMatches = request.getMethod().equals("PUT") || request.getMethod().equals("GET");
|
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) {
|
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 pathMatches = request.getRequestURI().matches(regex);
|
||||||
|
|
||||||
boolean methodMatches = "POST".equals(request.getMethod());
|
boolean methodMatches = "POST".equals(request.getMethod());
|
||||||
@@ -284,12 +243,8 @@ public class ScmGitServlet extends GitServlet
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private final RepositoryProvider repositoryProvider;
|
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private final RepositoryRequestListenerUtil repositoryRequestListenerUtil;
|
private final RepositoryRequestListenerUtil repositoryRequestListenerUtil;
|
||||||
|
|
||||||
@@ -299,5 +254,4 @@ public class ScmGitServlet extends GitServlet
|
|||||||
private final GitRepositoryViewer repositoryViewer;
|
private final GitRepositoryViewer repositoryViewer;
|
||||||
|
|
||||||
private final LfsServletFactory lfsServletFactory;
|
private final LfsServletFactory lfsServletFactory;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<ScmGitServlet> servletProvider;
|
||||||
|
|
||||||
|
public ScmGitServletProvider() {
|
||||||
|
super(GitRepositoryHandler.TYPE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ScmProviderHttpServlet getRootServlet() {
|
||||||
|
return servletProvider.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,7 +70,7 @@ public class LfsServletFactory {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static String buildBaseUri(Repository repository, HttpServletRequest request) {
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class GitConfigResourceTest {
|
|||||||
private GitConfigDtoToGitConfigMapperImpl dtoToConfigMapper;
|
private GitConfigDtoToGitConfigMapperImpl dtoToConfigMapper;
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private GitConfigToGitConfigDtoMapperImpl configToDtoMapper;
|
private GitConfigToGitConfigDtoMapperImpl configToDtoMapper;
|
||||||
@@ -67,7 +67,7 @@ public class GitConfigResourceTest {
|
|||||||
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
|
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
|
||||||
GitConfigResource gitConfigResource = new GitConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler);
|
GitConfigResource gitConfigResource = new GitConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler);
|
||||||
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class GitConfigToGitConfigDtoMapperTest {
|
|||||||
private URI baseUri = URI.create("http://example.com/base/");
|
private URI baseUri = URI.create("http://example.com/base/");
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private GitConfigToGitConfigDtoMapperImpl mapper;
|
private GitConfigToGitConfigDtoMapperImpl mapper;
|
||||||
@@ -40,7 +40,7 @@ public class GitConfigToGitConfigDtoMapperTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
expectedBaseUri = baseUri.resolve(GitConfigResource.GIT_CONFIG_PATH_V2);
|
expectedBaseUri = baseUri.resolve(GitConfigResource.GIT_CONFIG_PATH_V2);
|
||||||
subjectThreadState.bind();
|
subjectThreadState.bind();
|
||||||
ThreadContext.bind(subject);
|
ThreadContext.bind(subject);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.config.ScmConfiguration;
|
import sonia.scm.config.ScmConfiguration;
|
||||||
import sonia.scm.repository.RepositoryProvider;
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
import sonia.scm.util.HttpUtil;
|
import sonia.scm.util.HttpUtil;
|
||||||
|
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
@@ -29,12 +29,7 @@ import static org.mockito.Mockito.when;
|
|||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class GitPermissionFilterTest {
|
public class GitPermissionFilterTest {
|
||||||
|
|
||||||
@Mock
|
private final GitPermissionFilter permissionFilter = new GitPermissionFilter(new ScmConfiguration(), mock(ScmProviderHttpServlet.class));
|
||||||
private RepositoryProvider repositoryProvider;
|
|
||||||
|
|
||||||
private final GitPermissionFilter permissionFilter = new GitPermissionFilter(
|
|
||||||
new ScmConfiguration(), repositoryProvider
|
|
||||||
);
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private HttpServletResponse response;
|
private HttpServletResponse response;
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ public class LfsServletFactoryTest {
|
|||||||
String repositoryName = "git-lfs-demo";
|
String repositoryName = "git-lfs-demo";
|
||||||
|
|
||||||
String result = LfsServletFactory.buildBaseUri(new Repository("", "GIT", repositoryNamespace, repositoryName), RequestWithUri(repositoryName, true));
|
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 will be with dot-git suffix, ide
|
||||||
result = LfsServletFactory.buildBaseUri(new Repository("", "GIT", repositoryNamespace, repositoryName), RequestWithUri(repositoryName, false));
|
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) {
|
private HttpServletRequest RequestWithUri(String repositoryName, boolean withDotGitSuffix) {
|
||||||
@@ -44,12 +44,10 @@ public class LfsServletFactoryTest {
|
|||||||
|
|
||||||
//build from valid live request data
|
//build from valid live request data
|
||||||
when(mockedRequest.getRequestURL()).thenReturn(
|
when(mockedRequest.getRequestURL()).thenReturn(
|
||||||
new StringBuffer(String.format("http://localhost:8081/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/git/%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");
|
when(mockedRequest.getContextPath()).thenReturn("/scm");
|
||||||
|
|
||||||
return mockedRequest;
|
return mockedRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import static de.otto.edison.hal.Links.linkingTo;
|
|||||||
|
|
||||||
public class HgConfigInstallationsToDtoMapper {
|
public class HgConfigInstallationsToDtoMapper {
|
||||||
|
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public HgConfigInstallationsToDtoMapper(UriInfoStore uriInfoStore) {
|
public HgConfigInstallationsToDtoMapper(ScmPathInfoStore scmPathInfoStore) {
|
||||||
this.uriInfoStore = uriInfoStore;
|
this.scmPathInfoStore = scmPathInfoStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HgConfigInstallationsDto map(List<String> installations, String path) {
|
public HgConfigInstallationsDto map(List<String> installations, String path) {
|
||||||
@@ -20,7 +20,7 @@ public class HgConfigInstallationsToDtoMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String createSelfLink(String path) {
|
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;
|
return linkBuilder.method("getInstallationsResource").parameters().href() + '/' + path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import static de.otto.edison.hal.Links.linkingTo;
|
|||||||
public abstract class HgConfigPackagesToDtoMapper {
|
public abstract class HgConfigPackagesToDtoMapper {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
public HgConfigPackagesDto map(HgPackages hgpackages) {
|
public HgConfigPackagesDto map(HgPackages hgpackages) {
|
||||||
return map(new HgPackagesNonIterable(hgpackages));
|
return map(new HgPackagesNonIterable(hgpackages));
|
||||||
@@ -40,7 +40,7 @@ public abstract class HgConfigPackagesToDtoMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String createSelfLink() {
|
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();
|
return linkBuilder.method("getPackagesResource").parameters().href();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import static de.otto.edison.hal.Links.linkingTo;
|
|||||||
public abstract class HgConfigToHgConfigDtoMapper extends BaseMapper<HgConfig, HgConfigDto> {
|
public abstract class HgConfigToHgConfigDtoMapper extends BaseMapper<HgConfig, HgConfigDto> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
void appendLinks(HgConfig config, @MappingTarget HgConfigDto target) {
|
void appendLinks(HgConfig config, @MappingTarget HgConfigDto target) {
|
||||||
@@ -30,12 +30,12 @@ public abstract class HgConfigToHgConfigDtoMapper extends BaseMapper<HgConfig, H
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String self() {
|
private String self() {
|
||||||
LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), HgConfigResource.class);
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class);
|
||||||
return linkBuilder.method("get").parameters().href();
|
return linkBuilder.method("get").parameters().href();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String update() {
|
private String update() {
|
||||||
LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), HgConfigResource.class);
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class);
|
||||||
return linkBuilder.method("update").parameters().href();
|
return linkBuilder.method("update").parameters().href();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,10 +33,7 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
|
|
||||||
import sonia.scm.repository.Feature;
|
import sonia.scm.repository.Feature;
|
||||||
import sonia.scm.repository.HgHookManager;
|
import sonia.scm.repository.HgHookManager;
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
@@ -44,11 +41,8 @@ import sonia.scm.repository.Repository;
|
|||||||
import sonia.scm.repository.api.Command;
|
import sonia.scm.repository.api.Command;
|
||||||
import sonia.scm.repository.api.CommandNotSupportedException;
|
import sonia.scm.repository.api.CommandNotSupportedException;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -82,16 +76,6 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param hookManager
|
|
||||||
* @param handler
|
|
||||||
* @param repository
|
|
||||||
*/
|
|
||||||
HgRepositoryServiceProvider(HgRepositoryHandler handler,
|
HgRepositoryServiceProvider(HgRepositoryHandler handler,
|
||||||
HgHookManager hookManager, Repository repository)
|
HgHookManager hookManager, Repository repository)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,10 +33,7 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.repository.HgHookManager;
|
import sonia.scm.repository.HgHookManager;
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
@@ -50,19 +47,9 @@ import sonia.scm.repository.Repository;
|
|||||||
public class HgRepositoryServiceResolver implements RepositoryServiceResolver
|
public class HgRepositoryServiceResolver implements RepositoryServiceResolver
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Field description */
|
private HgRepositoryHandler handler;
|
||||||
private static final String TYPE = "hg";
|
private HgHookManager hookManager;
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param hookManager
|
|
||||||
* @param handler
|
|
||||||
*/
|
|
||||||
@Inject
|
@Inject
|
||||||
public HgRepositoryServiceResolver(HgRepositoryHandler handler,
|
public HgRepositoryServiceResolver(HgRepositoryHandler handler,
|
||||||
HgHookManager hookManager)
|
HgHookManager hookManager)
|
||||||
@@ -71,35 +58,14 @@ public class HgRepositoryServiceResolver implements RepositoryServiceResolver
|
|||||||
this.hookManager = hookManager;
|
this.hookManager = hookManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public HgRepositoryServiceProvider resolve(Repository repository)
|
public HgRepositoryServiceProvider resolve(Repository repository) {
|
||||||
{
|
|
||||||
HgRepositoryServiceProvider provider = null;
|
HgRepositoryServiceProvider provider = null;
|
||||||
|
|
||||||
if (TYPE.equalsIgnoreCase(repository.getType()))
|
if (HgRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
||||||
{
|
provider = new HgRepositoryServiceProvider(handler, hookManager, repository);
|
||||||
provider = new HgRepositoryServiceProvider(handler, hookManager,
|
|
||||||
repository);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private HgRepositoryHandler handler;
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private HgHookManager hookManager;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +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;
|
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
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.web.filter.AuthenticationFilter;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
@Priority(Filters.PRIORITY_AUTHENTICATION)
|
|
||||||
@WebElement(value = HgServletModule.MAPPING_HG)
|
|
||||||
public class HgBasicAuthenticationFilter extends AuthenticationFilter
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param configuration
|
|
||||||
* @param webTokenGenerators
|
|
||||||
*/
|
|
||||||
@Inject
|
|
||||||
public HgBasicAuthenticationFilter(ScmConfiguration configuration,
|
|
||||||
Set<WebTokenGenerator> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
package sonia.scm.web;
|
package sonia.scm.web;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -49,8 +47,8 @@ import sonia.scm.repository.HgHookManager;
|
|||||||
import sonia.scm.repository.HgPythonScript;
|
import sonia.scm.repository.HgPythonScript;
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryProvider;
|
|
||||||
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
||||||
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
import sonia.scm.security.CipherUtil;
|
import sonia.scm.security.CipherUtil;
|
||||||
import sonia.scm.util.AssertUtil;
|
import sonia.scm.util.AssertUtil;
|
||||||
import sonia.scm.util.HttpUtil;
|
import sonia.scm.util.HttpUtil;
|
||||||
@@ -68,14 +66,12 @@ import java.io.IOException;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class HgCGIServlet extends HttpServlet
|
public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
@@ -108,20 +104,18 @@ public class HgCGIServlet extends HttpServlet
|
|||||||
*
|
*
|
||||||
* @param cgiExecutorFactory
|
* @param cgiExecutorFactory
|
||||||
* @param configuration
|
* @param configuration
|
||||||
* @param repositoryProvider
|
|
||||||
* @param handler
|
* @param handler
|
||||||
* @param hookManager
|
* @param hookManager
|
||||||
* @param requestListenerUtil
|
* @param requestListenerUtil
|
||||||
*/
|
*/
|
||||||
@Inject
|
@Inject
|
||||||
public HgCGIServlet(CGIExecutorFactory cgiExecutorFactory,
|
public HgCGIServlet(CGIExecutorFactory cgiExecutorFactory,
|
||||||
ScmConfiguration configuration, RepositoryProvider repositoryProvider,
|
ScmConfiguration configuration,
|
||||||
HgRepositoryHandler handler, HgHookManager hookManager,
|
HgRepositoryHandler handler, HgHookManager hookManager,
|
||||||
RepositoryRequestListenerUtil requestListenerUtil)
|
RepositoryRequestListenerUtil requestListenerUtil)
|
||||||
{
|
{
|
||||||
this.cgiExecutorFactory = cgiExecutorFactory;
|
this.cgiExecutorFactory = cgiExecutorFactory;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.repositoryProvider = repositoryProvider;
|
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.hookManager = hookManager;
|
this.hookManager = hookManager;
|
||||||
this.requestListenerUtil = requestListenerUtil;
|
this.requestListenerUtil = requestListenerUtil;
|
||||||
@@ -131,46 +125,11 @@ public class HgCGIServlet extends HttpServlet
|
|||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @throws ServletException
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void init() throws ServletException
|
public void service(HttpServletRequest request,
|
||||||
|
HttpServletResponse response, Repository repository)
|
||||||
{
|
{
|
||||||
|
if (!handler.isConfigured())
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
exceptionHandler.sendFormattedError(request, response,
|
exceptionHandler.sendFormattedError(request, response,
|
||||||
HgCGIExceptionHandler.ERROR_NOT_CONFIGURED);
|
HgCGIExceptionHandler.ERROR_NOT_CONFIGURED);
|
||||||
@@ -379,9 +338,6 @@ public class HgCGIServlet extends HttpServlet
|
|||||||
/** Field description */
|
/** Field description */
|
||||||
private final HgHookManager hookManager;
|
private final HgHookManager hookManager;
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private final RepositoryProvider repositoryProvider;
|
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private final RepositoryRequestListenerUtil requestListenerUtil;
|
private final RepositoryRequestListenerUtil requestListenerUtil;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<HgCGIServlet> servletProvider;
|
||||||
|
|
||||||
|
public HgCGIServletProvider() {
|
||||||
|
super(HgRepositoryHandler.TYPE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ScmProviderHttpServlet getRootServlet() {
|
||||||
|
return servletProvider.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,53 +33,31 @@
|
|||||||
|
|
||||||
package sonia.scm.web;
|
package sonia.scm.web;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
import sonia.scm.Priority;
|
|
||||||
import sonia.scm.config.ScmConfiguration;
|
import sonia.scm.config.ScmConfiguration;
|
||||||
import sonia.scm.filter.Filters;
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
import sonia.scm.filter.WebElement;
|
import sonia.scm.web.filter.PermissionFilter;
|
||||||
import sonia.scm.repository.RepositoryProvider;
|
|
||||||
import sonia.scm.web.filter.ProviderPermissionFilter;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permission filter for mercurial repositories.
|
* Permission filter for mercurial repositories.
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Priority(Filters.PRIORITY_AUTHORIZATION)
|
public class HgPermissionFilter extends PermissionFilter
|
||||||
@WebElement(value = HgServletModule.MAPPING_HG)
|
|
||||||
public class HgPermissionFilter extends ProviderPermissionFilter
|
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final Set<String> READ_METHODS = ImmutableSet.of("GET", "HEAD", "OPTIONS", "TRACE");
|
private static final Set<String> READ_METHODS = ImmutableSet.of("GET", "HEAD", "OPTIONS", "TRACE");
|
||||||
|
|
||||||
/**
|
public HgPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate)
|
||||||
* Constructs a new instance.
|
|
||||||
*
|
|
||||||
* @param configuration scm configuration
|
|
||||||
* @param repositoryProvider repository provider
|
|
||||||
*/
|
|
||||||
@Inject
|
|
||||||
public HgPermissionFilter(ScmConfiguration configuration,
|
|
||||||
RepositoryProvider repositoryProvider)
|
|
||||||
{
|
{
|
||||||
super(configuration, repositoryProvider);
|
super(configuration, delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isWriteRequest(HttpServletRequest request)
|
public boolean isWriteRequest(HttpServletRequest request)
|
||||||
{
|
{
|
||||||
return !READ_METHODS.contains(request.getMethod());
|
return !READ_METHODS.contains(request.getMethod());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ScmPathInfoStore> uriInfoStore, ScmConfiguration scmConfiguration) {
|
||||||
|
super(servletProvider, uriInfoStore, scmConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return HgRepositoryHandler.TYPE_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,8 +81,5 @@ public class HgServletModule extends ServletModule
|
|||||||
|
|
||||||
// bind servlets
|
// bind servlets
|
||||||
serve(MAPPING_HOOK).with(HgHookCallbackServlet.class);
|
serve(MAPPING_HOOK).with(HgHookCallbackServlet.class);
|
||||||
|
|
||||||
// register hg cgi servlet
|
|
||||||
serve(MAPPING_HG).with(HgCGIServlet.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class HgConfigInstallationsResourceTest {
|
|||||||
private final URI baseUri = URI.create("/");
|
private final URI baseUri = URI.create("/");
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private HgConfigInstallationsToDtoMapper mapper;
|
private HgConfigInstallationsToDtoMapper mapper;
|
||||||
@@ -61,7 +61,7 @@ public class HgConfigInstallationsResourceTest {
|
|||||||
new HgConfigResource(null, null, null, null,
|
new HgConfigResource(null, null, null, null,
|
||||||
null, resourceProvider));
|
null, resourceProvider));
|
||||||
|
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class HgConfigInstallationsToDtoMapperTest {
|
|||||||
private URI baseUri = URI.create("http://example.com/base/");
|
private URI baseUri = URI.create("http://example.com/base/");
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private HgConfigInstallationsToDtoMapper mapper;
|
private HgConfigInstallationsToDtoMapper mapper;
|
||||||
@@ -34,7 +34,7 @@ public class HgConfigInstallationsToDtoMapperTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
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);
|
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2 + "/installations/" + expectedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class HgConfigPackageResourceTest {
|
|||||||
private final URI baseUri = java.net.URI.create("/");
|
private final URI baseUri = java.net.URI.create("/");
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private HgConfigPackagesToDtoMapperImpl mapper;
|
private HgConfigPackagesToDtoMapperImpl mapper;
|
||||||
@@ -81,7 +81,7 @@ public class HgConfigPackageResourceTest {
|
|||||||
public void prepareEnvironment() {
|
public void prepareEnvironment() {
|
||||||
setupResources();
|
setupResources();
|
||||||
|
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
|
|
||||||
when(hgPackageReader.getPackages().getPackages()).thenReturn(createPackages());
|
when(hgPackageReader.getPackages().getPackages()).thenReturn(createPackages());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import de.otto.edison.hal.HalRepresentation;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -13,12 +12,10 @@ import sonia.scm.installer.HgPackages;
|
|||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static sonia.scm.api.v2.resources.HgConfigTests.assertEqualsPackage;
|
import static sonia.scm.api.v2.resources.HgConfigTests.assertEqualsPackage;
|
||||||
import static sonia.scm.api.v2.resources.HgConfigTests.createPackage;
|
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/");
|
private URI baseUri = URI.create("http://example.com/base/");
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private HgConfigPackagesToDtoMapperImpl mapper;
|
private HgConfigPackagesToDtoMapperImpl mapper;
|
||||||
@@ -38,7 +35,7 @@ public class HgConfigPackagesToDtoMapperTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2 + "/packages");
|
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2 + "/packages");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class HgConfigResourceTest {
|
|||||||
private HgConfigDtoToHgConfigMapperImpl dtoToConfigMapper;
|
private HgConfigDtoToHgConfigMapperImpl dtoToConfigMapper;
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private HgConfigToHgConfigDtoMapperImpl configToDtoMapper;
|
private HgConfigToHgConfigDtoMapperImpl configToDtoMapper;
|
||||||
@@ -79,7 +79,7 @@ public class HgConfigResourceTest {
|
|||||||
new HgConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, packagesResource,
|
new HgConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, packagesResource,
|
||||||
autoconfigResource, installationsResource);
|
autoconfigResource, installationsResource);
|
||||||
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class HgConfigToHgConfigDtoMapperTest {
|
|||||||
private URI baseUri = URI.create("http://example.com/base/");
|
private URI baseUri = URI.create("http://example.com/base/");
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private HgConfigToHgConfigDtoMapperImpl mapper;
|
private HgConfigToHgConfigDtoMapperImpl mapper;
|
||||||
@@ -41,7 +41,7 @@ public class HgConfigToHgConfigDtoMapperTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2);
|
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2);
|
||||||
subjectThreadState.bind();
|
subjectThreadState.bind();
|
||||||
ThreadContext.bind(subject);
|
ThreadContext.bind(subject);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import static de.otto.edison.hal.Links.linkingTo;
|
|||||||
public abstract class SvnConfigToSvnConfigDtoMapper extends BaseMapper<SvnConfig, SvnConfigDto> {
|
public abstract class SvnConfigToSvnConfigDtoMapper extends BaseMapper<SvnConfig, SvnConfigDto> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
void appendLinks(SvnConfig config, @MappingTarget SvnConfigDto target) {
|
void appendLinks(SvnConfig config, @MappingTarget SvnConfigDto target) {
|
||||||
@@ -30,12 +30,12 @@ public abstract class SvnConfigToSvnConfigDtoMapper extends BaseMapper<SvnConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String self() {
|
private String self() {
|
||||||
LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), SvnConfigResource.class);
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), SvnConfigResource.class);
|
||||||
return linkBuilder.method("get").parameters().href();
|
return linkBuilder.method("get").parameters().href();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String update() {
|
private String update() {
|
||||||
LinkBuilder linkBuilder = new LinkBuilder(uriInfoStore.get(), SvnConfigResource.class);
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), SvnConfigResource.class);
|
||||||
return linkBuilder.method("update").parameters().href();
|
return linkBuilder.method("update").parameters().href();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,19 +33,13 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
|
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.SvnRepositoryHandler;
|
import sonia.scm.repository.SvnRepositoryHandler;
|
||||||
import sonia.scm.repository.api.Command;
|
import sonia.scm.repository.api.Command;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,13 +59,6 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param handler
|
|
||||||
* @param repository
|
|
||||||
*/
|
|
||||||
SvnRepositoryServiceProvider(SvnRepositoryHandler handler,
|
SvnRepositoryServiceProvider(SvnRepositoryHandler handler,
|
||||||
Repository repository)
|
Repository repository)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,64 +32,29 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.SvnRepositoryHandler;
|
import sonia.scm.repository.SvnRepositoryHandler;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
@Extension
|
@Extension
|
||||||
public class SvnRepositoryServiceResolver implements RepositoryServiceResolver
|
public class SvnRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||||
{
|
|
||||||
|
|
||||||
/** Field description */
|
private SvnRepositoryHandler handler;
|
||||||
public static final String TYPE = "svn";
|
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param handler
|
|
||||||
*/
|
|
||||||
@Inject
|
@Inject
|
||||||
public SvnRepositoryServiceResolver(SvnRepositoryHandler handler)
|
public SvnRepositoryServiceResolver(SvnRepositoryHandler handler) {
|
||||||
{
|
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SvnRepositoryServiceProvider resolve(Repository repository)
|
public SvnRepositoryServiceProvider resolve(Repository repository) {
|
||||||
{
|
|
||||||
SvnRepositoryServiceProvider provider = null;
|
SvnRepositoryServiceProvider provider = null;
|
||||||
|
|
||||||
if (TYPE.equalsIgnoreCase(repository.getType()))
|
if (SvnRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
||||||
{
|
|
||||||
provider = new SvnRepositoryServiceProvider(handler, repository);
|
provider = new SvnRepositoryServiceProvider(handler, repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private SvnRepositoryHandler handler;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +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;
|
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
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.SvnUtil;
|
|
||||||
import sonia.scm.util.HttpUtil;
|
|
||||||
import sonia.scm.web.filter.AuthenticationFilter;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
@Priority(Filters.PRIORITY_AUTHENTICATION)
|
|
||||||
@WebElement(value = SvnServletModule.PATTERN_SVN)
|
|
||||||
public class SvnBasicAuthenticationFilter extends AuthenticationFilter
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param configuration
|
|
||||||
* @param webTokenGenerators
|
|
||||||
*/
|
|
||||||
@Inject
|
|
||||||
public SvnBasicAuthenticationFilter(ScmConfiguration configuration, Set<WebTokenGenerator> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
package sonia.scm.web;
|
package sonia.scm.web;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -45,6 +43,7 @@ import sonia.scm.repository.Repository;
|
|||||||
import sonia.scm.repository.RepositoryProvider;
|
import sonia.scm.repository.RepositoryProvider;
|
||||||
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
||||||
import sonia.scm.repository.SvnRepositoryHandler;
|
import sonia.scm.repository.SvnRepositoryHandler;
|
||||||
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
import sonia.scm.util.AssertUtil;
|
import sonia.scm.util.AssertUtil;
|
||||||
import sonia.scm.util.HttpUtil;
|
import sonia.scm.util.HttpUtil;
|
||||||
|
|
||||||
@@ -54,14 +53,12 @@ import javax.servlet.http.HttpServletRequestWrapper;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class SvnDAVServlet extends DAVServlet
|
public class SvnDAVServlet extends DAVServlet implements ScmProviderHttpServlet
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
@@ -110,28 +107,18 @@ public class SvnDAVServlet extends DAVServlet
|
|||||||
* @throws ServletException
|
* @throws ServletException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void service(HttpServletRequest request, HttpServletResponse response)
|
public void service(HttpServletRequest request, HttpServletResponse response, Repository repository)
|
||||||
throws ServletException, IOException
|
throws ServletException, IOException
|
||||||
{
|
{
|
||||||
Repository repository = repositoryProvider.get();
|
if (repositoryRequestListenerUtil.callListeners(request, response,
|
||||||
|
repository))
|
||||||
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
|
|
||||||
{
|
{
|
||||||
super.service(new SvnHttpServletRequestWrapper(request,
|
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
|
extends HttpServletRequestWrapper
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
* @param repositoryProvider
|
|
||||||
*/
|
|
||||||
public SvnHttpServletRequestWrapper(HttpServletRequest request,
|
public SvnHttpServletRequestWrapper(HttpServletRequest request,
|
||||||
RepositoryProvider repositoryProvider)
|
Repository repository)
|
||||||
{
|
{
|
||||||
super(request);
|
super(request);
|
||||||
this.repositoryProvider = repositoryProvider;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods --------------------------------------------------------
|
//~--- get methods --------------------------------------------------------
|
||||||
@@ -211,8 +191,6 @@ public class SvnDAVServlet extends DAVServlet
|
|||||||
|
|
||||||
AssertUtil.assertIsNotEmpty(pathInfo);
|
AssertUtil.assertIsNotEmpty(pathInfo);
|
||||||
|
|
||||||
Repository repository = repositoryProvider.get();
|
|
||||||
|
|
||||||
if (repository != null)
|
if (repository != null)
|
||||||
{
|
{
|
||||||
if (pathInfo.startsWith(HttpUtil.SEPARATOR_PATH))
|
if (pathInfo.startsWith(HttpUtil.SEPARATOR_PATH))
|
||||||
@@ -236,7 +214,6 @@ public class SvnDAVServlet extends DAVServlet
|
|||||||
public String getServletPath()
|
public String getServletPath()
|
||||||
{
|
{
|
||||||
String servletPath = super.getServletPath();
|
String servletPath = super.getServletPath();
|
||||||
Repository repository = repositoryProvider.get();
|
|
||||||
|
|
||||||
if (repository != null)
|
if (repository != null)
|
||||||
{
|
{
|
||||||
@@ -280,10 +257,9 @@ public class SvnDAVServlet extends DAVServlet
|
|||||||
//~--- fields -------------------------------------------------------------
|
//~--- fields -------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private final RepositoryProvider repositoryProvider;
|
private final Repository repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
|
|||||||
@@ -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<SvnDAVServlet> servletProvider;
|
||||||
|
|
||||||
|
public SvnDAVServletProvider() {
|
||||||
|
super(SvnRepositoryHandler.TYPE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ScmProviderHttpServlet getRootServlet() {
|
||||||
|
return servletProvider.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,38 +34,34 @@ package sonia.scm.web;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sonia.scm.filter.GZipFilter;
|
import sonia.scm.filter.GZipFilter;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.SvnRepositoryHandler;
|
import sonia.scm.repository.SvnRepositoryHandler;
|
||||||
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
|
import javax.servlet.ServletConfig;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Singleton
|
public class SvnGZipFilter extends GZipFilter implements ScmProviderHttpServlet
|
||||||
public class SvnGZipFilter extends GZipFilter
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
private static final Logger logger = LoggerFactory.getLogger(SvnGZipFilter.class);
|
||||||
* the logger for SvnGZipFilter
|
|
||||||
*/
|
private final SvnRepositoryHandler handler;
|
||||||
private static final Logger logger =
|
private final ScmProviderHttpServlet delegate;
|
||||||
LoggerFactory.getLogger(SvnGZipFilter.class);
|
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
@@ -75,10 +71,10 @@ public class SvnGZipFilter extends GZipFilter
|
|||||||
*
|
*
|
||||||
* @param handler
|
* @param handler
|
||||||
*/
|
*/
|
||||||
@Inject
|
public SvnGZipFilter(SvnRepositoryHandler handler, ScmProviderHttpServlet delegate)
|
||||||
public SvnGZipFilter(SvnRepositoryHandler handler)
|
|
||||||
{
|
{
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
|
this.delegate = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- 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 */
|
super.doFilter(request, response, (servletRequest, servletResponse) -> delegate.service((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse, repository));
|
||||||
private SvnRepositoryHandler handler;
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,37 +33,24 @@
|
|||||||
|
|
||||||
package sonia.scm.web;
|
package sonia.scm.web;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
import sonia.scm.ClientMessages;
|
import sonia.scm.ClientMessages;
|
||||||
import sonia.scm.Priority;
|
|
||||||
import sonia.scm.config.ScmConfiguration;
|
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.ScmSvnErrorCode;
|
||||||
import sonia.scm.repository.SvnUtil;
|
import sonia.scm.repository.SvnUtil;
|
||||||
import sonia.scm.web.filter.ProviderPermissionFilter;
|
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||||
|
import sonia.scm.web.filter.PermissionFilter;
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Priority(Filters.PRIORITY_AUTHORIZATION)
|
public class SvnPermissionFilter extends PermissionFilter
|
||||||
@WebElement(value = SvnServletModule.PATTERN_SVN)
|
|
||||||
public class SvnPermissionFilter extends ProviderPermissionFilter
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
@@ -77,13 +64,10 @@ public class SvnPermissionFilter extends ProviderPermissionFilter
|
|||||||
* Constructs ...
|
* Constructs ...
|
||||||
*
|
*
|
||||||
* @param configuration
|
* @param configuration
|
||||||
* @param repository
|
|
||||||
*/
|
*/
|
||||||
@Inject
|
public SvnPermissionFilter(ScmConfiguration configuration, ScmProviderHttpServlet delegate)
|
||||||
public SvnPermissionFilter(ScmConfiguration configuration,
|
|
||||||
RepositoryProvider repository)
|
|
||||||
{
|
{
|
||||||
super(configuration, repository);
|
super(configuration, delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
@@ -132,7 +116,7 @@ public class SvnPermissionFilter extends ProviderPermissionFilter
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean isWriteRequest(HttpServletRequest request)
|
public boolean isWriteRequest(HttpServletRequest request)
|
||||||
{
|
{
|
||||||
return WRITEMETHOD_SET.contains(request.getMethod().toUpperCase());
|
return WRITEMETHOD_SET.contains(request.getMethod().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ScmPathInfoStore> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,53 +33,22 @@
|
|||||||
|
|
||||||
package sonia.scm.web;
|
package sonia.scm.web;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.inject.servlet.ServletModule;
|
import com.google.inject.servlet.ServletModule;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
import sonia.scm.api.v2.resources.SvnConfigDtoToSvnConfigMapper;
|
import sonia.scm.api.v2.resources.SvnConfigDtoToSvnConfigMapper;
|
||||||
import sonia.scm.api.v2.resources.SvnConfigToSvnConfigDtoMapper;
|
import sonia.scm.api.v2.resources.SvnConfigToSvnConfigDtoMapper;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Extension
|
@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
|
@Override
|
||||||
protected void configureServlets()
|
protected void configureServlets() {
|
||||||
{
|
|
||||||
filter(PATTERN_SVN).through(SvnGZipFilter.class);
|
|
||||||
filter(PATTERN_SVN).through(SvnBasicAuthenticationFilter.class);
|
|
||||||
filter(PATTERN_SVN).through(SvnPermissionFilter.class);
|
|
||||||
|
|
||||||
bind(SvnConfigDtoToSvnConfigMapper.class).to(Mappers.getMapper(SvnConfigDtoToSvnConfigMapper.class).getClass());
|
bind(SvnConfigDtoToSvnConfigMapper.class).to(Mappers.getMapper(SvnConfigDtoToSvnConfigMapper.class).getClass());
|
||||||
bind(SvnConfigToSvnConfigDtoMapper.class).to(Mappers.getMapper(SvnConfigToSvnConfigDtoMapper.class).getClass());
|
bind(SvnConfigToSvnConfigDtoMapper.class).to(Mappers.getMapper(SvnConfigToSvnConfigDtoMapper.class).getClass());
|
||||||
|
|
||||||
Map<String, String> parameters = new HashMap<String, String>();
|
|
||||||
|
|
||||||
parameters.put(PARAMETER_SVN_PARENTPATH,
|
|
||||||
System.getProperty("java.io.tmpdir"));
|
|
||||||
serve(PATTERN_SVN).with(SvnDAVServlet.class, parameters);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class SvnConfigResourceTest {
|
|||||||
private SvnConfigDtoToSvnConfigMapperImpl dtoToConfigMapper;
|
private SvnConfigDtoToSvnConfigMapperImpl dtoToConfigMapper;
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private SvnConfigToSvnConfigDtoMapperImpl configToDtoMapper;
|
private SvnConfigToSvnConfigDtoMapperImpl configToDtoMapper;
|
||||||
@@ -67,7 +67,7 @@ public class SvnConfigResourceTest {
|
|||||||
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
|
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
|
||||||
SvnConfigResource gitConfigResource = new SvnConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler);
|
SvnConfigResource gitConfigResource = new SvnConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler);
|
||||||
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class SvnConfigToSvnConfigDtoMapperTest {
|
|||||||
private URI baseUri = URI.create("http://example.com/base/");
|
private URI baseUri = URI.create("http://example.com/base/");
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UriInfoStore uriInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private SvnConfigToSvnConfigDtoMapperImpl mapper;
|
private SvnConfigToSvnConfigDtoMapperImpl mapper;
|
||||||
@@ -42,7 +42,7 @@ public class SvnConfigToSvnConfigDtoMapperTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
when(uriInfoStore.get().getBaseUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
expectedBaseUri = baseUri.resolve(SvnConfigResource.SVN_CONFIG_PATH_V2);
|
expectedBaseUri = baseUri.resolve(SvnConfigResource.SVN_CONFIG_PATH_V2);
|
||||||
subjectThreadState.bind();
|
subjectThreadState.bind();
|
||||||
ThreadContext.bind(subject);
|
ThreadContext.bind(subject);
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
package sonia.scm;
|
package sonia.scm;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
@@ -63,8 +61,6 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
@@ -135,7 +131,7 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
|||||||
moduleList.add(new EagerSingletonModule());
|
moduleList.add(new EagerSingletonModule());
|
||||||
moduleList.add(ShiroWebModule.guiceFilterModule());
|
moduleList.add(ShiroWebModule.guiceFilterModule());
|
||||||
moduleList.add(new WebElementModule(pluginLoader));
|
moduleList.add(new WebElementModule(pluginLoader));
|
||||||
moduleList.add(new ScmServletModule(context, pluginLoader, overrides, pluginLoader.getExtensionProcessor()));
|
moduleList.add(new ScmServletModule(context, pluginLoader, overrides));
|
||||||
moduleList.add(
|
moduleList.add(
|
||||||
new ScmSecurityModule(context, pluginLoader.getExtensionProcessor())
|
new ScmSecurityModule(context, pluginLoader.getExtensionProcessor())
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
package sonia.scm;
|
package sonia.scm;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.multibindings.Multibinder;
|
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.DefaultFileSystem;
|
||||||
import sonia.scm.io.FileSystem;
|
import sonia.scm.io.FileSystem;
|
||||||
import sonia.scm.net.SSLContextProvider;
|
import sonia.scm.net.SSLContextProvider;
|
||||||
import sonia.scm.net.ahc.*;
|
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||||
import sonia.scm.plugin.*;
|
import sonia.scm.net.ahc.ContentTransformer;
|
||||||
import sonia.scm.repository.*;
|
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.HookContextFactory;
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
import sonia.scm.repository.spi.HookEventFacade;
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
import sonia.scm.repository.xml.XmlRepositoryDAO;
|
import sonia.scm.repository.xml.XmlRepositoryDAO;
|
||||||
import sonia.scm.schedule.QuartzScheduler;
|
import sonia.scm.schedule.QuartzScheduler;
|
||||||
import sonia.scm.schedule.Scheduler;
|
import sonia.scm.schedule.Scheduler;
|
||||||
import sonia.scm.security.*;
|
import sonia.scm.security.AuthorizationChangedEventProducer;
|
||||||
import sonia.scm.store.*;
|
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.MustacheTemplateEngine;
|
||||||
import sonia.scm.template.TemplateEngine;
|
import sonia.scm.template.TemplateEngine;
|
||||||
import sonia.scm.template.TemplateEngineFactory;
|
import sonia.scm.template.TemplateEngineFactory;
|
||||||
@@ -81,14 +110,16 @@ import sonia.scm.util.ScmConfigurationUtil;
|
|||||||
import sonia.scm.web.UserAgentParser;
|
import sonia.scm.web.UserAgentParser;
|
||||||
import sonia.scm.web.cgi.CGIExecutorFactory;
|
import sonia.scm.web.cgi.CGIExecutorFactory;
|
||||||
import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
|
import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
|
||||||
|
import sonia.scm.web.filter.AuthenticationFilter;
|
||||||
import sonia.scm.web.filter.LoggingFilter;
|
import sonia.scm.web.filter.LoggingFilter;
|
||||||
|
import sonia.scm.web.protocol.HttpProtocolServlet;
|
||||||
import sonia.scm.web.security.AdministrationContext;
|
import sonia.scm.web.security.AdministrationContext;
|
||||||
import sonia.scm.web.security.DefaultAdministrationContext;
|
import sonia.scm.web.security.DefaultAdministrationContext;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.servlet.ServletContext;
|
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 */
|
/** Field description */
|
||||||
public static final String[] PATTERN_ADMIN = new String[] {
|
public static final String[] PATTERN_ADMIN = new String[] {
|
||||||
"/api/rest/groups*",
|
REST_API_PATH + "/groups*",
|
||||||
"/api/rest/users*", "/api/rest/plguins*" };
|
REST_API_PATH + "/users*", REST_API_PATH + "/plguins*" };
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_ALL = "/*";
|
public static final String PATTERN_ALL = "/*";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_CONFIG = "/api/rest/config*";
|
public static final String PATTERN_CONFIG = REST_API_PATH + "/config*";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PATTERN_DEBUG = "/debug.html";
|
public static final String PATTERN_DEBUG = "/debug.html";
|
||||||
@@ -155,22 +186,11 @@ public class ScmServletModule extends ServletModule
|
|||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
ScmServletModule(ServletContext servletContext, DefaultPluginLoader pluginLoader, ClassOverrides overrides)
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* @param pluginLoader
|
|
||||||
* @param overrides
|
|
||||||
* @param extensionProcessor
|
|
||||||
*/
|
|
||||||
ScmServletModule(ServletContext servletContext,
|
|
||||||
DefaultPluginLoader pluginLoader, ClassOverrides overrides, ExtensionProcessor extensionProcessor)
|
|
||||||
{
|
{
|
||||||
this.servletContext = servletContext;
|
this.servletContext = servletContext;
|
||||||
this.pluginLoader = pluginLoader;
|
this.pluginLoader = pluginLoader;
|
||||||
this.overrides = overrides;
|
this.overrides = overrides;
|
||||||
this.extensionProcessor = extensionProcessor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
@@ -293,6 +313,8 @@ public class ScmServletModule extends ServletModule
|
|||||||
bind(TemplateEngineFactory.class);
|
bind(TemplateEngineFactory.class);
|
||||||
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
|
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
|
||||||
|
|
||||||
|
filter(HttpProtocolServlet.PATTERN).through(AuthenticationFilter.class);
|
||||||
|
|
||||||
// bind events
|
// bind events
|
||||||
// bind(LastModifiedUpdateListener.class);
|
// bind(LastModifiedUpdateListener.class);
|
||||||
|
|
||||||
@@ -389,11 +411,6 @@ public class ScmServletModule extends ServletModule
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Load ScmConfiguration with JAXB
|
* Load ScmConfiguration with JAXB
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private ScmConfiguration getScmConfiguration()
|
private ScmConfiguration getScmConfiguration()
|
||||||
{
|
{
|
||||||
@@ -414,6 +431,4 @@ public class ScmServletModule extends ServletModule
|
|||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private final ServletContext servletContext;
|
private final ServletContext servletContext;
|
||||||
|
|
||||||
private final ExtensionProcessor extensionProcessor;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class WebResourceServlet extends HttpServlet {
|
|||||||
* TODO remove old protocol servlets and hook. Move /hook/hg to api?
|
* TODO remove old protocol servlets and hook. Move /hook/hg to api?
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@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);
|
private static final Logger LOG = LoggerFactory.getLogger(WebResourceServlet.class);
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
package sonia.scm.api.rest;
|
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.inject.Inject;
|
||||||
import javax.ws.rs.container.ContainerRequestContext;
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
import javax.ws.rs.container.ContainerRequestFilter;
|
import javax.ws.rs.container.ContainerRequestFilter;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
import javax.ws.rs.ext.Provider;
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
@Provider
|
@Provider
|
||||||
public class UriInfoFilter implements ContainerRequestFilter {
|
public class UriInfoFilter implements ContainerRequestFilter {
|
||||||
|
|
||||||
private final javax.inject.Provider<UriInfoStore> storeProvider;
|
private final javax.inject.Provider<ScmPathInfoStore> storeProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UriInfoFilter(javax.inject.Provider<UriInfoStore> storeProvider) {
|
public UriInfoFilter(javax.inject.Provider<ScmPathInfoStore> storeProvider) {
|
||||||
this.storeProvider = storeProvider;
|
this.storeProvider = storeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void filter(ContainerRequestContext requestContext) {
|
public void filter(ContainerRequestContext requestContext) {
|
||||||
storeProvider.get().set(requestContext.getUriInfo());
|
UriInfo uriInfo = requestContext.getUriInfo();
|
||||||
|
storeProvider.get().set(uriInfo::getBaseUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,37 +33,28 @@
|
|||||||
|
|
||||||
package sonia.scm.api.rest.resources;
|
package sonia.scm.api.rest.resources;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryManager;
|
import sonia.scm.repository.RepositoryManager;
|
||||||
import sonia.scm.repository.RepositoryTypePredicate;
|
import sonia.scm.repository.RepositoryTypePredicate;
|
||||||
import sonia.scm.util.HttpUtil;
|
import sonia.scm.template.Viewable;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
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)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
public Viewable renderRepositoriesRoot(@Context HttpServletRequest request, @PathParam("type") final String type)
|
public Viewable renderRepositoriesRoot(@Context HttpServletRequest request, @PathParam("type") final String type)
|
||||||
{
|
{
|
||||||
String baseUrl = HttpUtil.getCompleteUrl(request);
|
|
||||||
//J-
|
//J-
|
||||||
Collection<RepositoryTemplateElement> unsortedRepositories =
|
Collection<RepositoryTemplateElement> unsortedRepositories =
|
||||||
Collections2.transform(
|
Collections2.transform(
|
||||||
Collections2.filter(
|
Collections2.filter(
|
||||||
repositoryManager.getAll(), new RepositoryTypePredicate(type))
|
repositoryManager.getAll(), new RepositoryTypePredicate(type))
|
||||||
, new RepositoryTransformFunction(baseUrl)
|
, new RepositoryTransformFunction()
|
||||||
);
|
);
|
||||||
|
|
||||||
List<RepositoryTemplateElement> repositories = Ordering.from(
|
List<RepositoryTemplateElement> repositories = Ordering.from(
|
||||||
@@ -138,17 +128,9 @@ public class RepositoryRootResource
|
|||||||
public static class RepositoryTemplateElement
|
public static class RepositoryTemplateElement
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
public RepositoryTemplateElement(Repository repository)
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
* @param baseUrl
|
|
||||||
*/
|
|
||||||
public RepositoryTemplateElement(Repository repository, String baseUrl)
|
|
||||||
{
|
{
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods --------------------------------------------------------
|
//~--- get methods --------------------------------------------------------
|
||||||
@@ -175,22 +157,8 @@ public class RepositoryRootResource
|
|||||||
return repository;
|
return repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getUrl()
|
|
||||||
{
|
|
||||||
return repository.createUrl(baseUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
//~--- fields -------------------------------------------------------------
|
//~--- fields -------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private String baseUrl;
|
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private Repository repository;
|
private Repository repository;
|
||||||
|
|
||||||
@@ -236,31 +204,10 @@ public class RepositoryRootResource
|
|||||||
private static class RepositoryTransformFunction
|
private static class RepositoryTransformFunction
|
||||||
implements Function<Repository, RepositoryTemplateElement>
|
implements Function<Repository, RepositoryTemplateElement>
|
||||||
{
|
{
|
||||||
|
|
||||||
public RepositoryTransformFunction(String baseUrl)
|
|
||||||
{
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
//~--- methods ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public RepositoryTemplateElement apply(Repository repository)
|
public RepositoryTemplateElement apply(Repository repository)
|
||||||
{
|
{
|
||||||
return new RepositoryTemplateElement(repository, baseUrl);
|
return new RepositoryTemplateElement(repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields -------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private String baseUrl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,6 @@ public class MapperModule extends AbstractModule {
|
|||||||
bind(UIPluginDtoMapper.class);
|
bind(UIPluginDtoMapper.class);
|
||||||
bind(UIPluginDtoCollectionMapper.class);
|
bind(UIPluginDtoCollectionMapper.class);
|
||||||
|
|
||||||
bind(UriInfoStore.class).in(ServletScopes.REQUEST);
|
bind(ScmPathInfoStore.class).in(ServletScopes.REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public abstract class MeToUserDtoMapper extends UserToUserDtoMapper{
|
|||||||
|
|
||||||
|
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
void appendLinks(User user, @MappingTarget UserDto target) {
|
protected void appendLinks(User user, @MappingTarget UserDto target) {
|
||||||
Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self());
|
Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self());
|
||||||
if (UserPermissions.delete(user).isPermitted()) {
|
if (UserPermissions.delete(user).isPermitted()) {
|
||||||
linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName())));
|
linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName())));
|
||||||
|
|||||||
@@ -238,8 +238,9 @@ public class PermissionRootResource {
|
|||||||
* @throws RepositoryNotFoundException if the repository does not exists
|
* @throws RepositoryNotFoundException if the repository does not exists
|
||||||
*/
|
*/
|
||||||
private Repository load(String namespace, String name) throws RepositoryNotFoundException {
|
private Repository load(String namespace, String name) throws RepositoryNotFoundException {
|
||||||
return Optional.ofNullable(manager.get(new NamespaceAndName(namespace, name)))
|
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
|
||||||
.orElseThrow(() -> new RepositoryNotFoundException(name));
|
return Optional.ofNullable(manager.get(namespaceAndName))
|
||||||
|
.orElseThrow(() -> new RepositoryNotFoundException(namespaceAndName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class RepositoryDto extends HalRepresentation {
|
|||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private Instant lastModified;
|
private Instant lastModified;
|
||||||
private String namespace;
|
private String namespace;
|
||||||
@Pattern(regexp = "(?!^\\.\\.$)(?!^\\.$)(?!.*[\\\\\\[\\]])^[A-z0-9\\.][A-z0-9\\.\\-_/]*$")
|
@Pattern(regexp = "^[A-z0-9\\-_]+$")
|
||||||
private String name;
|
private String name;
|
||||||
private boolean archived = false;
|
private boolean archived = false;
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import de.otto.edison.hal.Link;
|
||||||
import de.otto.edison.hal.Links;
|
import de.otto.edison.hal.Links;
|
||||||
import org.mapstruct.AfterMapping;
|
import org.mapstruct.AfterMapping;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
@@ -11,9 +12,13 @@ import sonia.scm.repository.RepositoryPermissions;
|
|||||||
import sonia.scm.repository.api.Command;
|
import sonia.scm.repository.api.Command;
|
||||||
import sonia.scm.repository.api.RepositoryService;
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
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.Link.link;
|
||||||
import static de.otto.edison.hal.Links.linkingTo;
|
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.
|
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||||
@SuppressWarnings("squid:S3306")
|
@SuppressWarnings("squid:S3306")
|
||||||
@@ -30,7 +35,6 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
|||||||
@AfterMapping
|
@AfterMapping
|
||||||
void appendLinks(Repository repository, @MappingTarget RepositoryDto target) {
|
void appendLinks(Repository repository, @MappingTarget RepositoryDto target) {
|
||||||
Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(target.getNamespace(), target.getName()));
|
Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(target.getNamespace(), target.getName()));
|
||||||
linksBuilder.single(link("httpProtocol", resourceLinks.repository().clone(target.getType(), target.getNamespace(), target.getName())));
|
|
||||||
if (RepositoryPermissions.delete(repository).isPermitted()) {
|
if (RepositoryPermissions.delete(repository).isPermitted()) {
|
||||||
linksBuilder.single(link("delete", resourceLinks.repository().delete(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("delete", resourceLinks.repository().delete(target.getNamespace(), target.getName())));
|
||||||
}
|
}
|
||||||
@@ -39,6 +43,12 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
|||||||
linksBuilder.single(link("permissions", resourceLinks.permission().all(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("permissions", resourceLinks.permission().all(target.getNamespace(), target.getName())));
|
||||||
}
|
}
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
|
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
|
||||||
|
if (RepositoryPermissions.pull(repository).isPermitted()) {
|
||||||
|
List<Link> protocolLinks = repositoryService.getSupportedProtocols()
|
||||||
|
.map(this::createProtocolLink)
|
||||||
|
.collect(toList());
|
||||||
|
linksBuilder.array(protocolLinks);
|
||||||
|
}
|
||||||
if (repositoryService.isSupported(Command.TAGS)) {
|
if (repositoryService.isSupported(Command.TAGS)) {
|
||||||
linksBuilder.single(link("tags", resourceLinks.tag().all(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("tags", resourceLinks.tag().all(target.getNamespace(), target.getName())));
|
||||||
}
|
}
|
||||||
@@ -50,4 +60,8 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
|||||||
linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName())));
|
||||||
target.add(linksBuilder.build());
|
target.add(linksBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Link createProtocolLink(ScmProtocol protocol) {
|
||||||
|
return Link.linkBuilder("protocol", protocol.getUrl()).withName(protocol.getType()).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import java.net.URI;
|
|||||||
|
|
||||||
class ResourceLinks {
|
class ResourceLinks {
|
||||||
|
|
||||||
private final UriInfoStore uriInfoStore;
|
private final ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ResourceLinks(UriInfoStore uriInfoStore) {
|
ResourceLinks(ScmPathInfoStore scmPathInfoStore) {
|
||||||
this.uriInfoStore = uriInfoStore;
|
this.scmPathInfoStore = scmPathInfoStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we have to add the file path using URI, so that path separators (aka '/') will not be encoded as '%2F'
|
// we have to add the file path using URI, so that path separators (aka '/') will not be encoded as '%2F'
|
||||||
@@ -21,14 +21,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GroupLinks group() {
|
GroupLinks group() {
|
||||||
return new GroupLinks(uriInfoStore.get());
|
return new GroupLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class GroupLinks {
|
static class GroupLinks {
|
||||||
private final LinkBuilder groupLinkBuilder;
|
private final LinkBuilder groupLinkBuilder;
|
||||||
|
|
||||||
GroupLinks(UriInfo uriInfo) {
|
GroupLinks(ScmPathInfo pathInfo) {
|
||||||
groupLinkBuilder = new LinkBuilder(uriInfo, GroupRootResource.class, GroupResource.class);
|
groupLinkBuilder = new LinkBuilder(pathInfo, GroupRootResource.class, GroupResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String name) {
|
String self(String name) {
|
||||||
@@ -45,14 +45,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GroupCollectionLinks groupCollection() {
|
GroupCollectionLinks groupCollection() {
|
||||||
return new GroupCollectionLinks(uriInfoStore.get());
|
return new GroupCollectionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class GroupCollectionLinks {
|
static class GroupCollectionLinks {
|
||||||
private final LinkBuilder collectionLinkBuilder;
|
private final LinkBuilder collectionLinkBuilder;
|
||||||
|
|
||||||
GroupCollectionLinks(UriInfo uriInfo) {
|
GroupCollectionLinks(ScmPathInfo pathInfo) {
|
||||||
collectionLinkBuilder = new LinkBuilder(uriInfo, GroupRootResource.class, GroupCollectionResource.class);
|
collectionLinkBuilder = new LinkBuilder(pathInfo, GroupRootResource.class, GroupCollectionResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self() {
|
String self() {
|
||||||
@@ -65,14 +65,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UserLinks user() {
|
UserLinks user() {
|
||||||
return new UserLinks(uriInfoStore.get());
|
return new UserLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UserLinks {
|
static class UserLinks {
|
||||||
private final LinkBuilder userLinkBuilder;
|
private final LinkBuilder userLinkBuilder;
|
||||||
|
|
||||||
UserLinks(UriInfo uriInfo) {
|
UserLinks(ScmPathInfo pathInfo) {
|
||||||
userLinkBuilder = new LinkBuilder(uriInfo, UserRootResource.class, UserResource.class);
|
userLinkBuilder = new LinkBuilder(pathInfo, UserRootResource.class, UserResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String name) {
|
String self(String name) {
|
||||||
@@ -93,14 +93,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MeLinks me() {
|
MeLinks me() {
|
||||||
return new MeLinks(uriInfoStore.get(), this.user());
|
return new MeLinks(scmPathInfoStore.get(), this.user());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class MeLinks {
|
static class MeLinks {
|
||||||
private final LinkBuilder meLinkBuilder;
|
private final LinkBuilder meLinkBuilder;
|
||||||
private UserLinks userLinks;
|
private UserLinks userLinks;
|
||||||
|
|
||||||
MeLinks(UriInfo uriInfo, UserLinks user) {
|
MeLinks(ScmPathInfo uriInfo, UserLinks user) {
|
||||||
meLinkBuilder = new LinkBuilder(uriInfo, MeResource.class);
|
meLinkBuilder = new LinkBuilder(uriInfo, MeResource.class);
|
||||||
userLinks = user;
|
userLinks = user;
|
||||||
}
|
}
|
||||||
@@ -124,14 +124,14 @@ class ResourceLinks {
|
|||||||
|
|
||||||
|
|
||||||
UserCollectionLinks userCollection() {
|
UserCollectionLinks userCollection() {
|
||||||
return new UserCollectionLinks(uriInfoStore.get());
|
return new UserCollectionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UserCollectionLinks {
|
static class UserCollectionLinks {
|
||||||
private final LinkBuilder collectionLinkBuilder;
|
private final LinkBuilder collectionLinkBuilder;
|
||||||
|
|
||||||
UserCollectionLinks(UriInfo uriInfo) {
|
UserCollectionLinks(ScmPathInfo pathInfo) {
|
||||||
collectionLinkBuilder = new LinkBuilder(uriInfo, UserRootResource.class, UserCollectionResource.class);
|
collectionLinkBuilder = new LinkBuilder(pathInfo, UserRootResource.class, UserCollectionResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self() {
|
String self() {
|
||||||
@@ -144,14 +144,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConfigLinks config() {
|
ConfigLinks config() {
|
||||||
return new ConfigLinks(uriInfoStore.get());
|
return new ConfigLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ConfigLinks {
|
static class ConfigLinks {
|
||||||
private final LinkBuilder configLinkBuilder;
|
private final LinkBuilder configLinkBuilder;
|
||||||
|
|
||||||
ConfigLinks(UriInfo uriInfo) {
|
ConfigLinks(ScmPathInfo pathInfo) {
|
||||||
configLinkBuilder = new LinkBuilder(uriInfo, ConfigResource.class);
|
configLinkBuilder = new LinkBuilder(pathInfo, ConfigResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self() {
|
String self() {
|
||||||
@@ -164,26 +164,20 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public RepositoryLinks repository() {
|
public RepositoryLinks repository() {
|
||||||
return new RepositoryLinks(uriInfoStore.get());
|
return new RepositoryLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class RepositoryLinks {
|
static class RepositoryLinks {
|
||||||
private final LinkBuilder repositoryLinkBuilder;
|
private final LinkBuilder repositoryLinkBuilder;
|
||||||
private final UriInfo uriInfo;
|
|
||||||
|
|
||||||
RepositoryLinks(UriInfo uriInfo) {
|
RepositoryLinks(ScmPathInfo pathInfo) {
|
||||||
repositoryLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class);
|
repositoryLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class);
|
||||||
this.uriInfo = uriInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name) {
|
String self(String namespace, String name) {
|
||||||
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("get").parameters().href();
|
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("get").parameters().href();
|
||||||
}
|
}
|
||||||
|
|
||||||
String clone(String type, String namespace, String name) {
|
|
||||||
return uriInfo.getBaseUri().resolve(URI.create("../../" + type + "/" + namespace + "/" + name)).toASCIIString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String delete(String namespace, String name) {
|
String delete(String namespace, String name) {
|
||||||
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("delete").parameters().href();
|
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("delete").parameters().href();
|
||||||
}
|
}
|
||||||
@@ -194,14 +188,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RepositoryCollectionLinks repositoryCollection() {
|
RepositoryCollectionLinks repositoryCollection() {
|
||||||
return new RepositoryCollectionLinks(uriInfoStore.get());
|
return new RepositoryCollectionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class RepositoryCollectionLinks {
|
static class RepositoryCollectionLinks {
|
||||||
private final LinkBuilder collectionLinkBuilder;
|
private final LinkBuilder collectionLinkBuilder;
|
||||||
|
|
||||||
RepositoryCollectionLinks(UriInfo uriInfo) {
|
RepositoryCollectionLinks(ScmPathInfo pathInfo) {
|
||||||
collectionLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryCollectionResource.class);
|
collectionLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryCollectionResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self() {
|
String self() {
|
||||||
@@ -214,14 +208,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public RepositoryTypeLinks repositoryType() {
|
public RepositoryTypeLinks repositoryType() {
|
||||||
return new RepositoryTypeLinks(uriInfoStore.get());
|
return new RepositoryTypeLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class RepositoryTypeLinks {
|
static class RepositoryTypeLinks {
|
||||||
private final LinkBuilder repositoryTypeLinkBuilder;
|
private final LinkBuilder repositoryTypeLinkBuilder;
|
||||||
|
|
||||||
RepositoryTypeLinks(UriInfo uriInfo) {
|
RepositoryTypeLinks(ScmPathInfo pathInfo) {
|
||||||
repositoryTypeLinkBuilder = new LinkBuilder(uriInfo, RepositoryTypeRootResource.class, RepositoryTypeResource.class);
|
repositoryTypeLinkBuilder = new LinkBuilder(pathInfo, RepositoryTypeRootResource.class, RepositoryTypeResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String name) {
|
String self(String name) {
|
||||||
@@ -230,14 +224,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public RepositoryTypeCollectionLinks repositoryTypeCollection() {
|
public RepositoryTypeCollectionLinks repositoryTypeCollection() {
|
||||||
return new RepositoryTypeCollectionLinks(uriInfoStore.get());
|
return new RepositoryTypeCollectionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class RepositoryTypeCollectionLinks {
|
static class RepositoryTypeCollectionLinks {
|
||||||
private final LinkBuilder collectionLinkBuilder;
|
private final LinkBuilder collectionLinkBuilder;
|
||||||
|
|
||||||
RepositoryTypeCollectionLinks(UriInfo uriInfo) {
|
RepositoryTypeCollectionLinks(ScmPathInfo pathInfo) {
|
||||||
collectionLinkBuilder = new LinkBuilder(uriInfo, RepositoryTypeRootResource.class, RepositoryTypeCollectionResource.class);
|
collectionLinkBuilder = new LinkBuilder(pathInfo, RepositoryTypeRootResource.class, RepositoryTypeCollectionResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self() {
|
String self() {
|
||||||
@@ -247,14 +241,14 @@ class ResourceLinks {
|
|||||||
|
|
||||||
|
|
||||||
public TagCollectionLinks tag() {
|
public TagCollectionLinks tag() {
|
||||||
return new TagCollectionLinks(uriInfoStore.get());
|
return new TagCollectionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class TagCollectionLinks {
|
static class TagCollectionLinks {
|
||||||
private final LinkBuilder tagLinkBuilder;
|
private final LinkBuilder tagLinkBuilder;
|
||||||
|
|
||||||
TagCollectionLinks(UriInfo uriInfo) {
|
TagCollectionLinks(ScmPathInfo pathInfo) {
|
||||||
tagLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, TagRootResource.class);
|
tagLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, TagRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name, String tagName) {
|
String self(String namespace, String name, String tagName) {
|
||||||
@@ -267,14 +261,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DiffLinks diff() {
|
public DiffLinks diff() {
|
||||||
return new DiffLinks(uriInfoStore.get());
|
return new DiffLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DiffLinks {
|
static class DiffLinks {
|
||||||
private final LinkBuilder diffLinkBuilder;
|
private final LinkBuilder diffLinkBuilder;
|
||||||
|
|
||||||
DiffLinks(UriInfo uriInfo) {
|
DiffLinks(ScmPathInfo pathInfo) {
|
||||||
diffLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, DiffRootResource.class);
|
diffLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, DiffRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name, String id) {
|
String self(String namespace, String name, String id) {
|
||||||
@@ -287,14 +281,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BranchLinks branch() {
|
public BranchLinks branch() {
|
||||||
return new BranchLinks(uriInfoStore.get());
|
return new BranchLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class BranchLinks {
|
static class BranchLinks {
|
||||||
private final LinkBuilder branchLinkBuilder;
|
private final LinkBuilder branchLinkBuilder;
|
||||||
|
|
||||||
BranchLinks(UriInfo uriInfo) {
|
BranchLinks(ScmPathInfo pathInfo) {
|
||||||
branchLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, BranchRootResource.class);
|
branchLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, BranchRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(NamespaceAndName namespaceAndName, String branch) {
|
String self(NamespaceAndName namespaceAndName, String branch) {
|
||||||
@@ -307,14 +301,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BranchCollectionLinks branchCollection() {
|
public BranchCollectionLinks branchCollection() {
|
||||||
return new BranchCollectionLinks(uriInfoStore.get());
|
return new BranchCollectionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class BranchCollectionLinks {
|
static class BranchCollectionLinks {
|
||||||
private final LinkBuilder branchLinkBuilder;
|
private final LinkBuilder branchLinkBuilder;
|
||||||
|
|
||||||
BranchCollectionLinks(UriInfo uriInfo) {
|
BranchCollectionLinks(ScmPathInfo pathInfo) {
|
||||||
branchLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, BranchRootResource.class);
|
branchLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, BranchRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name) {
|
String self(String namespace, String name) {
|
||||||
@@ -323,14 +317,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ChangesetLinks changeset() {
|
public ChangesetLinks changeset() {
|
||||||
return new ChangesetLinks(uriInfoStore.get());
|
return new ChangesetLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ChangesetLinks {
|
static class ChangesetLinks {
|
||||||
private final LinkBuilder changesetLinkBuilder;
|
private final LinkBuilder changesetLinkBuilder;
|
||||||
|
|
||||||
ChangesetLinks(UriInfo uriInfo) {
|
ChangesetLinks(ScmPathInfo pathInfo) {
|
||||||
changesetLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, ChangesetRootResource.class);
|
changesetLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, ChangesetRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name, String changesetId) {
|
String self(String namespace, String name, String changesetId) {
|
||||||
@@ -347,14 +341,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ModificationsLinks modifications() {
|
public ModificationsLinks modifications() {
|
||||||
return new ModificationsLinks(uriInfoStore.get());
|
return new ModificationsLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ModificationsLinks {
|
static class ModificationsLinks {
|
||||||
private final LinkBuilder modificationsLinkBuilder;
|
private final LinkBuilder modificationsLinkBuilder;
|
||||||
|
|
||||||
ModificationsLinks(UriInfo uriInfo) {
|
ModificationsLinks(ScmPathInfo pathInfo) {
|
||||||
modificationsLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, ModificationsRootResource.class);
|
modificationsLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, ModificationsRootResource.class);
|
||||||
}
|
}
|
||||||
String self(String namespace, String name, String revision) {
|
String self(String namespace, String name, String revision) {
|
||||||
return modificationsLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("modifications").parameters().method("get").parameters(revision).href();
|
return modificationsLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("modifications").parameters().method("get").parameters(revision).href();
|
||||||
@@ -362,14 +356,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FileHistoryLinks fileHistory() {
|
public FileHistoryLinks fileHistory() {
|
||||||
return new FileHistoryLinks(uriInfoStore.get());
|
return new FileHistoryLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class FileHistoryLinks {
|
static class FileHistoryLinks {
|
||||||
private final LinkBuilder fileHistoryLinkBuilder;
|
private final LinkBuilder fileHistoryLinkBuilder;
|
||||||
|
|
||||||
FileHistoryLinks(UriInfo uriInfo) {
|
FileHistoryLinks(ScmPathInfo pathInfo) {
|
||||||
fileHistoryLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, FileHistoryRootResource.class);
|
fileHistoryLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, FileHistoryRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name, String changesetId, String path) {
|
String self(String namespace, String name, String changesetId, String path) {
|
||||||
@@ -379,14 +373,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SourceLinks source() {
|
public SourceLinks source() {
|
||||||
return new SourceLinks(uriInfoStore.get());
|
return new SourceLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class SourceLinks {
|
static class SourceLinks {
|
||||||
private final LinkBuilder sourceLinkBuilder;
|
private final LinkBuilder sourceLinkBuilder;
|
||||||
|
|
||||||
SourceLinks(UriInfo uriInfo) {
|
SourceLinks(ScmPathInfo pathInfo) {
|
||||||
sourceLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, SourceRootResource.class);
|
sourceLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, SourceRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name, String revision) {
|
String self(String namespace, String name, String revision) {
|
||||||
@@ -412,14 +406,14 @@ class ResourceLinks {
|
|||||||
|
|
||||||
}
|
}
|
||||||
public PermissionLinks permission() {
|
public PermissionLinks permission() {
|
||||||
return new PermissionLinks(uriInfoStore.get());
|
return new PermissionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class PermissionLinks {
|
static class PermissionLinks {
|
||||||
private final LinkBuilder permissionLinkBuilder;
|
private final LinkBuilder permissionLinkBuilder;
|
||||||
|
|
||||||
PermissionLinks(UriInfo uriInfo) {
|
PermissionLinks(ScmPathInfo pathInfo) {
|
||||||
permissionLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, PermissionRootResource.class);
|
permissionLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, PermissionRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String all(String namespace, String name) {
|
String all(String namespace, String name) {
|
||||||
@@ -449,14 +443,14 @@ class ResourceLinks {
|
|||||||
|
|
||||||
|
|
||||||
public UIPluginLinks uiPlugin() {
|
public UIPluginLinks uiPlugin() {
|
||||||
return new UIPluginLinks(uriInfoStore.get());
|
return new UIPluginLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UIPluginLinks {
|
static class UIPluginLinks {
|
||||||
private final LinkBuilder uiPluginLinkBuilder;
|
private final LinkBuilder uiPluginLinkBuilder;
|
||||||
|
|
||||||
UIPluginLinks(UriInfo uriInfo) {
|
UIPluginLinks(ScmPathInfo pathInfo) {
|
||||||
uiPluginLinkBuilder = new LinkBuilder(uriInfo, UIRootResource.class, UIPluginResource.class);
|
uiPluginLinkBuilder = new LinkBuilder(pathInfo, UIRootResource.class, UIPluginResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String id) {
|
String self(String id) {
|
||||||
@@ -465,14 +459,14 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public UIPluginCollectionLinks uiPluginCollection() {
|
public UIPluginCollectionLinks uiPluginCollection() {
|
||||||
return new UIPluginCollectionLinks(uriInfoStore.get());
|
return new UIPluginCollectionLinks(scmPathInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UIPluginCollectionLinks {
|
static class UIPluginCollectionLinks {
|
||||||
private final LinkBuilder uiPluginCollectionLinkBuilder;
|
private final LinkBuilder uiPluginCollectionLinkBuilder;
|
||||||
|
|
||||||
UIPluginCollectionLinks(UriInfo uriInfo) {
|
UIPluginCollectionLinks(ScmPathInfo pathInfo) {
|
||||||
uiPluginCollectionLinkBuilder = new LinkBuilder(uriInfo, UIRootResource.class, UIPluginResource.class);
|
uiPluginCollectionLinkBuilder = new LinkBuilder(pathInfo, UIRootResource.class, UIPluginResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self() {
|
String self() {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AfterMapping
|
@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()));
|
Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(target.getName()));
|
||||||
if (UserPermissions.delete(user).isPermitted()) {
|
if (UserPermissions.delete(user).isPermitted()) {
|
||||||
linksBuilder.single(link("delete", resourceLinks.user().delete(target.getName())));
|
linksBuilder.single(link("delete", resourceLinks.user().delete(target.getName())));
|
||||||
|
|||||||
@@ -37,10 +37,8 @@ package sonia.scm.filter;
|
|||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import org.apache.shiro.SecurityUtils;
|
import org.apache.shiro.SecurityUtils;
|
||||||
import org.apache.shiro.subject.Subject;
|
import org.apache.shiro.subject.Subject;
|
||||||
|
|
||||||
import sonia.scm.Priority;
|
import sonia.scm.Priority;
|
||||||
import sonia.scm.SCMContext;
|
import sonia.scm.SCMContext;
|
||||||
import sonia.scm.config.ScmConfiguration;
|
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.HttpFilter;
|
||||||
import sonia.scm.web.filter.SecurityHttpServletRequestWrapper;
|
import sonia.scm.web.filter.SecurityHttpServletRequestWrapper;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
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)
|
@Priority(Filters.PRIORITY_AUTHORIZATION)
|
||||||
// TODO find a better way for unprotected resources
|
// 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
|
public class SecurityFilter extends HttpFilter
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,7 @@
|
|||||||
|
|
||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.github.sdorra.ssp.PermissionActionCheck;
|
import com.github.sdorra.ssp.PermissionActionCheck;
|
||||||
import com.google.common.base.Strings;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -43,7 +40,6 @@ import org.apache.shiro.concurrent.SubjectAwareExecutorService;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.AlreadyExistsException;
|
import sonia.scm.AlreadyExistsException;
|
||||||
import sonia.scm.ArgumentIsInvalidException;
|
|
||||||
import sonia.scm.ConfigurationException;
|
import sonia.scm.ConfigurationException;
|
||||||
import sonia.scm.HandlerEventType;
|
import sonia.scm.HandlerEventType;
|
||||||
import sonia.scm.ManagerDaoAdapter;
|
import sonia.scm.ManagerDaoAdapter;
|
||||||
@@ -54,11 +50,9 @@ import sonia.scm.config.ScmConfiguration;
|
|||||||
import sonia.scm.security.KeyGenerator;
|
import sonia.scm.security.KeyGenerator;
|
||||||
import sonia.scm.util.AssertUtil;
|
import sonia.scm.util.AssertUtil;
|
||||||
import sonia.scm.util.CollectionAppender;
|
import sonia.scm.util.CollectionAppender;
|
||||||
import sonia.scm.util.HttpUtil;
|
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -71,8 +65,6 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of {@link RepositoryManager}.
|
* Default implementation of {@link RepositoryManager}.
|
||||||
*
|
*
|
||||||
@@ -90,7 +82,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
|||||||
private final KeyGenerator keyGenerator;
|
private final KeyGenerator keyGenerator;
|
||||||
private final RepositoryDAO repositoryDAO;
|
private final RepositoryDAO repositoryDAO;
|
||||||
private final Set<Type> types;
|
private final Set<Type> types;
|
||||||
private RepositoryMatcher repositoryMatcher;
|
|
||||||
private NamespaceStrategy namespaceStrategy;
|
private NamespaceStrategy namespaceStrategy;
|
||||||
private final ManagerDaoAdapter<Repository> managerDaoAdapter;
|
private final ManagerDaoAdapter<Repository> managerDaoAdapter;
|
||||||
|
|
||||||
@@ -99,12 +90,10 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
|||||||
public DefaultRepositoryManager(ScmConfiguration configuration,
|
public DefaultRepositoryManager(ScmConfiguration configuration,
|
||||||
SCMContextProvider contextProvider, KeyGenerator keyGenerator,
|
SCMContextProvider contextProvider, KeyGenerator keyGenerator,
|
||||||
RepositoryDAO repositoryDAO, Set<RepositoryHandler> handlerSet,
|
RepositoryDAO repositoryDAO, Set<RepositoryHandler> handlerSet,
|
||||||
RepositoryMatcher repositoryMatcher,
|
|
||||||
NamespaceStrategy namespaceStrategy) {
|
NamespaceStrategy namespaceStrategy) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.keyGenerator = keyGenerator;
|
this.keyGenerator = keyGenerator;
|
||||||
this.repositoryDAO = repositoryDAO;
|
this.repositoryDAO = repositoryDAO;
|
||||||
this.repositoryMatcher = repositoryMatcher;
|
|
||||||
this.namespaceStrategy = namespaceStrategy;
|
this.namespaceStrategy = namespaceStrategy;
|
||||||
|
|
||||||
ThreadFactory factory = new ThreadFactoryBuilder()
|
ThreadFactory factory = new ThreadFactoryBuilder()
|
||||||
@@ -317,71 +306,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
|||||||
return validTypes;
|
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<Repository> repositories = repositoryDAO.getAll();
|
|
||||||
|
|
||||||
PermissionActionCheck<Repository> 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
|
@Override
|
||||||
public RepositoryHandler getHandler(String type) {
|
public RepositoryHandler getHandler(String type) {
|
||||||
return handlerMap.get(type);
|
return handlerMap.get(type);
|
||||||
|
|||||||
@@ -33,86 +33,32 @@
|
|||||||
|
|
||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.servlet.RequestScoped;
|
import com.google.inject.servlet.RequestScoped;
|
||||||
|
|
||||||
import sonia.scm.security.ScmSecurityException;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
public class DefaultRepositoryProvider implements RepositoryProvider
|
public class DefaultRepositoryProvider implements RepositoryProvider {
|
||||||
{
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
public static final String ATTRIBUTE_NAME = "scm.request.repository";
|
public static final String ATTRIBUTE_NAME = "scm.request.repository";
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
private final Provider<HttpServletRequest> requestProvider;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param requestProvider
|
|
||||||
* @param manager
|
|
||||||
*/
|
|
||||||
@Inject
|
@Inject
|
||||||
public DefaultRepositoryProvider(
|
public DefaultRepositoryProvider(Provider<HttpServletRequest> requestProvider) {
|
||||||
Provider<HttpServletRequest> requestProvider,
|
|
||||||
RepositoryManager manager)
|
|
||||||
{
|
|
||||||
this.requestProvider = requestProvider;
|
this.requestProvider = requestProvider;
|
||||||
this.manager = manager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*
|
|
||||||
* @throws ScmSecurityException
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Repository get() throws ScmSecurityException
|
public Repository get() {
|
||||||
{
|
|
||||||
Repository repository = null;
|
|
||||||
HttpServletRequest request = requestProvider.get();
|
HttpServletRequest request = requestProvider.get();
|
||||||
|
|
||||||
if (request != null)
|
if (request != null) {
|
||||||
{
|
return (Repository) request.getAttribute(ATTRIBUTE_NAME);
|
||||||
repository = (Repository) request.getAttribute(ATTRIBUTE_NAME);
|
|
||||||
|
|
||||||
if (repository == null)
|
|
||||||
{
|
|
||||||
repository = manager.getFromRequest(request);
|
|
||||||
|
|
||||||
if (repository != null)
|
|
||||||
{
|
|
||||||
request.setAttribute(ATTRIBUTE_NAME, repository);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return repository;
|
throw new IllegalStateException("request not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private final RepositoryManager manager;
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private final Provider<HttpServletRequest> requestProvider;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ public final class HealthChecker {
|
|||||||
Repository repository = repositoryManager.get(id);
|
Repository repository = repositoryManager.get(id);
|
||||||
|
|
||||||
if (repository == null) {
|
if (repository == null) {
|
||||||
throw new RepositoryNotFoundException(
|
throw new RepositoryNotFoundException(id);
|
||||||
"could not find repository with id ".concat(id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doCheck(repository);
|
doCheck(repository);
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package sonia.scm.security;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by masuewer on 04.07.18.
|
* Created by masuewer on 04.07.18.
|
||||||
*/
|
*/
|
||||||
public final class SecurityRequests {
|
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() {}
|
private SecurityRequests() {}
|
||||||
|
|
||||||
|
|||||||
@@ -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<HttpServletRequest> requestProvider;
|
||||||
|
|
||||||
|
private final PushStateDispatcher dispatcher;
|
||||||
|
private final UserAgentParser userAgentParser;
|
||||||
|
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public HttpProtocolServlet(RepositoryServiceFactory serviceFactory, Provider<HttpServletRequest> 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> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<NamespaceAndName> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.google.inject.util.Providers;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.shiro.subject.Subject;
|
import org.apache.shiro.subject.Subject;
|
||||||
import org.apache.shiro.subject.support.SubjectThreadState;
|
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||||
@@ -92,7 +93,7 @@ public class BranchRootResourceTest extends RepositoryTestBase {
|
|||||||
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
|
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
|
||||||
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
|
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
|
||||||
branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper, changesetCollectionToDtoMapper);
|
branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper, changesetCollectionToDtoMapper);
|
||||||
super.branchRootResource = MockProvider.of(branchRootResource);
|
super.branchRootResource = Providers.of(branchRootResource);
|
||||||
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
|
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
|
||||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
|
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
|
||||||
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
|
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user