diff --git a/main/pom.xml b/main/pom.xml index ddd4262ec..02223a389 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -25,7 +25,7 @@ 16 - 2.0.0-beta5 + 2.1.0-beta1 1.0.0-beta2 1.0.0-beta2 1.0.0-beta2 diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java index 625c95acb..a1773d7af 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -12,6 +12,7 @@ public enum FxmlFile { ERROR("/fxml/error.fxml"), // FORGET_PASSWORD("/fxml/forget_password.fxml"), // HEALTH_START("/fxml/health_start.fxml"), // + HEALTH_CHECK("/fxml/health_check.fxml"), // LOCK_FORCED("/fxml/lock_forced.fxml"), // LOCK_FAILED("/fxml/lock_failed.fxml"), // MAIN_WINDOW("/fxml/main_window.fxml"), // diff --git a/main/ui/src/main/java/org/cryptomator/ui/health/CheckController.java b/main/ui/src/main/java/org/cryptomator/ui/health/CheckController.java new file mode 100644 index 000000000..77b137494 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/health/CheckController.java @@ -0,0 +1,59 @@ +package org.cryptomator.ui.health; + +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.cryptofs.VaultConfig; +import org.cryptomator.cryptofs.health.api.DiagnosticResult; +import org.cryptomator.cryptofs.health.api.HealthCheck; +import org.cryptomator.cryptolib.api.Masterkey; +import org.cryptomator.ui.common.FxController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javafx.fxml.FXML; +import javafx.stage.Stage; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +@HealthCheckScoped +public class CheckController implements FxController { + + private static final Logger LOG = LoggerFactory.getLogger(CheckController.class); + + private final Vault vault; + private final Stage window; + private final Masterkey masterkey; + private final VaultConfig vaultConfig; + private final SecureRandom csprng; + + @Inject + public CheckController(@HealthCheckWindow Vault vault, @HealthCheckWindow Stage window, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, SecureRandom csprng) { + this.vault = vault; + this.window = window; + this.masterkey = Objects.requireNonNull(masterkeyRef.get()); + this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get()); + this.csprng = csprng; + } + + @FXML + public void runCheck() { + // TODO run in background task... + try (var cryptor = vaultConfig.getCipherCombo().getCryptorProvider(csprng).withKey(masterkey)) { + List results = new ArrayList<>(); + HealthCheck.allChecks().forEach(c -> { + LOG.info("Running check {}...", c.identifier()); + results.addAll(c.check(vault.getPath(), vaultConfig, masterkey, cryptor)); + }); + results.forEach(r -> { + LOG.info("Result: {}", r); + }); + } + } + + public VaultConfig getVaultConfig() { + return vaultConfig; + } +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java b/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java index c5414ab9a..0a17678fc 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java @@ -5,6 +5,8 @@ import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; import org.cryptomator.common.vaults.Vault; +import org.cryptomator.cryptofs.VaultConfig; +import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxControllerKey; @@ -17,15 +19,32 @@ import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.mainwindow.MainWindow; import javax.inject.Provider; +import javafx.beans.Observable; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.scene.Scene; import javafx.stage.Modality; import javafx.stage.Stage; import java.util.Map; +import java.util.Optional; import java.util.ResourceBundle; +import java.util.concurrent.atomic.AtomicReference; @Module(subcomponents = {KeyLoadingComponent.class}) abstract class HealthCheckModule { + @Provides + @HealthCheckScoped + static AtomicReference provideMasterkeyRef() { + return new AtomicReference<>(); + } + + @Provides + @HealthCheckScoped + static AtomicReference provideVaultConfigRef() { + return new AtomicReference<>(); + } + @Provides @HealthCheckWindow @HealthCheckScoped @@ -43,25 +62,48 @@ abstract class HealthCheckModule { @Provides @HealthCheckWindow @HealthCheckScoped - static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) { + static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle, ChangeListener showingListener) { Stage stage = factory.create(); stage.setTitle(resourceBundle.getString("health.title")); stage.setResizable(false); stage.initModality(Modality.WINDOW_MODAL); stage.initOwner(owner); + stage.showingProperty().addListener(showingListener); return stage; } + @Provides + @HealthCheckScoped + static ChangeListener provideWindowShowingChangeListener(AtomicReference masterkey) { + return (observable, wasShowing, isShowing) -> { + if (!isShowing) { + Optional.ofNullable(masterkey.getAndSet(null)).ifPresent(Masterkey::destroy); + } + }; + } + @Provides @FxmlScene(FxmlFile.HEALTH_START) @HealthCheckScoped - static Scene provideUnlockSelectMasterkeyFileScene(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) { + static Scene provideHealthStartScene(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) { return fxmlLoaders.createScene(FxmlFile.HEALTH_START); } + @Provides + @FxmlScene(FxmlFile.HEALTH_CHECK) + @HealthCheckScoped + static Scene provideHealthCheckScene(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) { + return fxmlLoaders.createScene(FxmlFile.HEALTH_CHECK); + } + @Binds @IntoMap @FxControllerKey(StartController.class) abstract FxController bindStartController(StartController controller); + @Binds + @IntoMap + @FxControllerKey(CheckController.class) + abstract FxController bindCheckController(CheckController controller); + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/health/StartController.java b/main/ui/src/main/java/org/cryptomator/ui/health/StartController.java index 6d9c51b4b..848be8a9d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/health/StartController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/health/StartController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.health; +import dagger.Lazy; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfigLoadException; @@ -7,6 +8,8 @@ import org.cryptomator.cryptofs.VaultKeyInvalidException; import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.unlock.UnlockCancelledException; @@ -16,10 +19,12 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.application.Platform; import javafx.fxml.FXML; +import javafx.scene.Scene; import javafx.stage.Stage; import java.net.URI; import java.util.Optional; import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicReference; @HealthCheckScoped public class StartController implements FxController { @@ -30,13 +35,19 @@ public class StartController implements FxController { private final Optional unverifiedVaultConfig; private final KeyLoadingStrategy keyLoadingStrategy; private final ExecutorService executor; + private final AtomicReference masterkeyRef; + private final AtomicReference vaultConfigRef; + private final Lazy checkScene; @Inject - public StartController(@HealthCheckWindow Vault vault, @HealthCheckWindow Stage window, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor) { + public StartController(@HealthCheckWindow Vault vault, @HealthCheckWindow Stage window, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK) Lazy checkScene) { this.window = window; this.unverifiedVaultConfig = vault.getUnverifiedVaultConfig(); this.keyLoadingStrategy = keyLoadingStrategy; this.executor = executor; + this.masterkeyRef = masterkeyRef; + this.vaultConfigRef = vaultConfigRef; + this.checkScene = checkScene; } @FXML @@ -53,9 +64,16 @@ public class StartController implements FxController { private void loadKey() { assert !Platform.isFxApplicationThread(); + assert unverifiedVaultConfig.isPresent(); try (var masterkey = keyLoadingStrategy.masterkeyLoader().loadKey(unverifiedVaultConfig.orElseThrow().getKeyId())) { - var clone = masterkey.clone(); // original key will get destroyed - Platform.runLater(() -> loadedKey(clone)); + var unverifiedCfg = unverifiedVaultConfig.get(); + var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion()); + vaultConfigRef.set(verifiedCfg); + var old = masterkeyRef.getAndSet(masterkey.clone()); + if (old != null) { + old.destroy(); + } + Platform.runLater(this::loadedKey); } catch (MasterkeyLoadingFailedException e) { if (keyLoadingStrategy.recoverFromException(e)) { // retry @@ -63,29 +81,24 @@ public class StartController implements FxController { } else { Platform.runLater(() -> loadingKeyFailed(e)); } - } - } - - private void loadedKey(Masterkey masterkey) { - assert unverifiedVaultConfig.isPresent(); - var unverifiedCfg = unverifiedVaultConfig.get(); - try { - var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion()); - LOG.info("Verified vault config with cipher {}", verifiedCfg.getCipherCombo()); } catch (VaultKeyInvalidException e) { - LOG.error("Invalid key"); - // TODO show error screen + Platform.runLater(() -> loadingKeyFailed(e)); } catch (VaultConfigLoadException e) { - LOG.error("Failed to verify vault config", e); - // TODO show error screen - } finally { - masterkey.destroy(); + Platform.runLater(() -> loadingKeyFailed(e)); } } - private void loadingKeyFailed(MasterkeyLoadingFailedException e) { + private void loadedKey() { + LOG.debug("Loaded valid key"); + window.setScene(checkScene.get()); + } + + private void loadingKeyFailed(Exception e) { if (e instanceof UnlockCancelledException) { // ok + } else if (e instanceof VaultKeyInvalidException) { + LOG.error("Invalid key"); + // TODO show error screen } else { LOG.error("Failed to load key.", e); // TODO show error screen diff --git a/main/ui/src/main/resources/fxml/health_check.fxml b/main/ui/src/main/resources/fxml/health_check.fxml new file mode 100644 index 000000000..42942e01b --- /dev/null +++ b/main/ui/src/main/resources/fxml/health_check.fxml @@ -0,0 +1,27 @@ + + + + + + + + + + + + +