mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-08-17 12:46:01 +00:00
Show warning dialog, if MAC check failed.
This commit is contained in:
@@ -11,6 +11,7 @@ package org.cryptomator.webdav;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@@ -83,11 +84,13 @@ public final class WebDavServer {
|
||||
/**
|
||||
* @param workDir Path of encrypted folder.
|
||||
* @param cryptor A fully initialized cryptor instance ready to en- or decrypt streams.
|
||||
* @param failingMacCollection A (observable, thread-safe) collection, to which the names of resources are written, whose MAC
|
||||
* authentication fails.
|
||||
* @param name The name of the folder. Must be non-empty and only contain any of
|
||||
* _ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
|
||||
* @return servlet
|
||||
*/
|
||||
public ServletLifeCycleAdapter createServlet(final Path workDir, final Cryptor cryptor, String name) {
|
||||
public ServletLifeCycleAdapter createServlet(final Path workDir, final Cryptor cryptor, final Collection<String> failingMacCollection, final String name) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
throw new IllegalArgumentException("name empty");
|
||||
@@ -98,7 +101,7 @@ public final class WebDavServer {
|
||||
final URI uri = new URI(null, null, localConnector.getHost(), localConnector.getLocalPort(), "/" + UUID.randomUUID().toString() + "/" + name, null, null);
|
||||
|
||||
final ServletContextHandler servletContext = new ServletContextHandler(servletCollection, uri.getRawPath(), ServletContextHandler.SESSIONS);
|
||||
final ServletHolder servlet = getWebDavServletHolder(workDir.toString(), cryptor);
|
||||
final ServletHolder servlet = getWebDavServletHolder(workDir.toString(), cryptor, failingMacCollection);
|
||||
servletContext.addServlet(servlet, "/*");
|
||||
|
||||
servletCollection.mapContexts();
|
||||
@@ -110,8 +113,8 @@ public final class WebDavServer {
|
||||
}
|
||||
}
|
||||
|
||||
private ServletHolder getWebDavServletHolder(final String workDir, final Cryptor cryptor) {
|
||||
final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor));
|
||||
private ServletHolder getWebDavServletHolder(final String workDir, final Cryptor cryptor, final Collection<String> failingMacCollection) {
|
||||
final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor, failingMacCollection));
|
||||
result.setInitParameter(WebDavServlet.CFG_FS_ROOT, workDir);
|
||||
return result;
|
||||
}
|
||||
@@ -123,7 +126,7 @@ public final class WebDavServer {
|
||||
/**
|
||||
* Exposes implementation-specific methods to other modules.
|
||||
*/
|
||||
public class ServletLifeCycleAdapter {
|
||||
public class ServletLifeCycleAdapter implements AutoCloseable {
|
||||
|
||||
private final LifeCycle lifecycle;
|
||||
private final URI servletUri;
|
||||
@@ -161,6 +164,11 @@ public final class WebDavServer {
|
||||
return servletUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
class CryptoWarningHandler {
|
||||
|
||||
private final Collection<String> resourcesWithInvalidMac;
|
||||
|
||||
public CryptoWarningHandler(Collection<String> resourcesWithInvalidMac) {
|
||||
this.resourcesWithInvalidMac = resourcesWithInvalidMac;
|
||||
}
|
||||
|
||||
public void macAuthFailed(String resourceName) {
|
||||
if (!resourcesWithInvalidMac.contains(resourceName)) {
|
||||
resourcesWithInvalidMac.add(resourceName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+5
-8
@@ -23,20 +23,17 @@ import org.apache.jackrabbit.webdav.DavSession;
|
||||
import org.apache.jackrabbit.webdav.lock.LockManager;
|
||||
import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
import org.cryptomator.webdav.jackrabbit.resources.EncryptedDir;
|
||||
import org.cryptomator.webdav.jackrabbit.resources.EncryptedFile;
|
||||
import org.cryptomator.webdav.jackrabbit.resources.EncryptedFilePart;
|
||||
import org.cryptomator.webdav.jackrabbit.resources.NonExistingNode;
|
||||
import org.cryptomator.webdav.jackrabbit.resources.ResourcePathUtils;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
|
||||
class DavResourceFactoryImpl implements DavResourceFactory {
|
||||
|
||||
private final LockManager lockManager = new SimpleLockManager();
|
||||
private final Cryptor cryptor;
|
||||
private final CryptoWarningHandler cryptoWarningHandler;
|
||||
|
||||
DavResourceFactoryImpl(Cryptor cryptor) {
|
||||
DavResourceFactoryImpl(Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler) {
|
||||
this.cryptor = cryptor;
|
||||
this.cryptoWarningHandler = cryptoWarningHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,11 +67,11 @@ class DavResourceFactoryImpl implements DavResourceFactory {
|
||||
}
|
||||
|
||||
private EncryptedFile createFilePart(DavResourceLocator locator, DavSession session, DavServletRequest request) {
|
||||
return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor);
|
||||
return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor, cryptoWarningHandler);
|
||||
}
|
||||
|
||||
private EncryptedFile createFile(DavResourceLocator locator, DavSession session) {
|
||||
return new EncryptedFile(this, locator, session, lockManager, cryptor);
|
||||
return new EncryptedFile(this, locator, session, lockManager, cryptor, cryptoWarningHandler);
|
||||
}
|
||||
|
||||
private EncryptedDir createDirectory(DavResourceLocator locator, DavSession session) {
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
@@ -42,7 +42,7 @@ import org.cryptomator.webdav.exceptions.IORuntimeException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class EncryptedDir extends AbstractEncryptedNode {
|
||||
class EncryptedDir extends AbstractEncryptedNode {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EncryptedDir.class);
|
||||
|
||||
+7
-4
@@ -6,7 +6,7 @@
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
@@ -36,12 +36,15 @@ import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class EncryptedFile extends AbstractEncryptedNode {
|
||||
class EncryptedFile extends AbstractEncryptedNode {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EncryptedFile.class);
|
||||
|
||||
public EncryptedFile(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
|
||||
private final CryptoWarningHandler cryptoWarningHandler;
|
||||
|
||||
public EncryptedFile(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler) {
|
||||
super(factory, locator, session, lockManager, cryptor);
|
||||
this.cryptoWarningHandler = cryptoWarningHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,7 +84,7 @@ public class EncryptedFile extends AbstractEncryptedNode {
|
||||
} catch (EOFException e) {
|
||||
LOG.warn("Unexpected end of stream (possibly client hung up).");
|
||||
} catch (MacAuthenticationFailedException e) {
|
||||
LOG.warn("MAC authentication failed, file content {} might be compromised.", getLocator().getResourcePath());
|
||||
cryptoWarningHandler.macAuthFailed(getLocator().getResourcePath());
|
||||
} catch (DecryptFailedException e) {
|
||||
throw new IOException("Error decrypting file " + path.toString(), e);
|
||||
}
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* @see {@link https://tools.ietf.org/html/rfc7233#section-4}
|
||||
*/
|
||||
public class EncryptedFilePart extends EncryptedFile {
|
||||
class EncryptedFilePart extends EncryptedFile {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EncryptedFilePart.class);
|
||||
private static final String BYTE_UNIT_PREFIX = "bytes=";
|
||||
@@ -49,8 +49,8 @@ public class EncryptedFilePart extends EncryptedFile {
|
||||
|
||||
private final Set<Pair<Long, Long>> requestedContentRanges = new HashSet<Pair<Long, Long>>();
|
||||
|
||||
public EncryptedFilePart(DavResourceFactory factory, DavResourceLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor) {
|
||||
super(factory, locator, session, lockManager, cryptor);
|
||||
public EncryptedFilePart(DavResourceFactory factory, DavResourceLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler) {
|
||||
super(factory, locator, session, lockManager, cryptor, cryptoWarningHandler);
|
||||
final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString());
|
||||
if (rangeHeader == null) {
|
||||
throw new IllegalArgumentException("HTTP request doesn't contain a range header");
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import org.apache.jackrabbit.webdav.property.AbstractDavProperty;
|
||||
import org.apache.jackrabbit.webdav.property.DavPropertyName;
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.apache.jackrabbit.webdav.io.OutputContext;
|
||||
import org.apache.jackrabbit.webdav.lock.LockManager;
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
|
||||
public class NonExistingNode extends AbstractEncryptedNode {
|
||||
class NonExistingNode extends AbstractEncryptedNode {
|
||||
|
||||
public NonExistingNode(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
|
||||
super(factory, locator, session, lockManager, cryptor);
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.webdav.jackrabbit.resources;
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
@@ -14,7 +14,7 @@ import java.nio.file.Path;
|
||||
import org.apache.jackrabbit.webdav.DavResource;
|
||||
import org.apache.jackrabbit.webdav.DavResourceLocator;
|
||||
|
||||
public final class ResourcePathUtils {
|
||||
final class ResourcePathUtils {
|
||||
|
||||
private ResourcePathUtils() {
|
||||
throw new IllegalStateException("not instantiable");
|
||||
@@ -8,6 +8,8 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
@@ -27,10 +29,12 @@ public class WebDavServlet extends AbstractWebdavServlet {
|
||||
private DavLocatorFactory davLocatorFactory;
|
||||
private DavResourceFactory davResourceFactory;
|
||||
private final Cryptor cryptor;
|
||||
private final CryptoWarningHandler cryptoWarningHandler;
|
||||
|
||||
public WebDavServlet(final Cryptor cryptor) {
|
||||
public WebDavServlet(final Cryptor cryptor, final Collection<String> failingMacCollection) {
|
||||
super();
|
||||
this.cryptor = cryptor;
|
||||
this.cryptoWarningHandler = new CryptoWarningHandler(failingMacCollection);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,7 +46,7 @@ public class WebDavServlet extends AbstractWebdavServlet {
|
||||
final String fsRoot = config.getInitParameter(CFG_FS_ROOT);
|
||||
this.davLocatorFactory = new DavLocatorFactoryImpl(fsRoot, cryptor);
|
||||
|
||||
this.davResourceFactory = new DavResourceFactoryImpl(cryptor);
|
||||
this.davResourceFactory = new DavResourceFactoryImpl(cryptor, cryptoWarningHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -53,11 +53,7 @@ public class MainApplication extends Application {
|
||||
}
|
||||
|
||||
private static Injector getInjector() {
|
||||
try {
|
||||
return Guice.createInjector(new MainModule());
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
return Guice.createInjector(new MainModule());
|
||||
}
|
||||
|
||||
public MainApplication(Injector injector) {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class MacWarningsController {
|
||||
|
||||
@FXML
|
||||
private ListView<String> warningsList;
|
||||
|
||||
private Stage stage;
|
||||
|
||||
@FXML
|
||||
private void hideWindow(ActionEvent event) {
|
||||
stage.hide();
|
||||
}
|
||||
|
||||
public void setMacWarnings(ObservableList<String> macWarnings) {
|
||||
this.warningsList.setItems(macWarnings);
|
||||
}
|
||||
|
||||
public Stage getStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
public void setStage(Stage stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,21 +13,25 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.SetChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.geometry.Side;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
@@ -47,6 +51,7 @@ import org.cryptomator.ui.controls.DirectoryListCell;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.model.VaultFactory;
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.util.ObservableSetAggregator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -79,6 +84,9 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
private final ControllerFactory controllerFactory;
|
||||
private final Settings settings;
|
||||
private final VaultFactory vaultFactoy;
|
||||
private final ObservableList<String> aggregatedMacWarnings;
|
||||
private final SetChangeListener<String> macWarningsAggregator;
|
||||
private final AtomicBoolean macWarningsWindowVisible;
|
||||
|
||||
private ResourceBundle rb;
|
||||
|
||||
@@ -88,6 +96,9 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
this.controllerFactory = controllerFactory;
|
||||
this.settings = settings;
|
||||
this.vaultFactoy = vaultFactoy;
|
||||
this.aggregatedMacWarnings = FXCollections.observableList(new ArrayList<>());
|
||||
this.macWarningsAggregator = new ObservableSetAggregator<>(this.aggregatedMacWarnings);
|
||||
this.macWarningsWindowVisible = new AtomicBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,6 +109,8 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
vaultList.setItems(items);
|
||||
vaultList.setCellFactory(this::createDirecoryListCell);
|
||||
vaultList.getSelectionModel().getSelectedItems().addListener(this::selectedVaultDidChange);
|
||||
|
||||
aggregatedMacWarnings.addListener(this::macWarningsDidChange);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -216,6 +229,12 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
showChangePasswordView(selectedVault);
|
||||
}
|
||||
|
||||
private void macWarningsDidChange(ListChangeListener.Change<? extends String> change) {
|
||||
if (aggregatedMacWarnings.size() > 0) {
|
||||
Platform.runLater(this::showMacWarningsWindow);
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Subcontroller for right panel
|
||||
// ****************************************
|
||||
@@ -270,6 +289,7 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
|
||||
@Override
|
||||
public void didUnlock(UnlockController ctrl) {
|
||||
ctrl.getVault().getNamesOfResourcesWithInvalidMac().addListener(this.macWarningsAggregator);
|
||||
showUnlockedView(ctrl.getVault());
|
||||
Platform.setImplicitExit(false);
|
||||
}
|
||||
@@ -282,6 +302,7 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
|
||||
@Override
|
||||
public void didLock(UnlockedController ctrl) {
|
||||
ctrl.getVault().getNamesOfResourcesWithInvalidMac().removeListener(this.macWarningsAggregator);
|
||||
showUnlockView(ctrl.getVault());
|
||||
if (getUnlockedDirectories().isEmpty()) {
|
||||
Platform.setImplicitExit(true);
|
||||
@@ -299,6 +320,36 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
showUnlockView(ctrl.getVault());
|
||||
}
|
||||
|
||||
private void showMacWarningsWindow() {
|
||||
if (macWarningsWindowVisible.getAndSet(true) == false) {
|
||||
try {
|
||||
final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/mac_warnings.fxml"), rb);
|
||||
loader.setControllerFactory(controllerFactory);
|
||||
|
||||
final Parent root = loader.load();
|
||||
final Stage stage = new Stage();
|
||||
stage.setTitle(rb.getString("macWarnings.windowTitle"));
|
||||
stage.setScene(new Scene(root));
|
||||
stage.sizeToScene();
|
||||
stage.setResizable(false);
|
||||
stage.setOnHidden(this::onHideMacWarningsWindow);
|
||||
|
||||
final MacWarningsController ctrl = loader.getController();
|
||||
ctrl.setMacWarnings(this.aggregatedMacWarnings);
|
||||
ctrl.setStage(stage);
|
||||
|
||||
stage.show();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to load fxml file.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onHideMacWarningsWindow(WindowEvent event) {
|
||||
macWarningsWindowVisible.set(false);
|
||||
aggregatedMacWarnings.clear();
|
||||
}
|
||||
|
||||
/* Convenience */
|
||||
|
||||
public Collection<Vault> getDirectories() {
|
||||
|
||||
@@ -10,11 +10,14 @@ import java.util.Optional;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
import org.cryptomator.ui.util.DeferredClosable;
|
||||
import org.cryptomator.ui.util.DeferredCloser;
|
||||
import org.cryptomator.ui.util.FXThreads;
|
||||
import org.cryptomator.ui.util.mount.CommandFailedException;
|
||||
import org.cryptomator.ui.util.mount.WebDavMount;
|
||||
import org.cryptomator.ui.util.mount.WebDavMounter;
|
||||
@@ -38,6 +41,7 @@ public class Vault implements Serializable {
|
||||
private final WebDavMounter mounter;
|
||||
private final DeferredCloser closer;
|
||||
private final ObjectProperty<Boolean> unlocked = new SimpleObjectProperty<Boolean>(this, "unlocked", Boolean.FALSE);
|
||||
private final ObservableSet<String> namesOfResourcesWithInvalidMac = FXThreads.observableSetOnMainThread(FXCollections.observableSet());
|
||||
|
||||
private String mountName;
|
||||
private DeferredClosable<ServletLifeCycleAdapter> webDavServlet = DeferredClosable.empty();
|
||||
@@ -70,13 +74,14 @@ public class Vault implements Serializable {
|
||||
}
|
||||
|
||||
public synchronized boolean startServer() {
|
||||
namesOfResourcesWithInvalidMac.clear();
|
||||
Optional<ServletLifeCycleAdapter> o = webDavServlet.get();
|
||||
if (o.isPresent() && o.get().isRunning()) {
|
||||
return false;
|
||||
}
|
||||
ServletLifeCycleAdapter servlet = server.createServlet(path, cryptor, getMountName());
|
||||
ServletLifeCycleAdapter servlet = server.createServlet(path, cryptor, namesOfResourcesWithInvalidMac, mountName);
|
||||
if (servlet.start()) {
|
||||
webDavServlet = closer.closeLater(servlet, ServletLifeCycleAdapter::stop);
|
||||
webDavServlet = closer.closeLater(servlet);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -86,6 +91,7 @@ public class Vault implements Serializable {
|
||||
unmount();
|
||||
webDavServlet.close();
|
||||
cryptor.swipeSensitiveData();
|
||||
namesOfResourcesWithInvalidMac.clear();
|
||||
}
|
||||
|
||||
public boolean mount() {
|
||||
@@ -94,7 +100,7 @@ public class Vault implements Serializable {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
webDavMount = closer.closeLater(mounter.mount(o.get().getServletUri(), getMountName()), WebDavMount::unmount);
|
||||
webDavMount = closer.closeLater(mounter.mount(o.get().getServletUri(), mountName));
|
||||
return true;
|
||||
} catch (CommandFailedException e) {
|
||||
LOG.warn("mount failed", e);
|
||||
@@ -139,6 +145,10 @@ public class Vault implements Serializable {
|
||||
return mountName;
|
||||
}
|
||||
|
||||
public ObservableSet<String> getNamesOfResourcesWithInvalidMac() {
|
||||
return namesOfResourcesWithInvalidMac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to form a similar string using the regular latin alphabet.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ObservableSet;
|
||||
|
||||
/**
|
||||
* Use this utility class to spawn background tasks and wait for them to finish. <br/>
|
||||
@@ -118,4 +119,8 @@ public final class FXThreads {
|
||||
void taskFailed(Throwable t);
|
||||
}
|
||||
|
||||
public static <E> ObservableSet<E> observableSetOnMainThread(ObservableSet<E> set) {
|
||||
return new ObservableSetOnMainThread<E>(set);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 cryptomator.org
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.collections.SetChangeListener;
|
||||
|
||||
/**
|
||||
* From the moment on, this aggregator is added as an observer to one or many {@link ObservableSet}s, change-events will be passed through
|
||||
* to the given aggregation.
|
||||
*/
|
||||
public class ObservableSetAggregator<E> implements SetChangeListener<E> {
|
||||
|
||||
private final Collection<E> aggregation;
|
||||
|
||||
/**
|
||||
* @param aggregation Set to which elements from observed subsets shall be added.
|
||||
*/
|
||||
public ObservableSetAggregator(final Collection<E> aggregation) {
|
||||
this.aggregation = aggregation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(Change<? extends E> change) {
|
||||
if (change.getSet() == aggregation) {
|
||||
// break cycle if aggregator observes aggregation
|
||||
return;
|
||||
}
|
||||
if (change.wasAdded()) {
|
||||
aggregation.add(change.getElementAdded());
|
||||
} else if (change.wasRemoved()) {
|
||||
aggregation.remove(change.getElementRemoved());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package org.cryptomator.ui.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.collections.SetChangeListener;
|
||||
import javafx.collections.SetChangeListener.Change;
|
||||
|
||||
class ObservableSetOnMainThread<E> implements ObservableSet<E> {
|
||||
|
||||
private final ObservableSet<E> set;
|
||||
private final Collection<InvalidationListener> invalidationListeners;
|
||||
private final Collection<SetChangeListener<? super E>> setChangeListeners;
|
||||
|
||||
public ObservableSetOnMainThread(ObservableSet<E> set) {
|
||||
this.set = set;
|
||||
this.invalidationListeners = new HashSet<>();
|
||||
this.setChangeListeners = new HashSet<>();
|
||||
this.set.addListener(this::invalidated);
|
||||
this.set.addListener(this::onChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return set.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return set.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return set.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return set.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return set.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
return set.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return set.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return set.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> c) {
|
||||
return set.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return set.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return set.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
set.clear();
|
||||
}
|
||||
|
||||
private void invalidated(Observable observable) {
|
||||
Platform.runLater(() -> {
|
||||
for (InvalidationListener listener : invalidationListeners) {
|
||||
listener.invalidated(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(InvalidationListener listener) {
|
||||
invalidationListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(InvalidationListener listener) {
|
||||
invalidationListeners.remove(listener);
|
||||
}
|
||||
|
||||
private void onChanged(Change<? extends E> change) {
|
||||
final Change<? extends E> c = new SetChange(this, change.getElementAdded(), change.getElementRemoved());
|
||||
Platform.runLater(() -> {
|
||||
for (SetChangeListener<? super E> listener : setChangeListeners) {
|
||||
listener.onChanged(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(SetChangeListener<? super E> listener) {
|
||||
setChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(SetChangeListener<? super E> listener) {
|
||||
setChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
private class SetChange extends SetChangeListener.Change<E> {
|
||||
|
||||
private final E added;
|
||||
private final E removed;
|
||||
|
||||
public SetChange(ObservableSet<E> set, E added, E removed) {
|
||||
super(set);
|
||||
this.added = added;
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasAdded() {
|
||||
return added != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasRemoved() {
|
||||
return removed != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getElementAdded() {
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getElementRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.cryptomator.ui.util.mount;
|
||||
|
||||
abstract class AbstractWebDavMount implements WebDavMount {
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
this.unmount();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,7 +30,7 @@ final class FallbackWebDavMounter implements WebDavMounterStrategy {
|
||||
@Override
|
||||
public WebDavMount mount(URI uri, String name) {
|
||||
displayMountInstructions();
|
||||
return new WebDavMount() {
|
||||
return new AbstractWebDavMount() {
|
||||
@Override
|
||||
public void unmount() {
|
||||
displayUnmountInstructions();
|
||||
|
||||
@@ -48,7 +48,7 @@ final class LinuxGvfsWebDavMounter implements WebDavMounterStrategy {
|
||||
"gvfs-mount -u \"dav:$DAV_SSP\"")
|
||||
.addEnv("DAV_SSP", uri.getRawSchemeSpecificPart());
|
||||
mountScript.execute();
|
||||
return new WebDavMount() {
|
||||
return new AbstractWebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute();
|
||||
|
||||
@@ -41,7 +41,7 @@ final class MacOsXWebDavMounter implements WebDavMounterStrategy {
|
||||
"umount $MOUNT_PATH")
|
||||
.addEnv("MOUNT_PATH", path);
|
||||
mountScript.execute();
|
||||
return new WebDavMount() {
|
||||
return new AbstractWebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute();
|
||||
|
||||
@@ -8,13 +8,12 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.mount;
|
||||
|
||||
|
||||
/**
|
||||
* A mounted webdav share.
|
||||
*
|
||||
* @author Markus Kreusch
|
||||
*/
|
||||
public interface WebDavMount {
|
||||
public interface WebDavMount extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Unmounts this {@code WebDavMount}.
|
||||
@@ -22,5 +21,5 @@ public interface WebDavMount {
|
||||
* @throws CommandFailedException if the unmount operation fails
|
||||
*/
|
||||
void unmount() throws CommandFailedException;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy {
|
||||
final CommandResult mountResult = mountScript.execute(30, TimeUnit.SECONDS);
|
||||
final String driveLetter = getDriveLetter(mountResult.getStdOut());
|
||||
final Script unmountScript = fromLines("net use " + driveLetter + " /delete").addEnv("DRIVE_LETTER", driveLetter);
|
||||
return new WebDavMount() {
|
||||
return new AbstractWebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute();
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
Contributors:
|
||||
Sebastian Stenzel - initial API and implementation
|
||||
-->
|
||||
<?import java.net.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import java.net.URL?>
|
||||
<?import java.lang.String?>
|
||||
<?import org.cryptomator.ui.controls.SecPasswordField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ProgressIndicator?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
|
||||
|
||||
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.ChangePasswordController" xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
Contributors:
|
||||
Sebastian Stenzel - initial API and implementation
|
||||
-->
|
||||
<?import java.net.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import org.cryptomator.ui.controls.*?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import org.cryptomator.ui.controls.SecPasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
|
||||
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.InitializeController" xmlns:fx="http://javafx.com/fxml">
|
||||
<padding>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2014 Sebastian Stenzel
|
||||
This file is licensed under the terms of the MIT license.
|
||||
See the LICENSE.txt file for more info.
|
||||
|
||||
Contributors:
|
||||
Sebastian Stenzel - initial API and implementation
|
||||
-->
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
|
||||
<VBox alignment="CENTER" prefHeight="225.0" prefWidth="400.0" spacing="12.0" fx:controller="org.cryptomator.ui.controllers.MacWarningsController" xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<padding><Insets top="12.0" right="12.0" bottom="12.0" left="12.0"/></padding>
|
||||
|
||||
<children>
|
||||
<Label text="%macWarnings.message" />
|
||||
<ListView fx:id="warningsList" VBox.vgrow="ALWAYS" focusTraversable="false" />
|
||||
<Button text="%macWarnings.okButton" defaultButton="true" prefWidth="150.0" onAction="#hideWindow" />
|
||||
</children>
|
||||
|
||||
</VBox>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
<?import javafx.scene.control.ToggleButton?>
|
||||
<?import javafx.scene.control.ContextMenu?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<?import javafx.scene.control.Separator?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
|
||||
<HBox fx:id="rootPane" prefHeight="440.0" prefWidth="640.0" fx:controller="org.cryptomator.ui.controllers.MainController" xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
Contributors:
|
||||
Sebastian Stenzel - initial API and implementation
|
||||
-->
|
||||
<?import java.net.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import java.net.URL?>
|
||||
<?import java.lang.String?>
|
||||
<?import org.cryptomator.ui.controls.SecPasswordField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ProgressIndicator?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
|
||||
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.UnlockController" xmlns:fx="http://javafx.com/fxml">
|
||||
<padding>
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
Contributors:
|
||||
Sebastian Stenzel - initial API and implementation
|
||||
-->
|
||||
<?import java.net.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import java.net.URL?>
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.chart.LineChart?>
|
||||
<?import javafx.scene.chart.NumberAxis?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
|
||||
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.UnlockedController" xmlns:fx="http://javafx.com/fxml">
|
||||
<padding>
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
Contributors:
|
||||
Sebastian Stenzel - initial API and implementation
|
||||
-->
|
||||
<?import java.net.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import java.net.URL?>
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.shape.Arc?>
|
||||
<?import javafx.scene.shape.QuadCurve?>
|
||||
<?import javafx.scene.shape.Path?>
|
||||
<?import javafx.scene.shape.Line?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
|
||||
|
||||
<AnchorPane xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
@@ -15,18 +15,15 @@ main.directoryList.contextMenu.changePassword=Change password
|
||||
main.addDirectory.contextMenu.new=Create new vault
|
||||
main.addDirectory.contextMenu.open=Add existing vault
|
||||
|
||||
|
||||
# welcome.fxml
|
||||
welcome.welcomeLabel=Welcome to Cryptomator
|
||||
welcome.addButtonInstructionLabel=Start by adding a new vault :-)
|
||||
|
||||
|
||||
# initialize.fxml
|
||||
initialize.label.password=Password
|
||||
initialize.label.retypePassword=Retype password
|
||||
initialize.button.ok=Create vault
|
||||
|
||||
|
||||
# unlock.fxml
|
||||
unlock.label.password=Password
|
||||
unlock.label.mountName=Drive name
|
||||
@@ -50,6 +47,10 @@ changePassword.infoMessage.success=Password changed.
|
||||
unlocked.button.lock=Lock vault
|
||||
unlocked.ioGraph.yAxis.label=Throughput (MiB/s)
|
||||
|
||||
# mac_warnings.fxml
|
||||
macWarnings.windowTitle=Mac authentication failed
|
||||
macWarnings.message=The following files may be corrupted
|
||||
macWarnings.okButton=Dismiss warning
|
||||
|
||||
# tray icon
|
||||
tray.menu.open=Open
|
||||
|
||||
Reference in New Issue
Block a user