This commit is contained in:
Mohamed Karray
2018-09-19 16:29:50 +02:00
131 changed files with 1862 additions and 1819 deletions

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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("../..");
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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.
* *

View File

@@ -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 ...).
* *

View File

@@ -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}
* *

View File

@@ -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());
} }
} }

View File

@@ -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();
} }

View File

@@ -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())));
}
} }

View File

@@ -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;
} }

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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;
/** /**

View File

@@ -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.
* *

View File

@@ -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;
}

View File

@@ -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/"));
}
}

View File

@@ -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 {}
}

View File

@@ -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) {
}
};
}
}

View File

@@ -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/"));
}
}

View File

@@ -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);
}
}

View File

@@ -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"

View File

@@ -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);
} }

View File

@@ -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();
} }
} }

View File

@@ -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 --------------------------------------------------------------

View File

@@ -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;
} }

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);
} }
} }

View File

@@ -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;
}
}

View File

@@ -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);
} }
} }

View File

@@ -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;
} }

View File

@@ -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();
}
}

View File

@@ -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());
} }
} }

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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();
} }

View File

@@ -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();
} }
} }

View File

@@ -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)
{ {

View File

@@ -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;
} }

View File

@@ -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);
}
}
}

View File

@@ -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;
} }

View File

@@ -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();
}
}

View File

@@ -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());
} }

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
} }
} }

View File

@@ -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

View File

@@ -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);
} }

View File

@@ -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());
} }

View File

@@ -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");
} }

View File

@@ -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

View File

@@ -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);

View File

@@ -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();
} }
} }

View File

@@ -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)
{ {

View File

@@ -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;
} }

View File

@@ -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);
}
}
}

View File

@@ -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 */

View File

@@ -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();
}
}

View File

@@ -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);
}
} }

View File

@@ -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);
}
}

View File

@@ -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());
} }

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
} }
} }

View File

@@ -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

View File

@@ -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);

View File

@@ -49,7 +49,7 @@
<artifactId>scm-core</artifactId> <artifactId>scm-core</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>2.0.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>sonia.scm</groupId> <groupId>sonia.scm</groupId>
<artifactId>scm-dao-xml</artifactId> <artifactId>scm-dao-xml</artifactId>

View File

@@ -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())
); );

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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);
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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())));

View File

@@ -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));
} }
/** /**

View File

@@ -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

View File

@@ -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();
}
} }

View File

@@ -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() {

View File

@@ -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())));

View File

@@ -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
{ {

View File

@@ -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);

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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() {}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}
}

Some files were not shown because too many files have changed in this diff Show More