mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 18:21:26 +00:00
- increased vault version
- Showing "per vault" MAC authentication failure dialogs
This commit is contained in:
@@ -84,13 +84,11 @@ 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
|
||||
* @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, final Collection<String> failingMacCollection, final String name) {
|
||||
public ServletLifeCycleAdapter createServlet(final Path workDir, final Cryptor cryptor, final Collection<String> failingMacCollection, final Collection<String> whitelistedResourceCollection, final String name) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
throw new IllegalArgumentException("name empty");
|
||||
@@ -101,7 +99,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, failingMacCollection);
|
||||
final ServletHolder servlet = getWebDavServletHolder(workDir.toString(), cryptor, failingMacCollection, whitelistedResourceCollection);
|
||||
servletContext.addServlet(servlet, "/*");
|
||||
|
||||
servletCollection.mapContexts();
|
||||
@@ -113,8 +111,8 @@ public final class WebDavServer {
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
private ServletHolder getWebDavServletHolder(final String workDir, final Cryptor cryptor, final Collection<String> failingMacCollection, final Collection<String> whitelistedResourceCollection) {
|
||||
final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor, failingMacCollection, whitelistedResourceCollection));
|
||||
result.setInitParameter(WebDavServlet.CFG_FS_ROOT, workDir);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -5,15 +5,22 @@ import java.util.Collection;
|
||||
class CryptoWarningHandler {
|
||||
|
||||
private final Collection<String> resourcesWithInvalidMac;
|
||||
private final Collection<String> whitelistedResources;
|
||||
|
||||
public CryptoWarningHandler(Collection<String> resourcesWithInvalidMac) {
|
||||
public CryptoWarningHandler(Collection<String> resourcesWithInvalidMac, Collection<String> whitelistedResources) {
|
||||
this.resourcesWithInvalidMac = resourcesWithInvalidMac;
|
||||
this.whitelistedResources = whitelistedResources;
|
||||
}
|
||||
|
||||
public void macAuthFailed(String resourceName) {
|
||||
if (!resourcesWithInvalidMac.contains(resourceName)) {
|
||||
resourcesWithInvalidMac.add(resourceName);
|
||||
public void macAuthFailed(String resourcePath) {
|
||||
// collection might be a list, but we don't want duplicates:
|
||||
if (!resourcesWithInvalidMac.contains(resourcePath)) {
|
||||
resourcesWithInvalidMac.add(resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean ignoreMac(String resourcePath) {
|
||||
return whitelistedResources.contains(resourcePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ class EncryptedFile extends AbstractEncryptedNode implements FileConstants {
|
||||
LOG.warn("Unexpected end of stream (possibly client hung up).");
|
||||
} catch (MacAuthenticationFailedException e) {
|
||||
LOG.warn("File integrity violation for " + getLocator().getResourcePath());
|
||||
cryptoWarningHandler.macAuthFailed(getLocator().getResourcePath());
|
||||
throw new IOException("Error decrypting file " + filePath.toString(), e);
|
||||
// cryptoWarningHandler.macAuthFailed(getLocator().getResourcePath());
|
||||
} catch (DecryptFailedException e) {
|
||||
throw new IOException("Error decrypting file " + filePath.toString(), e);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.apache.jackrabbit.webdav.io.OutputContext;
|
||||
import org.apache.jackrabbit.webdav.lock.LockManager;
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
import org.cryptomator.crypto.exceptions.DecryptFailedException;
|
||||
import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -111,19 +112,23 @@ class EncryptedFilePart extends EncryptedFile {
|
||||
public void spool(OutputContext outputContext) throws IOException {
|
||||
assert Files.isRegularFile(filePath);
|
||||
outputContext.setModificationTime(Files.getLastModifiedTime(filePath).toMillis());
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(filePath, StandardOpenOption.READ)) {
|
||||
final Long fileSize = cryptor.decryptedContentLength(channel);
|
||||
try (final SeekableByteChannel c = Files.newByteChannel(filePath, StandardOpenOption.READ)) {
|
||||
final Long fileSize = cryptor.decryptedContentLength(c);
|
||||
final Pair<Long, Long> range = getUnionRange(fileSize);
|
||||
final Long rangeLength = range.getRight() - range.getLeft() + 1;
|
||||
outputContext.setContentLength(rangeLength);
|
||||
outputContext.setProperty(HttpHeader.CONTENT_RANGE.asString(), getContentRangeHeader(range.getLeft(), range.getRight(), fileSize));
|
||||
if (outputContext.hasStream()) {
|
||||
cryptor.decryptRange(channel, outputContext.getOutputStream(), range.getLeft(), rangeLength);
|
||||
cryptor.decryptRange(c, outputContext.getOutputStream(), range.getLeft(), rangeLength);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.trace("Unexpected end of stream during delivery of partial content (client hung up).");
|
||||
}
|
||||
} catch (MacAuthenticationFailedException e) {
|
||||
LOG.warn("File integrity violation for " + getLocator().getResourcePath());
|
||||
cryptoWarningHandler.macAuthFailed(getLocator().getResourcePath());
|
||||
throw new IOException("Error decrypting file " + filePath.toString(), e);
|
||||
} catch (DecryptFailedException e) {
|
||||
throw new IOException("Error decrypting file " + filePath.toString(), e);
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ public class WebDavServlet extends AbstractWebdavServlet {
|
||||
private final Cryptor cryptor;
|
||||
private final CryptoWarningHandler cryptoWarningHandler;
|
||||
|
||||
public WebDavServlet(final Cryptor cryptor, final Collection<String> failingMacCollection) {
|
||||
public WebDavServlet(final Cryptor cryptor, final Collection<String> failingMacCollection, final Collection<String> whitelistedResourceCollection) {
|
||||
super();
|
||||
this.cryptor = cryptor;
|
||||
this.cryptoWarningHandler = new CryptoWarningHandler(failingMacCollection);
|
||||
this.cryptoWarningHandler = new CryptoWarningHandler(failingMacCollection, whitelistedResourceCollection);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
@JsonPropertyOrder(value = {"version", "scryptSalt", "scryptCostParam", "scryptBlockSize", "keyLength", "primaryMasterKey", "hMacMasterKey"})
|
||||
public class KeyFile implements Serializable {
|
||||
|
||||
static final Integer CURRENT_VERSION = 1;
|
||||
static final Integer CURRENT_VERSION = 2;
|
||||
private static final long serialVersionUID = 8578363158959619885L;
|
||||
|
||||
private Integer version;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ListChangeListener.Change;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.WeakListChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -11,14 +11,18 @@ import javafx.stage.Stage;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
|
||||
public class MacWarningsController {
|
||||
|
||||
@FXML
|
||||
private ListView<String> warningsList;
|
||||
|
||||
private Stage stage;
|
||||
|
||||
private final Application application;
|
||||
private final ListChangeListener<? super String> macWarningsListener = this::warningsDidChange;
|
||||
private final ListChangeListener<? super String> weakMacWarningsListener = new WeakListChangeListener<>(macWarningsListener);
|
||||
private Stage stage;
|
||||
private Vault vault;
|
||||
|
||||
@Inject
|
||||
public MacWarningsController(Application application) {
|
||||
@@ -27,6 +31,7 @@ public class MacWarningsController {
|
||||
|
||||
@FXML
|
||||
private void didClickDismissButton(ActionEvent event) {
|
||||
warningsList.getItems().removeListener(weakMacWarningsListener);
|
||||
stage.hide();
|
||||
}
|
||||
|
||||
@@ -35,14 +40,10 @@ public class MacWarningsController {
|
||||
application.getHostServices().showDocument("https://cryptomator.org/help.html#macWarning");
|
||||
}
|
||||
|
||||
public void setMacWarnings(ObservableList<String> macWarnings) {
|
||||
this.warningsList.setItems(macWarnings);
|
||||
this.warningsList.getItems().addListener(new WeakListChangeListener<String>(this::warningsDidChange));
|
||||
}
|
||||
|
||||
// closes this window automatically, if all warnings disappeared (e.g. due to an unmount event)
|
||||
private void warningsDidChange(Change<? extends String> change) {
|
||||
if (change.getList().isEmpty()) {
|
||||
if (change.getList().isEmpty() && stage != null) {
|
||||
change.getList().removeListener(weakMacWarningsListener);
|
||||
stage.hide();
|
||||
}
|
||||
}
|
||||
@@ -55,4 +56,10 @@ public class MacWarningsController {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
public void setVault(Vault vault) {
|
||||
this.vault = vault;
|
||||
this.warningsList.setItems(vault.getNamesOfResourcesWithInvalidMac());
|
||||
this.warningsList.getItems().addListener(weakMacWarningsListener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,25 +13,21 @@ 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;
|
||||
@@ -51,8 +47,6 @@ 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.ActiveWindowStyleSupport;
|
||||
import org.cryptomator.ui.util.ObservableSetAggregator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -85,9 +79,6 @@ 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;
|
||||
|
||||
@@ -97,9 +88,6 @@ 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
|
||||
@@ -110,8 +98,6 @@ 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
|
||||
@@ -233,12 +219,6 @@ 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
|
||||
// ****************************************
|
||||
@@ -293,7 +273,6 @@ 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);
|
||||
}
|
||||
@@ -306,7 +285,6 @@ 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);
|
||||
@@ -324,37 +302,6 @@ 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);
|
||||
ActiveWindowStyleSupport.startObservingFocus(stage);
|
||||
|
||||
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() {
|
||||
|
||||
@@ -8,31 +8,49 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.WeakListChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.chart.LineChart;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.XYChart.Data;
|
||||
import javafx.scene.chart.XYChart.Series;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import org.cryptomator.crypto.CryptorIOSampling;
|
||||
import org.cryptomator.ui.MainModule.ControllerFactory;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.util.ActiveWindowStyleSupport;
|
||||
import org.cryptomator.ui.util.mount.CommandFailedException;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class UnlockedController implements Initializable {
|
||||
|
||||
private static final int IO_SAMPLING_STEPS = 100;
|
||||
private static final double IO_SAMPLING_INTERVAL = 0.25;
|
||||
private final ControllerFactory controllerFactory;
|
||||
private final ListChangeListener<String> macWarningsListener = this::macWarningsDidChange;
|
||||
private final ListChangeListener<String> weakMacWarningsListener = new WeakListChangeListener<>(macWarningsListener);
|
||||
private final AtomicBoolean macWarningsWindowVisible = new AtomicBoolean();
|
||||
private LockListener listener;
|
||||
private Vault vault;
|
||||
private Timeline ioAnimation;
|
||||
@@ -48,6 +66,11 @@ public class UnlockedController implements Initializable {
|
||||
|
||||
private ResourceBundle rb;
|
||||
|
||||
@Inject
|
||||
public UnlockedController(ControllerFactory controllerFactory) {
|
||||
this.controllerFactory = controllerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb) {
|
||||
this.rb = rb;
|
||||
@@ -61,6 +84,7 @@ public class UnlockedController implements Initializable {
|
||||
messageLabel.setText(rb.getString("unlocked.label.unmountFailed"));
|
||||
return;
|
||||
}
|
||||
vault.getNamesOfResourcesWithInvalidMac().removeListener(weakMacWarningsListener);
|
||||
vault.stopServer();
|
||||
vault.setUnlocked(false);
|
||||
if (listener != null) {
|
||||
@@ -68,6 +92,46 @@ public class UnlockedController implements Initializable {
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// MAC Auth Warnings
|
||||
// ****************************************
|
||||
|
||||
private void macWarningsDidChange(ListChangeListener.Change<? extends String> change) {
|
||||
if (change.getList().size() > 0) {
|
||||
Platform.runLater(this::showMacWarningsWindow);
|
||||
}
|
||||
}
|
||||
|
||||
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(String.format(rb.getString("macWarnings.windowTitle"), vault.getName()));
|
||||
stage.setScene(new Scene(root));
|
||||
stage.sizeToScene();
|
||||
stage.setResizable(false);
|
||||
stage.setOnHidden(this::onHideMacWarningsWindow);
|
||||
ActiveWindowStyleSupport.startObservingFocus(stage);
|
||||
|
||||
final MacWarningsController ctrl = loader.getController();
|
||||
ctrl.setVault(vault);
|
||||
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);
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// IO Graph
|
||||
// ****************************************
|
||||
@@ -128,11 +192,12 @@ public class UnlockedController implements Initializable {
|
||||
return vault;
|
||||
}
|
||||
|
||||
public void setVault(Vault directory) {
|
||||
this.vault = directory;
|
||||
public void setVault(Vault vault) {
|
||||
this.vault = vault;
|
||||
vault.getNamesOfResourcesWithInvalidMac().addListener(weakMacWarningsListener);
|
||||
|
||||
if (directory.getCryptor() instanceof CryptorIOSampling) {
|
||||
startIoSampling((CryptorIOSampling) directory.getCryptor());
|
||||
if (vault.getCryptor() instanceof CryptorIOSampling) {
|
||||
startIoSampling((CryptorIOSampling) vault.getCryptor());
|
||||
} else {
|
||||
ioGraph.setVisible(false);
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.Normalizer;
|
||||
import java.text.Normalizer.Form;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
|
||||
@@ -43,7 +45,8 @@ 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 final ObservableList<String> namesOfResourcesWithInvalidMac = FXThreads.observableListOnMainThread(FXCollections.observableArrayList());
|
||||
private final Set<String> whitelistedResourcesWithInvalidMac = new HashSet<>();
|
||||
|
||||
private String mountName;
|
||||
private DeferredClosable<ServletLifeCycleAdapter> webDavServlet = DeferredClosable.empty();
|
||||
@@ -77,11 +80,12 @@ public class Vault implements Serializable {
|
||||
|
||||
public synchronized boolean startServer() {
|
||||
namesOfResourcesWithInvalidMac.clear();
|
||||
whitelistedResourcesWithInvalidMac.clear();
|
||||
Optional<ServletLifeCycleAdapter> o = webDavServlet.get();
|
||||
if (o.isPresent() && o.get().isRunning()) {
|
||||
return false;
|
||||
}
|
||||
ServletLifeCycleAdapter servlet = server.createServlet(path, cryptor, namesOfResourcesWithInvalidMac, mountName);
|
||||
ServletLifeCycleAdapter servlet = server.createServlet(path, cryptor, namesOfResourcesWithInvalidMac, whitelistedResourcesWithInvalidMac, mountName);
|
||||
if (servlet.start()) {
|
||||
webDavServlet = closer.closeLater(servlet);
|
||||
return true;
|
||||
@@ -102,6 +106,7 @@ public class Vault implements Serializable {
|
||||
LOG.error("Destruction of cryptor throw an exception.", e);
|
||||
}
|
||||
setUnlocked(false);
|
||||
whitelistedResourcesWithInvalidMac.clear();
|
||||
namesOfResourcesWithInvalidMac.clear();
|
||||
}
|
||||
|
||||
@@ -160,10 +165,14 @@ public class Vault implements Serializable {
|
||||
return mountName;
|
||||
}
|
||||
|
||||
public ObservableSet<String> getNamesOfResourcesWithInvalidMac() {
|
||||
public ObservableList<String> getNamesOfResourcesWithInvalidMac() {
|
||||
return namesOfResourcesWithInvalidMac;
|
||||
}
|
||||
|
||||
public Set<String> getWhitelistedResourcesWithInvalidMac() {
|
||||
return whitelistedResourcesWithInvalidMac;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.ObservableList;
|
||||
import javafx.collections.ObservableSet;
|
||||
|
||||
/**
|
||||
@@ -53,8 +54,7 @@ public final class FXThreads {
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits for the given task to complete and notifies the given successCallback. If an exception occurs, the callback will never be
|
||||
* called. If you are interested in the exception, use
|
||||
* Waits for the given task to complete and notifies the given successCallback. If an exception occurs, the callback will never be called. If you are interested in the exception, use
|
||||
* {@link #runOnMainThreadWhenFinished(ExecutorService, Future, CallbackWhenTaskFinished, CallbackWhenTaskFailed)} instead.
|
||||
*
|
||||
* <pre>
|
||||
@@ -74,8 +74,7 @@ public final class FXThreads {
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the given task to complete and notifies the given successCallback. If an exception occurs, the callback will never be
|
||||
* called. If you are interested in the exception, use
|
||||
* Waits for the given task to complete and notifies the given successCallback. If an exception occurs, the callback will never be called. If you are interested in the exception, use
|
||||
* {@link #runOnMainThreadWhenFinished(ExecutorService, Future, CallbackWhenTaskFinished, CallbackWhenTaskFailed)} instead.
|
||||
*
|
||||
* <pre>
|
||||
@@ -123,4 +122,8 @@ public final class FXThreads {
|
||||
return new ObservableSetOnMainThread<E>(set);
|
||||
}
|
||||
|
||||
public static <E> ObservableList<E> observableListOnMainThread(ObservableList<E> list) {
|
||||
return new ObservableListOnMainThread<E>(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
package org.cryptomator.ui.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ListChangeListener.Change;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
class ObservableListOnMainThread<E> implements ObservableList<E> {
|
||||
|
||||
private final ObservableList<E> list;
|
||||
private final Collection<InvalidationListener> invalidationListeners;
|
||||
private final Collection<ListChangeListener<? super E>> listChangeListeners;
|
||||
|
||||
public ObservableListOnMainThread(ObservableList<E> list) {
|
||||
this.list = list;
|
||||
this.invalidationListeners = new HashSet<>();
|
||||
this.listChangeListeners = new HashSet<>();
|
||||
this.list.addListener(this::invalidated);
|
||||
this.list.addListener(this::onChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return list.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return list.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return list.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
return list.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return list.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return list.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> c) {
|
||||
return list.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends E> c) {
|
||||
return list.addAll(index, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return list.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return list.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int index, E element) {
|
||||
return list.set(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, E element) {
|
||||
list.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int index) {
|
||||
return list.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return list.indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return list.lastIndexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator() {
|
||||
return list.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
return list.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<E> subList(int fromIndex, int toIndex) {
|
||||
return list.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@SuppressWarnings("unchecked") E... elements) {
|
||||
return list.addAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAll(@SuppressWarnings("unchecked") E... elements) {
|
||||
return list.addAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAll(Collection<? extends E> col) {
|
||||
return list.setAll(col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@SuppressWarnings("unchecked") E... elements) {
|
||||
return list.removeAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@SuppressWarnings("unchecked") E... elements) {
|
||||
return list.retainAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(int from, int to) {
|
||||
list.remove(from, to);
|
||||
}
|
||||
|
||||
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 ListChange(change);
|
||||
Platform.runLater(() -> {
|
||||
for (ListChangeListener<? super E> listener : listChangeListeners) {
|
||||
listener.onChanged(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ListChangeListener<? super E> listener) {
|
||||
listChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ListChangeListener<? super E> listener) {
|
||||
listChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
private class ListChange extends ListChangeListener.Change<E> {
|
||||
|
||||
private final Change<? extends E> originalChange;
|
||||
|
||||
public ListChange(Change<? extends E> change) {
|
||||
super(ObservableListOnMainThread.this);
|
||||
this.originalChange = change;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasAdded() {
|
||||
return originalChange.wasAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasRemoved() {
|
||||
return originalChange.wasRemoved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
return originalChange.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
originalChange.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrom() {
|
||||
return originalChange.getFrom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTo() {
|
||||
return originalChange.getTo();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<E> getRemoved() {
|
||||
return (List<E>) originalChange.getRemoved();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] getPermutation() {
|
||||
if (originalChange.wasPermutated()) {
|
||||
int[] permutations = new int[originalChange.getTo() - originalChange.getFrom()];
|
||||
for (int i = 0; i < permutations.length; i++) {
|
||||
permutations[i] = originalChange.getPermutation(i);
|
||||
}
|
||||
return permutations;
|
||||
} else {
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -56,7 +56,7 @@ unlocked.label.unmountFailed=Ejecting drive failed.
|
||||
unlocked.ioGraph.yAxis.label=Throughput (MiB/s)
|
||||
|
||||
# mac_warnings.fxml
|
||||
macWarnings.windowTitle=Danger - MAC authentication failed
|
||||
macWarnings.windowTitle=Danger - Corrupted file in %s
|
||||
macWarnings.message=Cryptomator detected potentially malicious corruptions in the following files:
|
||||
macWarnings.moreInformationButton=Learn more
|
||||
macWarnings.dismissButton=I promise to be careful
|
||||
|
||||
Reference in New Issue
Block a user