From 432beb2a1761dcbf5809931a88519c7ed7055d51 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 22 Jan 2015 21:45:42 +0100 Subject: [PATCH] - fixed #19 (again): vault-specific prefix is now handled by the servlet context instead of jackrabbit. - simplified webdav locator, as workspaces and pathPrefixes are not relevant to jackrabbit any longer --- .../org/cryptomator/webdav/WebDavServer.java | 24 +-- .../jackrabbit/DavLocatorFactoryImpl.java | 161 +++++++++++++++--- .../webdav/jackrabbit/WebDavServlet.java | 4 +- 3 files changed, 148 insertions(+), 41 deletions(-) diff --git a/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java b/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java index 3757359d2..d7371a7f4 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java +++ b/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java @@ -20,6 +20,7 @@ import org.cryptomator.webdav.jackrabbit.WebDavServlet; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.component.LifeCycle; @@ -39,7 +40,7 @@ public final class WebDavServer { private static final WebDavServer INSTANCE = new WebDavServer(); private final Server server; private final ServerConnector localConnector; - private final ServletContextHandler servletContext; + private final ContextHandlerCollection servletCollection; public static WebDavServer getInstance() { return INSTANCE; @@ -51,10 +52,9 @@ public final class WebDavServer { server = new Server(tp); localConnector = new ServerConnector(server); localConnector.setHost(LOCALHOST); - servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS); - servletContext.setContextPath("/"); + servletCollection = new ContextHandlerCollection(); server.setConnectors(new Connector[] {localConnector}); - server.setHandler(servletContext); + server.setHandler(servletCollection); } public synchronized void start() { @@ -87,22 +87,22 @@ public final class WebDavServer { try { final URI uri = new URI(null, null, localConnector.getHost(), localConnector.getLocalPort(), "/" + UUID.randomUUID().toString(), null, null); - final String pathPrefix = uri.getRawPath() + "/"; - final String pathSpec = pathPrefix + "*"; - final ServletHolder servlet = getWebDavServletHolder(workDir.toString(), pathPrefix, checkFileIntegrity, cryptor); - servletContext.addServlet(servlet, pathSpec); + final ServletContextHandler servletContext = new ServletContextHandler(servletCollection, uri.getRawPath(), ServletContextHandler.SESSIONS); + final ServletHolder servlet = getWebDavServletHolder(workDir.toString(), checkFileIntegrity, cryptor); + servletContext.addServlet(servlet, "/*"); - LOG.info("{} available on http:{}", workDir, uri.getRawSchemeSpecificPart()); - return new ServletLifeCycleAdapter(servlet, uri); + servletCollection.mapContexts(); + + LOG.debug("{} available on http:{}", workDir, uri.getRawSchemeSpecificPart()); + return new ServletLifeCycleAdapter(servletContext, uri); } catch (URISyntaxException e) { throw new IllegalStateException("Invalid hard-coded URI components.", e); } } - private ServletHolder getWebDavServletHolder(final String workDir, final String contextPath, final boolean checkFileIntegrity, final Cryptor cryptor) { + private ServletHolder getWebDavServletHolder(final String workDir, final boolean checkFileIntegrity, final Cryptor cryptor) { final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor)); result.setInitParameter(WebDavServlet.CFG_FS_ROOT, workDir); - result.setInitParameter(WebDavServlet.CFG_HTTP_ROOT, contextPath); result.setInitParameter(WebDavServlet.CFG_CHECK_FILE_INTEGRITY, Boolean.toString(checkFileIntegrity)); return result; } diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/DavLocatorFactoryImpl.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/DavLocatorFactoryImpl.java index 24e7bbf25..de0410d25 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/DavLocatorFactoryImpl.java +++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/DavLocatorFactoryImpl.java @@ -15,31 +15,59 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import org.apache.commons.collections4.BidiMap; -import org.apache.jackrabbit.webdav.AbstractLocatorFactory; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.jackrabbit.webdav.DavLocatorFactory; import org.apache.jackrabbit.webdav.DavResourceLocator; +import org.apache.jackrabbit.webdav.util.EncodeUtil; import org.cryptomator.crypto.Cryptor; import org.cryptomator.crypto.CryptorIOSupport; import org.cryptomator.crypto.SensitiveDataSwipeListener; -class DavLocatorFactoryImpl extends AbstractLocatorFactory implements SensitiveDataSwipeListener, CryptorIOSupport { +class DavLocatorFactoryImpl implements DavLocatorFactory, SensitiveDataSwipeListener, CryptorIOSupport { private static final int MAX_CACHED_PATHS = 10000; private final Path fsRoot; private final Cryptor cryptor; private final BidiMap pathCache = new BidiLRUMap<>(MAX_CACHED_PATHS); // - DavLocatorFactoryImpl(String fsRoot, String httpRoot, Cryptor cryptor) { - super(httpRoot); + DavLocatorFactoryImpl(String fsRoot, Cryptor cryptor) { this.fsRoot = FileSystems.getDefault().getPath(fsRoot); this.cryptor = cryptor; cryptor.addSensitiveDataSwipeListener(this); } + /* DavLocatorFactory */ + + @Override + public DavResourceLocator createResourceLocator(String prefix, String href) { + final String fullPrefix = prefix.endsWith("/") ? prefix : prefix + "/"; + final String relativeHref = StringUtils.removeStart(href, fullPrefix); + + final String resourcePath = EncodeUtil.unescape(StringUtils.removeStart(relativeHref, "/")); + return new DavResourceLocatorImpl(fullPrefix, resourcePath); + } + + @Override + public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String path, boolean isResourcePath) { + final String fullPrefix = prefix.endsWith("/") ? prefix : prefix + "/"; + + final String resourcePath = (isResourcePath) ? path : getResourcePath(path); + return new DavResourceLocatorImpl(fullPrefix, resourcePath); + } + + @Override + public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String resourcePath) { + return createResourceLocator(prefix, workspacePath, resourcePath, true); + } + + /* Encryption/Decryption */ + /** * @return Encrypted absolute paths on the file system. */ - @Override - protected String getRepositoryPath(String resourcePath, String wspPath) { + private String getRepositoryPath(String resourcePath) { String encryptedPath = pathCache.get(resourcePath); if (encryptedPath == null) { encryptedPath = encryptRepositoryPath(resourcePath); @@ -59,8 +87,7 @@ class DavLocatorFactoryImpl extends AbstractLocatorFactory implements SensitiveD /** * @return Decrypted path for use in URIs. */ - @Override - protected String getResourcePath(String repositoryPath, String wspPath) { + private String getResourcePath(String repositoryPath) { String decryptedPath = pathCache.getKey(repositoryPath); if (decryptedPath == null) { decryptedPath = decryptResourcePath(repositoryPath); @@ -80,24 +107,7 @@ class DavLocatorFactoryImpl extends AbstractLocatorFactory implements SensitiveD } } - @Override - public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String path, boolean isResourcePath) { - // we don't support workspaces - return super.createResourceLocator(prefix, "", path, isResourcePath); - } - - @Override - public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String resourcePath) { - // we don't support workspaces - return super.createResourceLocator(prefix, "", resourcePath); - } - - @Override - public void swipeSensitiveData() { - pathCache.clear(); - } - - /* Cryptor I/O Support */ + /* CryptorIOSupport */ @Override public void writePathSpecificMetadata(String encryptedPath, byte[] encryptedMetadata) throws IOException { @@ -115,4 +125,103 @@ class DavLocatorFactoryImpl extends AbstractLocatorFactory implements SensitiveD } } + /* SensitiveDataSwipeListener */ + + @Override + public void swipeSensitiveData() { + pathCache.clear(); + } + + /* Locator */ + + private class DavResourceLocatorImpl implements DavResourceLocator { + + private final String prefix; + private final String resourcePath; + + private DavResourceLocatorImpl(String prefix, String resourcePath) { + this.prefix = prefix; + this.resourcePath = resourcePath; + } + + @Override + public String getPrefix() { + return prefix; + } + + @Override + public String getResourcePath() { + return resourcePath; + } + + @Override + public String getWorkspacePath() { + return isRootLocation() ? null : ""; + } + + @Override + public String getWorkspaceName() { + return getPrefix(); + } + + @Override + public boolean isSameWorkspace(DavResourceLocator locator) { + return (locator == null) ? false : isSameWorkspace(locator.getWorkspaceName()); + } + + @Override + public boolean isSameWorkspace(String workspaceName) { + return getWorkspaceName().equals(workspaceName); + } + + @Override + public String getHref(boolean isCollection) { + final String href = getPrefix().concat(getResourcePath()); + if (isCollection && !href.endsWith("/")) { + return href.concat("/"); + } else if (!isCollection && href.endsWith("/")) { + return href.substring(0, href.length() - 1); + } else { + return href; + } + } + + @Override + public boolean isRootLocation() { + return getResourcePath() == null; + } + + @Override + public DavLocatorFactory getFactory() { + return DavLocatorFactoryImpl.this; + } + + @Override + public String getRepositoryPath() { + return DavLocatorFactoryImpl.this.getRepositoryPath(getResourcePath()); + } + + @Override + public int hashCode() { + final HashCodeBuilder builder = new HashCodeBuilder(); + builder.append(prefix); + builder.append(resourcePath); + return builder.toHashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DavResourceLocatorImpl) { + final DavResourceLocatorImpl other = (DavResourceLocatorImpl) obj; + final EqualsBuilder builder = new EqualsBuilder(); + builder.append(this.prefix, other.prefix); + builder.append(this.resourcePath, other.resourcePath); + return builder.isEquals(); + } else { + return false; + } + } + + } + } diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java index cc5e9bee6..1f8ec6cb7 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java +++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java @@ -23,7 +23,6 @@ public class WebDavServlet extends AbstractWebdavServlet { private static final long serialVersionUID = 7965170007048673022L; public static final String CFG_FS_ROOT = "cfg.fs.root"; - public static final String CFG_HTTP_ROOT = "cfg.http.root"; public static final String CFG_CHECK_FILE_INTEGRITY = "cfg.checkFileIntegrity"; private DavSessionProvider davSessionProvider; private DavLocatorFactory davLocatorFactory; @@ -42,9 +41,8 @@ public class WebDavServlet extends AbstractWebdavServlet { davSessionProvider = new DavSessionProviderImpl(); final String fsRoot = config.getInitParameter(CFG_FS_ROOT); - final String httpRoot = config.getInitParameter(CFG_HTTP_ROOT); final boolean checkFileIntegrity = Boolean.parseBoolean(config.getInitParameter(CFG_CHECK_FILE_INTEGRITY)); - this.davLocatorFactory = new DavLocatorFactoryImpl(fsRoot, httpRoot, cryptor); + this.davLocatorFactory = new DavLocatorFactoryImpl(fsRoot, cryptor); this.davResourceFactory = new DavResourceFactoryImpl(cryptor, checkFileIntegrity); }