From be5c430bd22fa9b102aa86711c0df3eaa0342037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 6 Sep 2018 10:58:09 +0200 Subject: [PATCH] Add POC protocol servlet with delegate to git --- .../scm/repository/RepositoryProvider.java | 28 ++---- .../spi/GitRepositoryServiceProvider.java | 17 ++-- .../spi/GitRepositoryServiceResolver.java | 18 ++-- .../java/sonia/scm/web/GitServletModule.java | 3 - .../java/sonia/scm/ScmContextListener.java | 2 +- .../main/java/sonia/scm/ScmServletModule.java | 56 ++++++++---- .../java/sonia/scm/WebResourceServlet.java | 2 +- .../repository/DefaultRepositoryManager.java | 48 +---------- .../repository/DefaultRepositoryProvider.java | 70 +++------------ .../scm/web/protocol/HttpProtocolServlet.java | 86 +++++++++++++++++++ 10 files changed, 163 insertions(+), 167 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java index 1a5300ad21..cea8574d14 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryProvider.java @@ -6,13 +6,13 @@ * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -26,35 +26,21 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * http://bitbucket.org/sdorra/scm-manager - * */ - package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.throwingproviders.CheckedProvider; -import sonia.scm.security.ScmSecurityException; - /** * * @author Sebastian Sdorra * @since 1.10 */ -public interface RepositoryProvider extends CheckedProvider -{ - - /** - * Method description - * - * - * @return - * - * @throws ScmSecurityException - */ +public interface RepositoryProvider extends CheckedProvider { @Override - public Repository get() throws ScmSecurityException; + Repository get(); } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java index 56ceb59de0..45a0ad5347 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java @@ -42,6 +42,7 @@ import sonia.scm.repository.api.Command; import sonia.scm.repository.api.ScmProtocol; import sonia.scm.web.ScmGitServlet; +import javax.inject.Provider; import java.io.IOException; import java.util.Collections; import java.util.Set; @@ -74,19 +75,13 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider //~--- constructors --------------------------------------------------------- - /** - * Constructs ... - * - * - * @param handler - * @param repository - */ public GitRepositoryServiceProvider(GitRepositoryHandler handler, - Repository repository) + Repository repository, Provider servletProvider) { this.handler = handler; this.repository = repository; - context = new GitContext(handler.getDirectory(repository)); + this.servletProvider = servletProvider; + this.context = new GitContext(handler.getDirectory(repository)); } //~--- methods -------------------------------------------------------------- @@ -251,7 +246,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider @Override public Set getSupportedProtocols() { - return Collections.singleton(new ScmGitServlet(null, null, null, null, null, null)); + return Collections.singleton(servletProvider.get()); } //~--- fields --------------------------------------------------------------- @@ -264,4 +259,6 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider /** Field description */ private Repository repository; + + private final Provider servletProvider; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceResolver.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceResolver.java index 52c5171627..2cecb964fe 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceResolver.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceResolver.java @@ -35,10 +35,12 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.Inject; - import sonia.scm.plugin.Extension; import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.Repository; +import sonia.scm.web.ScmGitServlet; + +import javax.inject.Provider; /** * @@ -53,16 +55,11 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver //~--- constructors --------------------------------------------------------- - /** - * Constructs ... - * - * - * @param handler - */ @Inject - public GitRepositoryServiceResolver(GitRepositoryHandler handler) + public GitRepositoryServiceResolver(GitRepositoryHandler handler, Provider servletProvider) { this.handler = handler; + this.servletProvider = servletProvider; } //~--- methods -------------------------------------------------------------- @@ -82,7 +79,7 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver if (TYPE.equalsIgnoreCase(repository.getType())) { - provider = new GitRepositoryServiceProvider(handler, repository); + provider = new GitRepositoryServiceProvider(handler, repository, servletProvider); } return provider; @@ -91,5 +88,6 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver //~--- fields --------------------------------------------------------------- /** Field description */ - private GitRepositoryHandler handler; + private final GitRepositoryHandler handler; + private final Provider servletProvider; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java index bdad103c15..94e772123e 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java @@ -75,8 +75,5 @@ public class GitServletModule extends ServletModule bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass()); bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass()); - - // serlvelts and filters - serve(PATTERN_GIT).with(ScmGitServlet.class); } } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java index 1eaef333a1..cb272de38f 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java @@ -135,7 +135,7 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList moduleList.add(new EagerSingletonModule()); moduleList.add(ShiroWebModule.guiceFilterModule()); moduleList.add(new WebElementModule(pluginLoader)); - moduleList.add(new ScmServletModule(context, pluginLoader, overrides, pluginLoader.getExtensionProcessor())); + moduleList.add(new ScmServletModule(context, pluginLoader, overrides)); moduleList.add( new ScmSecurityModule(context, pluginLoader.getExtensionProcessor()) ); diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 1d627de8ed..2ea17e9da3 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -56,17 +56,48 @@ import sonia.scm.group.xml.XmlGroupDAO; import sonia.scm.io.DefaultFileSystem; import sonia.scm.io.FileSystem; import sonia.scm.net.SSLContextProvider; -import sonia.scm.net.ahc.*; -import sonia.scm.plugin.*; -import sonia.scm.repository.*; +import sonia.scm.net.ahc.AdvancedHttpClient; +import sonia.scm.net.ahc.ContentTransformer; +import sonia.scm.net.ahc.DefaultAdvancedHttpClient; +import sonia.scm.net.ahc.JsonContentTransformer; +import sonia.scm.net.ahc.XmlContentTransformer; +import sonia.scm.plugin.DefaultPluginLoader; +import sonia.scm.plugin.DefaultPluginManager; +import sonia.scm.plugin.PluginLoader; +import sonia.scm.plugin.PluginManager; +import sonia.scm.repository.DefaultRepositoryManager; +import sonia.scm.repository.DefaultRepositoryProvider; +import sonia.scm.repository.HealthCheckContextListener; +import sonia.scm.repository.NamespaceStrategy; +import sonia.scm.repository.NamespaceStrategyProvider; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryDAO; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.repository.RepositoryManagerProvider; +import sonia.scm.repository.RepositoryProvider; import sonia.scm.repository.api.HookContextFactory; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.repository.xml.XmlRepositoryDAO; import sonia.scm.schedule.QuartzScheduler; import sonia.scm.schedule.Scheduler; -import sonia.scm.security.*; -import sonia.scm.store.*; +import sonia.scm.security.AuthorizationChangedEventProducer; +import sonia.scm.security.CipherHandler; +import sonia.scm.security.CipherUtil; +import sonia.scm.security.ConfigurableLoginAttemptHandler; +import sonia.scm.security.DefaultKeyGenerator; +import sonia.scm.security.DefaultSecuritySystem; +import sonia.scm.security.KeyGenerator; +import sonia.scm.security.LoginAttemptHandler; +import sonia.scm.security.SecuritySystem; +import sonia.scm.store.BlobStoreFactory; +import sonia.scm.store.ConfigurationEntryStoreFactory; +import sonia.scm.store.ConfigurationStoreFactory; +import sonia.scm.store.DataStoreFactory; +import sonia.scm.store.FileBlobStoreFactory; +import sonia.scm.store.JAXBConfigurationEntryStoreFactory; +import sonia.scm.store.JAXBConfigurationStoreFactory; +import sonia.scm.store.JAXBDataStoreFactory; import sonia.scm.template.MustacheTemplateEngine; import sonia.scm.template.TemplateEngine; import sonia.scm.template.TemplateEngineFactory; @@ -81,6 +112,7 @@ import sonia.scm.util.ScmConfigurationUtil; import sonia.scm.web.UserAgentParser; import sonia.scm.web.cgi.CGIExecutorFactory; import sonia.scm.web.cgi.DefaultCGIExecutorFactory; +import sonia.scm.web.filter.AuthenticationFilter; import sonia.scm.web.filter.LoggingFilter; import sonia.scm.web.security.AdministrationContext; import sonia.scm.web.security.DefaultAdministrationContext; @@ -162,15 +194,12 @@ public class ScmServletModule extends ServletModule * @param servletContext * @param pluginLoader * @param overrides - * @param extensionProcessor */ - ScmServletModule(ServletContext servletContext, - DefaultPluginLoader pluginLoader, ClassOverrides overrides, ExtensionProcessor extensionProcessor) + ScmServletModule(ServletContext servletContext, DefaultPluginLoader pluginLoader, ClassOverrides overrides) { this.servletContext = servletContext; this.pluginLoader = pluginLoader; this.overrides = overrides; - this.extensionProcessor = extensionProcessor; } //~--- methods -------------------------------------------------------------- @@ -293,6 +322,8 @@ public class ScmServletModule extends ServletModule bind(TemplateEngineFactory.class); bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class); + filter("/repo/*").through(AuthenticationFilter.class); + // bind events // bind(LastModifiedUpdateListener.class); @@ -389,11 +420,6 @@ public class ScmServletModule extends ServletModule /** * Load ScmConfiguration with JAXB - * - * - * @param context - * - * @return */ private ScmConfiguration getScmConfiguration() { @@ -414,6 +440,4 @@ public class ScmServletModule extends ServletModule /** Field description */ private final ServletContext servletContext; - - private final ExtensionProcessor extensionProcessor; } diff --git a/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java b/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java index 764e4f18d2..ce3e3fa4e1 100644 --- a/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/WebResourceServlet.java @@ -33,7 +33,7 @@ public class WebResourceServlet extends HttpServlet { * TODO remove old protocol servlets and hook. Move /hook/hg to api? */ @VisibleForTesting - static final String PATTERN = "/(?!api/|git/|hg/|svn/|hook/).*"; + static final String PATTERN = "/(?!api/|git/|hg/|svn/|hook/|repo/).*"; private static final Logger LOG = LoggerFactory.getLogger(WebResourceServlet.class); diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java index 02ae67719b..06f92f7bec 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java @@ -34,7 +34,6 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import com.github.sdorra.ssp.PermissionActionCheck; -import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.inject.Inject; @@ -43,7 +42,6 @@ import org.apache.shiro.concurrent.SubjectAwareExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.AlreadyExistsException; -import sonia.scm.ArgumentIsInvalidException; import sonia.scm.ConfigurationException; import sonia.scm.HandlerEventType; import sonia.scm.ManagerDaoAdapter; @@ -332,52 +330,12 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { uri = uri.substring(1); } - int typeSeparator = uri.indexOf(HttpUtil.SEPARATOR_PATH); Repository repository = null; - if (typeSeparator > 0) { - String type = uri.substring(0, typeSeparator); + String namespace = uri.substring(0, uri.indexOf(HttpUtil.SEPARATOR_PATH)); + String name = uri.substring(uri.indexOf(HttpUtil.SEPARATOR_PATH) + 1, uri.indexOf(HttpUtil.SEPARATOR_PATH, uri.indexOf(HttpUtil.SEPARATOR_PATH) + 1)); - uri = uri.substring(typeSeparator + 1); - repository = getFromTypeAndUri(type, uri); - } - - return repository; - } - - private Repository getFromTypeAndUri(String type, String uri) { - if (Strings.isNullOrEmpty(type)) { - throw new ArgumentIsInvalidException("argument type is required"); - } - - if (Strings.isNullOrEmpty(uri)) { - throw new ArgumentIsInvalidException("argument uri is required"); - } - - // remove ;jsessionid, jetty bug? - uri = HttpUtil.removeMatrixParameter(uri); - - Repository repository = null; - - if (handlerMap.containsKey(type)) { - Collection repositories = repositoryDAO.getAll(); - - PermissionActionCheck check = RepositoryPermissions.read(); - - for (Repository r : repositories) { - if (repositoryMatcher.matches(r, type, uri)) { - check.check(r); - repository = r.clone(); - - break; - } - } - } - - if ((repository == null) && logger.isDebugEnabled()) { - logger.debug("could not find repository with type {} and uri {}", type, - uri); - } + repository = get(new NamespaceAndName(namespace, name)); return repository; } diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java index c3341bbd22..3f061ff46d 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryProvider.java @@ -38,81 +38,31 @@ package sonia.scm.repository; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.servlet.RequestScoped; - import sonia.scm.security.ScmSecurityException; -//~--- JDK imports ------------------------------------------------------------ - import javax.servlet.http.HttpServletRequest; -/** - * - * @author Sebastian Sdorra - */ -@RequestScoped -public class DefaultRepositoryProvider implements RepositoryProvider -{ +//~--- JDK imports ------------------------------------------------------------ + +@RequestScoped +public class DefaultRepositoryProvider implements RepositoryProvider { - /** Field description */ public static final String ATTRIBUTE_NAME = "scm.request.repository"; - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param requestProvider - * @param manager - */ + private final Provider requestProvider; @Inject - public DefaultRepositoryProvider( - Provider requestProvider, - RepositoryManager manager) - { + public DefaultRepositoryProvider(Provider requestProvider) { this.requestProvider = requestProvider; - this.manager = manager; } - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - * - * @throws ScmSecurityException - */ @Override - public Repository get() throws ScmSecurityException - { - Repository repository = null; + public Repository get() throws ScmSecurityException { HttpServletRequest request = requestProvider.get(); - if (request != null) - { - repository = (Repository) request.getAttribute(ATTRIBUTE_NAME); - - if (repository == null) - { - repository = manager.getFromRequest(request); - - if (repository != null) - { - request.setAttribute(ATTRIBUTE_NAME, repository); - } - } + if (request != null) { + return (Repository) request.getAttribute(ATTRIBUTE_NAME); } - return repository; + throw new IllegalStateException("request not found"); } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final RepositoryManager manager; - - /** Field description */ - private final Provider requestProvider; } diff --git a/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java b/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java new file mode 100644 index 0000000000..7991cbb596 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java @@ -0,0 +1,86 @@ +package sonia.scm.web.protocol; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +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.RepositoryProvider; +import sonia.scm.repository.api.RepositoryService; +import sonia.scm.repository.api.RepositoryServiceFactory; +import sonia.scm.repository.spi.HttpScmProtocol; +import sonia.scm.util.HttpUtil; +import sonia.scm.web.UserAgent; +import sonia.scm.web.UserAgentParser; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Singleton +@WebElement(value = HttpProtocolServlet.PATTERN) +@Slf4j +public class HttpProtocolServlet extends HttpServlet { + + public static final String PATTERN = "/repo/*"; + + private final RepositoryProvider repositoryProvider; + private final RepositoryServiceFactory serviceFactory; + + private final Provider requestProvider; + + private final PushStateDispatcher dispatcher; + private final UserAgentParser userAgentParser; + + @Inject + public HttpProtocolServlet(RepositoryProvider repositoryProvider, RepositoryServiceFactory serviceFactory, Provider requestProvider, PushStateDispatcher dispatcher, UserAgentParser userAgentParser) { + this.repositoryProvider = repositoryProvider; + this.serviceFactory = serviceFactory; + this.requestProvider = requestProvider; + this.dispatcher = dispatcher; + this.userAgentParser = userAgentParser; + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + Subject subject = SecurityUtils.getSubject(); + + + UserAgent userAgent = userAgentParser.parse(req); + if (userAgent.isBrowser()) { + log.trace("dispatch browser request for user agent {}", userAgent); + dispatcher.dispatch(req, resp, req.getRequestURI()); + } else { + + + String pathInfo = req.getPathInfo(); + NamespaceAndName namespaceAndName = fromUri(pathInfo); + try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { + requestProvider.get().setAttribute(DefaultRepositoryProvider.ATTRIBUTE_NAME, repositoryService.getRepository()); + HttpScmProtocol protocol = repositoryService.getProtocol(HttpScmProtocol.class); + protocol.serve(req, resp); + } catch (RepositoryNotFoundException e) { + resp.setStatus(404); + } + } + } + + private NamespaceAndName fromUri(String uri) { + if (uri.startsWith(HttpUtil.SEPARATOR_PATH)) { + uri = uri.substring(1); + } + + String namespace = uri.substring(0, uri.indexOf(HttpUtil.SEPARATOR_PATH)); + String name = uri.substring(uri.indexOf(HttpUtil.SEPARATOR_PATH) + 1, uri.indexOf(HttpUtil.SEPARATOR_PATH, uri.indexOf(HttpUtil.SEPARATOR_PATH) + 1)); + + return new NamespaceAndName(namespace, name); + } +}