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
This commit is contained in:
Armin Schrenk
2021-08-26 21:10:39 +02:00
parent aef1bf821a
commit 460e6528bf
8 changed files with 97 additions and 68 deletions

View File

@@ -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> cryptoFileSystem;
private final VaultState state;
private final ObjectProperty<Exception> 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<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
Vault(VaultSettings vaultSettings, VaultConfigWrapper configWrapper, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> 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);
}

View File

@@ -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<VaultConfig.UnverifiedVaultConfig> 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);
}
}

View File

@@ -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;
};
}

View File

@@ -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<Scene> startScene();
@FxmlScene(FxmlFile.HEALTH_START_FAIL)
Lazy<Scene> 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) {}
}

View File

@@ -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<Masterkey> provideMasterkeyRef() {

View File

@@ -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<VaultConfig.UnverifiedVaultConfig> unverifiedVaultConfig;
private final Vault vault;
private final KeyLoadingStrategy keyLoadingStrategy;
private final ExecutorService executor;
private final AtomicReference<Masterkey> masterkeyRef;
@@ -44,11 +43,10 @@ public class StartController implements FxController {
private final Lazy<ErrorComponent.Builder> errorComponent;
@Inject
public StartController(@HealthCheckWindow Stage window, HealthCheckComponent.LoadUnverifiedConfigResult configLoadResult, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> errorComponent, @Named("unlockWindow") Stage unlockWindow) {
Preconditions.checkNotNull(configLoadResult.config());
public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> 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);

View File

@@ -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);
}

View File

@@ -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<String, Provider<KeyLoadingStrategy>> 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();
}
}