From 460e6528bfcde290323e705218893a9b49f3f39e Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 26 Aug 2021 21:10:39 +0200 Subject: [PATCH] Eagerly load vault config: * wrap (re-)loading in new class * assume existence of config in certain vault states * deprecate classes which are not used anymore --- .../org/cryptomator/common/vaults/Vault.java | 34 ++++++----- .../common/vaults/VaultConfigWrapper.java | 56 +++++++++++++++++++ .../common/vaults/VaultListManager.java | 14 ++++- .../ui/health/HealthCheckComponent.java | 14 +---- .../ui/health/HealthCheckModule.java | 13 ----- .../ui/health/StartController.java | 15 ++--- .../ui/health/StartFailController.java | 6 +- .../ui/keyloading/KeyLoadingModule.java | 13 +---- 8 files changed, 97 insertions(+), 68 deletions(-) create mode 100644 src/main/java/org/cryptomator/common/vaults/VaultConfigWrapper.java diff --git a/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java index 74ac7dc40..4c2fd7896 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -17,9 +17,7 @@ import org.cryptomator.cryptofs.CryptoFileSystem; import org.cryptomator.cryptofs.CryptoFileSystemProperties; import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags; import org.cryptomator.cryptofs.CryptoFileSystemProvider; -import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfig.UnverifiedVaultConfig; -import org.cryptomator.cryptofs.VaultConfigLoadException; import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.MasterkeyLoader; @@ -38,8 +36,6 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumSet; @@ -62,6 +58,7 @@ public class Vault { private final AtomicReference cryptoFileSystem; private final VaultState state; private final ObjectProperty lastKnownException; + private final VaultConfigWrapper configWrapper; private final VaultStats stats; private final StringBinding displayName; private final StringBinding displayablePath; @@ -78,8 +75,9 @@ public class Vault { private volatile Volume volume; @Inject - Vault(VaultSettings vaultSettings, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) { + Vault(VaultSettings vaultSettings, VaultConfigWrapper configWrapper, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) { this.vaultSettings = vaultSettings; + this.configWrapper = configWrapper; this.volumeProvider = volumeProvider; this.defaultMountFlags = defaultMountFlags; this.cryptoFileSystem = cryptoFileSystem; @@ -195,6 +193,10 @@ public class Vault { volume.reveal(vaultRevealer); } + public void reloadConfig() throws IOException { + configWrapper.reloadConfig(); + } + // ****************************************************************************** // Observable Properties // ******************************************************************************* @@ -328,19 +330,6 @@ public class Vault { return stats; } - /** - * Attempts to read the vault config file and parse it without verifying its integrity. - * - * @return an unverified vault config - * @throws VaultConfigLoadException if the read file cannot be properly parsed - * @throws IOException if reading the file fails - * - */ - public UnverifiedVaultConfig getUnverifiedVaultConfig() throws IOException { - Path configPath = getPath().resolve(org.cryptomator.common.Constants.VAULTCONFIG_FILENAME); - String token = Files.readString(configPath, StandardCharsets.US_ASCII); - return VaultConfig.decode(token); - } public Observable[] observables() { return new Observable[]{state}; @@ -375,6 +364,15 @@ public class Vault { } } + public UnverifiedVaultConfig getUnverifiedVaultConfig() { + try { + return configWrapper.getConfig(); + } catch (IOException e) { + throw new IllegalStateException("One should not accquire the config if thee is not present."); + } + + } + public void setCustomMountFlags(String mountFlags) { vaultSettings.mountFlags().set(mountFlags); } diff --git a/src/main/java/org/cryptomator/common/vaults/VaultConfigWrapper.java b/src/main/java/org/cryptomator/common/vaults/VaultConfigWrapper.java new file mode 100644 index 000000000..b6f767e23 --- /dev/null +++ b/src/main/java/org/cryptomator/common/vaults/VaultConfigWrapper.java @@ -0,0 +1,56 @@ +package org.cryptomator.common.vaults; + +import org.cryptomator.common.Constants; +import org.cryptomator.common.settings.VaultSettings; +import org.cryptomator.cryptofs.VaultConfig; +import org.cryptomator.cryptofs.VaultConfigLoadException; + +import javax.inject.Inject; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +/** + * Wrapper for lazy loading and on-demand reloading of the vault configuration. + */ +@PerVault +public class VaultConfigWrapper { + + private final VaultSettings settings; + private final ObjectProperty config; + + @Inject + VaultConfigWrapper(VaultSettings settings) { + this.settings = settings; + this.config = new SimpleObjectProperty<>(); + } + + void reloadConfig() throws IOException { + config.set(readConfigFromStorage(this.settings.path().get())); + } + + VaultConfig.UnverifiedVaultConfig getConfig() throws IOException { + if (Objects.isNull(config.get())) { + reloadConfig(); + } + return config.get(); + } + + + /** + * Attempts to read the vault config file and parse it without verifying its integrity. + * + * @throws VaultConfigLoadException if the read file cannot be properly parsed + * @throws IOException if reading the file fails + */ + static VaultConfig.UnverifiedVaultConfig readConfigFromStorage(Path vaultPath) throws IOException { + Path configPath = vaultPath.resolve(Constants.VAULTCONFIG_FILENAME); + String token = Files.readString(configPath, StandardCharsets.US_ASCII); + return VaultConfig.decode(token); + } + +} diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 05ea8ce07..53374d929 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -12,13 +12,13 @@ import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.DirStructure; +import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.migration.Migrators; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; -import javafx.collections.FXCollections; import javafx.collections.ObservableList; import java.io.IOException; import java.nio.file.Files; @@ -31,6 +31,7 @@ import java.util.ResourceBundle; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME; import static org.cryptomator.common.vaults.VaultState.Value.ERROR; +import static org.cryptomator.common.vaults.VaultState.Value.LOCKED; @Singleton public class VaultListManager { @@ -96,6 +97,10 @@ public class VaultListManager { VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings); try { VaultState.Value vaultState = determineVaultState(vaultSettings.path().get()); + if (vaultState == LOCKED) { + //TODO: maybe already set it in the vault Wrapper ? + VaultConfig.UnverifiedVaultConfig config = VaultConfigWrapper.readConfigFromStorage(vaultSettings.path().get()); + } compBuilder.initialVaultState(vaultState); } catch (IOException e) { LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e); @@ -112,6 +117,9 @@ public class VaultListManager { case LOCKED, NEEDS_MIGRATION, MISSING -> { try { var determinedState = determineVaultState(vault.getPath()); + if (determinedState == LOCKED) { + vault.reloadConfig(); + } state.set(determinedState); yield determinedState; } catch (IOException e) { @@ -132,7 +140,9 @@ public class VaultListManager { return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) { case VAULT -> VaultState.Value.LOCKED; case UNRELATED -> VaultState.Value.MISSING; - case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? VaultState.Value.NEEDS_MIGRATION : VaultState.Value.MISSING; + case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? // + VaultState.Value.NEEDS_MIGRATION // + : VaultState.Value.MISSING; }; } diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java b/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java index f78e815c6..aa69e0828 100644 --- a/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java +++ b/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java @@ -16,26 +16,15 @@ import javafx.stage.Stage; @Subcomponent(modules = {HealthCheckModule.class}) public interface HealthCheckComponent { - LoadUnverifiedConfigResult loadConfig(); - @HealthCheckWindow Stage window(); @FxmlScene(FxmlFile.HEALTH_START) Lazy startScene(); - @FxmlScene(FxmlFile.HEALTH_START_FAIL) - Lazy failScene(); - default Stage showHealthCheckWindow() { Stage stage = window(); - // TODO reevaluate config loading, as soon as we have the new generic error screen - var unverifiedConf = loadConfig(); - if (unverifiedConf.config() != null) { - stage.setScene(startScene().get()); - } else { - stage.setScene(failScene().get()); - } + stage.setScene(startScene().get()); stage.show(); return stage; } @@ -52,5 +41,4 @@ public interface HealthCheckComponent { HealthCheckComponent build(); } - record LoadUnverifiedConfigResult(VaultConfig.UnverifiedVaultConfig config, Throwable error) {} } diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java index ad5ac6156..a98807d82 100644 --- a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java +++ b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java @@ -27,7 +27,6 @@ import javafx.scene.Scene; import javafx.stage.Modality; import javafx.stage.Stage; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -37,18 +36,6 @@ import java.util.concurrent.atomic.AtomicReference; @Module(subcomponents = {KeyLoadingComponent.class}) abstract class HealthCheckModule { - // TODO reevaluate config loading, as soon as we have the new generic error screen - @Provides - @HealthCheckScoped - static HealthCheckComponent.LoadUnverifiedConfigResult provideLoadConfigResult(@HealthCheckWindow Vault vault) { - try { - return new HealthCheckComponent.LoadUnverifiedConfigResult(vault.getUnverifiedVaultConfig(), null); - } catch (IOException e) { - return new HealthCheckComponent.LoadUnverifiedConfigResult(null, e); - } - } - - @Provides @HealthCheckScoped static AtomicReference provideMasterkeyRef() { diff --git a/src/main/java/org/cryptomator/ui/health/StartController.java b/src/main/java/org/cryptomator/ui/health/StartController.java index ebba001b5..4b57ba7b8 100644 --- a/src/main/java/org/cryptomator/ui/health/StartController.java +++ b/src/main/java/org/cryptomator/ui/health/StartController.java @@ -1,7 +1,7 @@ package org.cryptomator.ui.health; -import com.google.common.base.Preconditions; import dagger.Lazy; +import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfigLoadException; import org.cryptomator.cryptofs.VaultKeyInvalidException; @@ -18,11 +18,10 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javafx.application.Platform; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; import javafx.fxml.FXML; import javafx.scene.Scene; import javafx.stage.Stage; +import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; @@ -35,7 +34,7 @@ public class StartController implements FxController { private final Stage window; private final Stage unlockWindow; - private final ObjectProperty unverifiedVaultConfig; + private final Vault vault; private final KeyLoadingStrategy keyLoadingStrategy; private final ExecutorService executor; private final AtomicReference masterkeyRef; @@ -44,11 +43,10 @@ public class StartController implements FxController { private final Lazy errorComponent; @Inject - public StartController(@HealthCheckWindow Stage window, HealthCheckComponent.LoadUnverifiedConfigResult configLoadResult, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy checkScene, Lazy errorComponent, @Named("unlockWindow") Stage unlockWindow) { - Preconditions.checkNotNull(configLoadResult.config()); + public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy checkScene, Lazy errorComponent, @Named("unlockWindow") Stage unlockWindow) { this.window = window; this.unlockWindow = unlockWindow; - this.unverifiedVaultConfig = new SimpleObjectProperty<>(configLoadResult.config()); + this.vault = vault; this.keyLoadingStrategy = keyLoadingStrategy; this.executor = executor; this.masterkeyRef = masterkeyRef; @@ -71,7 +69,6 @@ public class StartController implements FxController { private void loadKey() { assert !Platform.isFxApplicationThread(); - assert unverifiedVaultConfig.get() != null; try { keyLoadingStrategy.use(this::verifyVaultConfig); } catch (VaultConfigLoadException | UnlockCancelledException e) { @@ -80,7 +77,7 @@ public class StartController implements FxController { } private void verifyVaultConfig(KeyLoadingStrategy keyLoadingStrategy) throws VaultConfigLoadException { - var unverifiedCfg = unverifiedVaultConfig.get(); + var unverifiedCfg = vault.getUnverifiedVaultConfig(); try (var masterkey = keyLoadingStrategy.loadKey(unverifiedCfg.getKeyId())) { var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion()); vaultConfigRef.set(verifiedCfg); diff --git a/src/main/java/org/cryptomator/ui/health/StartFailController.java b/src/main/java/org/cryptomator/ui/health/StartFailController.java index 826766026..80b97bb44 100644 --- a/src/main/java/org/cryptomator/ui/health/StartFailController.java +++ b/src/main/java/org/cryptomator/ui/health/StartFailController.java @@ -18,6 +18,7 @@ import java.nio.charset.StandardCharsets; // TODO reevaluate config loading, as soon as we have the new generic error screen @HealthCheckScoped +@Deprecated public class StartFailController implements FxController { private final Stage window; @@ -28,10 +29,9 @@ public class StartFailController implements FxController { public TitledPane moreInfoPane; @Inject - public StartFailController(@HealthCheckWindow Stage window, HealthCheckComponent.LoadUnverifiedConfigResult configLoadResult) { - Preconditions.checkNotNull(configLoadResult.error()); + public StartFailController(@HealthCheckWindow Stage window) { this.window = window; - this.loadError = new SimpleObjectProperty<>(configLoadResult.error()); + this.loadError = new SimpleObjectProperty<>(new IllegalStateException("This class is not reachable anymore")); this.moreInfoIcon = new SimpleObjectProperty<>(FontAwesome5Icon.CARET_RIGHT); } diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java index f7eb8922f..bc6a911dd 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java @@ -3,7 +3,6 @@ package org.cryptomator.ui.keyloading; import dagger.Module; import dagger.Provides; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.cryptofs.VaultConfig.UnverifiedVaultConfig; import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlLoaderFactory; @@ -11,9 +10,7 @@ import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule; import javax.inject.Provider; import java.io.IOException; -import java.net.URI; import java.util.Map; -import java.util.Optional; import java.util.ResourceBundle; @Module(includes = {MasterkeyFileLoadingModule.class}) @@ -30,13 +27,9 @@ abstract class KeyLoadingModule { @KeyLoading @KeyLoadingScoped static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Vault vault, Map> strategies) { - try { - String scheme = vault.getUnverifiedVaultConfig().getKeyId().getScheme(); - var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme)); - return strategies.getOrDefault(scheme, () -> fallback).get(); - } catch (IOException e) { - return KeyLoadingStrategy.failed(e); - } + String scheme = vault.getUnverifiedVaultConfig().getKeyId().getScheme(); + var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme)); + return strategies.getOrDefault(scheme, () -> fallback).get(); } }