removed MASTERKEY_MISSING
new recoveryActionType RESTORE_ALL
This commit is contained in:
Jan-Peter Klein
2025-06-30 09:26:30 +02:00
parent 4280112eab
commit acf11da202
32 changed files with 468 additions and 175 deletions

View File

@@ -7,21 +7,17 @@ import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
import java.util.stream.Stream;
import org.cryptomator.common.vaults.VaultState.Value;
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
public final class BackupRestorer {
private BackupRestorer() {}
public static boolean restoreIfPresent(Path vaultPath, Value vaultState) {
Path targetFile = switch (vaultState) {
case VAULT_CONFIG_MISSING -> vaultPath.resolve("vault.cryptomator");
case MASTERKEY_MISSING -> vaultPath.resolve("masterkey.cryptomator");
default -> throw new IllegalArgumentException("Unexpected vault state: " + vaultState);
};
public static boolean restoreIfPresent(Path vaultPath, String fileName) {
Path targetFile = vaultPath.resolve(fileName);
try (Stream<Path> files = Files.list(vaultPath)) {
return files.filter(file -> isValidBackupFileForState(file.getFileName().toString(), vaultState))
return files.filter(file -> isValidBackupFileForState(file.getFileName().toString(), fileName))
.max((f1, f2) -> {
try {
FileTime time1 = Files.getLastModifiedTime(f1);
@@ -38,12 +34,8 @@ public final class BackupRestorer {
}
}
private static boolean isValidBackupFileForState(String fileName, Value vaultState) {
return switch (vaultState) {
case VAULT_CONFIG_MISSING -> fileName.startsWith("vault.cryptomator") && fileName.endsWith(".bkup");
case MASTERKEY_MISSING -> fileName.startsWith("masterkey.cryptomator") && fileName.endsWith(".bkup");
default -> false;
};
private static boolean isValidBackupFileForState(String fileName, String vaultState) {
return fileName.startsWith(vaultState) && fileName.endsWith(MASTERKEY_BACKUP_SUFFIX);
}
private static boolean copyBackupFile(Path backupFile, Path configPath) {

View File

@@ -1,8 +1,9 @@
package org.cryptomator.common.recovery;
public enum RecoveryActionType {
RESTORE_VAULT_CONFIG,
RESTORE_ALL,
RESTORE_MASTERKEY,
RESTORE_VAULT_CONFIG,
RESET_PASSWORD,
SHOW_KEY,
CONVERT_VAULT

View File

@@ -11,9 +11,6 @@ import java.util.Comparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
public final class RecoveryDirectory implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(RecoveryDirectory.class);
@@ -34,9 +31,8 @@ public final class RecoveryDirectory implements AutoCloseable {
return new RecoveryDirectory(vaultPath, tempDir);
}
public void moveRecoveredFiles() throws IOException {
Files.move(recoveryPath.resolve(MASTERKEY_FILENAME), vaultPath.resolve(MASTERKEY_FILENAME), StandardCopyOption.REPLACE_EXISTING);
Files.move(recoveryPath.resolve(VAULTCONFIG_FILENAME), vaultPath.resolve(VAULTCONFIG_FILENAME), StandardCopyOption.REPLACE_EXISTING);
public void moveRecoveredFile(String file) throws IOException {
Files.move(recoveryPath.resolve(file), vaultPath.resolve(file), StandardCopyOption.REPLACE_EXISTING);
}
private void deleteRecoveryDirectory() {

View File

@@ -23,7 +23,6 @@ import org.cryptomator.cryptofs.event.FilesystemEvent;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.event.VaultEvent;
import org.cryptomator.integrations.mount.MountFailedException;
import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.mount.UnmountFailedException;
@@ -35,7 +34,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
@@ -75,7 +73,6 @@ public class Vault {
private final BooleanBinding missing;
private final BooleanBinding needsMigration;
private final BooleanBinding unknownError;
private final BooleanBinding missingMasterkey;
private final BooleanBinding missingVaultConfig;
private final ObjectBinding<Mountpoint> mountPoint;
private final Mounter mounter;
@@ -105,7 +102,6 @@ public class Vault {
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
this.missing = Bindings.createBooleanBinding(this::isMissing, state);
this.missingMasterkey = Bindings.createBooleanBinding(this::isMissingMasterkey, state);
this.missingVaultConfig = Bindings.createBooleanBinding(this::isMissingVaultConfig, state);
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
@@ -340,20 +336,12 @@ public class Vault {
return state.get() == VaultState.Value.ERROR;
}
public BooleanBinding missingMasterkeyProperty() {
return missingMasterkey;
}
public boolean isMissingMasterkey() {
return state.get() == VaultState.Value.MASTERKEY_MISSING;
}
public BooleanBinding missingVaultConfigProperty() {
return missingVaultConfig;
}
public boolean isMissingVaultConfig() {
return state.get() == VaultState.Value.VAULT_CONFIG_MISSING;
return state.get() == VaultState.Value.VAULT_CONFIG_MISSING || state.get() == VaultState.Value.ALL_MISSING;
}
public ReadOnlyStringProperty displayNameProperty() {

View File

@@ -25,10 +25,11 @@ 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;
import static org.cryptomator.common.vaults.VaultState.Value.MASTERKEY_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.ALL_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
import static org.cryptomator.common.vaults.VaultState.Value.PROCESSING;
import static org.cryptomator.common.vaults.VaultState.Value.UNLOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
@@ -39,7 +40,6 @@ import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.cryptofs.migration.Migrators;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -146,7 +146,7 @@ public class VaultListManager {
vaultSettings.lastKnownKeyLoader.set(Constants.DEFAULT_KEY_ID.toString());
}
if (vaultState != VaultState.Value.VAULT_CONFIG_MISSING) {
if (vaultState != VAULT_CONFIG_MISSING) {
initializeLastKnownKeyLoaderIfPossible(vaultSettings, wrapper);
}
@@ -179,10 +179,6 @@ public class VaultListManager {
try {
VaultState.Value determined = determineVaultState(vault.getPath(), vault.getVaultSettings());
if (determined == MASTERKEY_MISSING && KeyLoadingStrategy.isHubVault(vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme())) {
determined = LOCKED;
}
if (determined == LOCKED) {
vault.getVaultConfigCache().reloadConfig();
}
@@ -205,24 +201,18 @@ public class VaultListManager {
return VaultState.Value.MISSING;
}
boolean vaultConfigRestored = Files.notExists(pathToVaultConfig)
&& BackupRestorer.restoreIfPresent(pathToVaultConfig.getParent(), VaultState.Value.VAULT_CONFIG_MISSING);
BackupRestorer.restoreIfPresent(pathToVaultConfig.getParent(), VAULTCONFIG_FILENAME);
boolean masterkeyRestored = Files.notExists(pathToMasterkey)
&& KeyLoadingStrategy.isMasterkeyFileVault(vaultSettings.lastKnownKeyLoader.get())
&& BackupRestorer.restoreIfPresent(pathToMasterkey.getParent(), VaultState.Value.MASTERKEY_MISSING);
BackupRestorer.restoreIfPresent(pathToMasterkey.getParent(), MASTERKEY_FILENAME);
if (vaultConfigRestored || masterkeyRestored) {
return LOCKED;
if (!Files.exists(pathToVaultConfig) && !Files.exists(pathToMasterkey)) {
return ALL_MISSING;
}
if (Files.notExists(pathToVaultConfig)) {
return VaultState.Value.VAULT_CONFIG_MISSING;
if (!Files.exists(pathToVaultConfig)) {
return VAULT_CONFIG_MISSING;
}
if (Files.notExists(pathToMasterkey) && KeyLoadingStrategy.isMasterkeyFileVault(vaultSettings.lastKnownKeyLoader.get())) {
return VaultState.Value.MASTERKEY_MISSING;
}
return checkDirStructure(pathToVault);
}

View File

@@ -31,9 +31,9 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
VAULT_CONFIG_MISSING,
/**
* No masterkey found at the provided path
* No vault config and masterkey found at the provided path
*/
MASTERKEY_MISSING,
ALL_MISSING,
/**
* Vault requires migration to a newer vault format

View File

@@ -18,13 +18,10 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.MASTERKEY_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils;
@@ -36,7 +33,6 @@ import org.cryptomator.common.vaults.VaultConfigCache;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
@@ -151,10 +147,11 @@ public class ChooseExistingVaultController implements FxController {
}
Vault preparedVault = prepareVault(selectedDirectory, vaultComponentFactory, mountServices);
VaultListManager.redetermineVaultState(preparedVault);
VaultState.Value state = preparedVault.getState();
switch (state) {
case VAULT_CONFIG_MISSING -> recoveryKeyWindow.create(preparedVault, window, RecoveryActionType.RESTORE_VAULT_CONFIG).showOnboardingDialogWindow();
case MASTERKEY_MISSING -> recoveryKeyWindow.create(preparedVault, window, RecoveryActionType.RESTORE_MASTERKEY).showOnboardingDialogWindow();
case VAULT_CONFIG_MISSING -> recoveryKeyWindow.create(preparedVault, window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_VAULT_CONFIG)).showOnboardingDialogWindow();
case ALL_MISSING -> recoveryKeyWindow.create(preparedVault, window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_ALL)).showOnboardingDialogWindow();
default -> {
vaultListManager.addVault(preparedVault);
vault.set(preparedVault);
@@ -174,7 +171,7 @@ public class ChooseExistingVaultController implements FxController {
}
var wrapper = new VaultConfigCache(vaultSettings);
Vault vault = vaultComponentFactory.create(vaultSettings, wrapper, VAULT_CONFIG_MISSING, null).vault();
Vault vault = vaultComponentFactory.create(vaultSettings, wrapper, LOCKED, null).vault();
try {
VaultListManager.determineVaultState(vault.getPath(), vaultSettings);
} catch (IOException e) {

View File

@@ -122,7 +122,7 @@ abstract class ConvertVaultModule {
@IntoMap
@FxControllerKey(RecoveryKeyValidateController.class)
static FxController bindRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory, RecoveryActionType.CONVERT_VAULT, null, null);
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory, new SimpleObjectProperty<>(RecoveryActionType.CONVERT_VAULT), null, null);
}
}

View File

@@ -15,15 +15,13 @@ import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.cryptomator.ui.sharevault.ShareVaultComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import javax.inject.Named;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.image.Image;
import java.io.IOException;
import java.io.InputStream;
@@ -40,7 +38,8 @@ import java.io.InputStream;
HealthCheckComponent.class, //
UpdateReminderComponent.class, //
ShareVaultComponent.class, //
EventViewComponent.class})
EventViewComponent.class, //
RecoveryKeyComponent.class})
abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException {

View File

@@ -1,14 +1,18 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
@@ -27,17 +31,39 @@ public class ChooseMasterkeyFileController implements FxController {
private final Stage window;
private final Vault vault;
private final CompletableFuture<Path> result;
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
private final ResourceBundle resourceBundle;
@FXML private CheckBox restoreInsteadCheckBox;
@FXML private Button chooseButton;
@Inject
public ChooseMasterkeyFileController(@KeyLoading Stage window, @KeyLoading Vault vault, CompletableFuture<Path> result, ResourceBundle resourceBundle) {
public ChooseMasterkeyFileController(@KeyLoading Stage window, //
@KeyLoading Vault vault, //
CompletableFuture<Path> result, //
RecoveryKeyComponent.Factory recoveryKeyWindow, //
ResourceBundle resourceBundle) {
this.window = window;
this.vault = vault;
this.result = result;
this.recoveryKeyWindow = recoveryKeyWindow;
this.resourceBundle = resourceBundle;
this.window.setOnHiding(this::windowClosed);
}
@FXML
private void initialize() {
restoreInsteadCheckBox.selectedProperty().addListener((_, _, newVal) -> {
if (newVal) {
chooseButton.setText(resourceBundle.getString("addvaultwizard.existing.restore"));
chooseButton.setOnAction(e -> restoreMasterkey());
} else {
chooseButton.setText(resourceBundle.getString("generic.button.choose"));
chooseButton.setOnAction(e -> proceed());
}
});
}
@FXML
public void cancel() {
window.close();
@@ -47,6 +73,12 @@ public class ChooseMasterkeyFileController implements FxController {
result.cancel(true);
}
@FXML
void restoreMasterkey() {
window.close();
recoveryKeyWindow.create(vault, window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_MASTERKEY)).showOnboardingDialogWindow();
}
@FXML
public void proceed() {
LOG.trace("proceed()");

View File

@@ -52,7 +52,7 @@ public class VaultDetailController implements FxController {
case LOCKED -> FontAwesome5Icon.LOCK;
case PROCESSING -> FontAwesome5Icon.SPINNER;
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
case NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, MASTERKEY_MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
case NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, ALL_MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
};
} else {
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;

View File

@@ -3,8 +3,10 @@ package org.cryptomator.ui.mainwindow;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
@@ -16,13 +18,17 @@ import javafx.fxml.FXML;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.nio.file.Files;
import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@MainWindowScoped
public class VaultDetailMissingVaultController implements FxController {
private final FxApplicationWindows appWindows;
private final Stage mainWindow;
private final ObjectProperty<Vault> vault;
private final ObservableList<Vault> vaults;
private final ResourceBundle resourceBundle;
@@ -31,12 +37,17 @@ public class VaultDetailMissingVaultController implements FxController {
private final Dialogs dialogs;
@Inject
public VaultDetailMissingVaultController(ObjectProperty<Vault> vault, //
public VaultDetailMissingVaultController(FxApplicationWindows appWindows, //
@MainWindow Stage mainWindow, //
ObjectProperty<Vault> vault, //
ObservableList<Vault> vaults, //
ResourceBundle resourceBundle, //
@MainWindow Stage window, //
Dialogs dialogs, //
RecoveryKeyComponent.Factory recoveryKeyWindow) {
this.appWindows = appWindows;
this.mainWindow = mainWindow;
this.vault = vault;
this.vaults = vaults;
this.resourceBundle = resourceBundle;
@@ -60,14 +71,23 @@ public class VaultDetailMissingVaultController implements FxController {
if(KeyLoadingStrategy.isHubVault(vault.get().getVaultSettings().lastKnownKeyLoader.get())){
dialogs.prepareContactHubAdmin(window).build().showAndWait();
}
else if(Files.exists(vault.get().getPath().resolve(MASTERKEY_FILENAME))){
recoveryKeyWindow.create(vault.get(), window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_VAULT_CONFIG)).showOnboardingDialogWindow();
}
else {
recoveryKeyWindow.create(vault.get(), window, RecoveryActionType.RESTORE_VAULT_CONFIG).showOnboardingDialogWindow();
recoveryKeyWindow.create(vault.get(), window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_ALL)).showOnboardingDialogWindow();
}
}
@FXML
void restoreMasterkey() {
recoveryKeyWindow.create(vault.get(), window, RecoveryActionType.RESTORE_MASTERKEY).showRecoveryKeyRecoverWindow();
recoveryKeyWindow.create(vault.get(), window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_MASTERKEY)).showRecoveryKeyRecoverWindow();
}
@FXML
public void unlock() {
vault.get().stateProperty().set(VaultState.Value.LOCKED);
appWindows.startUnlockWorkflow(vault.get(), mainWindow);
}
@FXML

View File

@@ -55,7 +55,7 @@ public class VaultListCellController implements FxController {
case LOCKED -> FontAwesome5Icon.LOCK;
case PROCESSING -> FontAwesome5Icon.SPINNER;
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
case NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, MASTERKEY_MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
case NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, ALL_MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
};
} else {
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;

View File

@@ -22,7 +22,7 @@ import java.util.Objects;
import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.MASTERKEY_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.ALL_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
import static org.cryptomator.common.vaults.VaultState.Value.UNLOCKED;
@@ -65,7 +65,7 @@ public class VaultListContextMenuController implements FxController {
this.selectedVaultState = selectedVault.flatMap(Vault::stateProperty).orElse(null);
this.selectedVaultPassphraseStored = selectedVault.map(this::isPasswordStored).orElse(false);
this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION, MASTERKEY_MISSING, VAULT_CONFIG_MISSING)::contains).orElse(false);
this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION, ALL_MISSING, VAULT_CONFIG_MISSING)::contains).orElse(false);
this.selectedVaultUnlockable = selectedVaultState.map(LOCKED::equals).orElse(false);
this.selectedVaultLockable = selectedVaultState.map(UNLOCKED::equals).orElse(false);
}

View File

@@ -9,6 +9,7 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Named;
import javafx.beans.property.ObjectProperty;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -54,7 +55,7 @@ public interface RecoveryKeyComponent {
RecoveryKeyComponent create(@BindsInstance @RecoveryKeyWindow Vault vault, //
@BindsInstance @Named("keyRecoveryOwner") Stage owner, //
@BindsInstance @Named("recoverType") RecoveryActionType recoverType);
@BindsInstance @Named("recoverType") ObjectProperty<RecoveryActionType> recoverType);
}
}

View File

@@ -1,28 +1,45 @@
package org.cryptomator.ui.recoverykey;
import dagger.Lazy;
import org.cryptomator.common.recovery.CryptoFsInitializer;
import org.cryptomator.common.recovery.MasterkeyService;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.recovery.RecoveryDirectory;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.controls.FormattedLabel;
import org.cryptomator.ui.controls.NiceSecurePasswordField;
import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
@RecoveryKeyScoped
public class RecoveryKeyCreationController implements FxController {
@@ -30,23 +47,74 @@ public class RecoveryKeyCreationController implements FxController {
private final Stage window;
private final Lazy<Scene> successScene;
private final Lazy<Scene> recoverykeyExpertSettingsScene;
private final MasterkeyFileAccess masterkeyFileAccess;
private final Vault vault;
private final ExecutorService executor;
private final RecoveryKeyFactory recoveryKeyFactory;
private final StringProperty recoveryKeyProperty;
private final FxApplicationWindows appWindows;
public NiceSecurePasswordField passwordField;
private final IntegerProperty shorteningThreshold;
private final ObjectProperty<RecoveryActionType> recoverType;
private final ResourceBundle resourceBundle;
public FormattedLabel descriptionLabel;
public Button cancelButton;
public Button nextButton;
private final VaultListManager vaultListManager;
private final Dialogs dialogs;
private final Stage owner;
@Inject
public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, FxApplicationWindows appWindows, ResourceBundle resourceBundle) {
public RecoveryKeyCreationController(FxApplicationWindows appWindows, //
@RecoveryKeyWindow Stage window, //
@Named("keyRecoveryOwner") Stage owner, //
@FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> recoverykeyExpertSettingsScene, //
@RecoveryKeyWindow Vault vault, //
RecoveryKeyFactory recoveryKeyFactory, //
MasterkeyFileAccess masterkeyFileAccess, //
ExecutorService executor, //
@RecoveryKeyWindow StringProperty recoveryKey, //
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
VaultListManager vaultListManager, //
ResourceBundle resourceBundle, //
Dialogs dialogs) {
this.window = window;
window.setTitle(resourceBundle.getString("recoveryKey.display.title"));
this.successScene = successScene;
this.recoverykeyExpertSettingsScene = recoverykeyExpertSettingsScene;
this.vault = vault;
this.executor = executor;
this.recoveryKeyFactory = recoveryKeyFactory;
this.recoveryKeyProperty = recoveryKey;
this.appWindows = appWindows;
this.recoverType = recoverType;
this.resourceBundle = resourceBundle;
this.masterkeyFileAccess = masterkeyFileAccess;
this.shorteningThreshold = shorteningThreshold;
this.vaultListManager = vaultListManager;
this.owner = owner;
this.dialogs = dialogs;
}
@FXML
public void initialize() {
if (recoverType.get().equals(RecoveryActionType.SHOW_KEY)) {
window.setTitle(resourceBundle.getString("recoveryKey.display.title"));
} else if (recoverType.get().equals(RecoveryActionType.RESTORE_VAULT_CONFIG)) {
window.setTitle(resourceBundle.getString("recoveryKey.recoverVaultConfig.title"));
descriptionLabel.formatProperty().set(resourceBundle.getString("recoveryKey.recover.description"));
cancelButton.setOnAction((_) -> back());
cancelButton.setText(resourceBundle.getString("generic.button.back"));
nextButton.setOnAction((_) -> restoreWithPassword());
}
}
@FXML
public void back() {
window.setScene(recoverykeyExpertSettingsScene.get());
window.centerOnScreen();
}
@FXML
@@ -71,6 +139,40 @@ public class RecoveryKeyCreationController implements FxController {
executor.submit(task);
}
@FXML
public void restoreWithPassword() {
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
Path recoveryPath = recoveryDirectory.getRecoveryPath();
Path masterkeyFilePath = vault.getPath().resolve(MASTERKEY_FILENAME);
try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, masterkeyFilePath, passwordField.getCharacters())) {
var combo = MasterkeyService.detect(masterkey.getEncoded(), vault.getPath());
CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), combo.get());
}
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
if (!vaultListManager.containsVault(vault.getPath())) {
vaultListManager.add(vault.getPath());
}
window.close();
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle) //
.setTitleKey("recoveryKey.recoverVaultConfig.title") //
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") //
.build().showAndWait();
} catch (InvalidPassphraseException e) {
LOG.info("Password invalid", e);
Animations.createShakeWindowAnimation(window).play();
} catch (IOException | CryptoException e) {
LOG.error("Recovery process failed", e);
appWindows.showErrorWindow(e, window, null);
}
}
@FXML
public void close() {
window.close();

View File

@@ -6,17 +6,23 @@ import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
import dagger.Lazy;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.addvaultwizard.CreateNewVaultExpertSettingsController;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.controls.NumericTextField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RecoveryKeyScoped
public class RecoveryKeyExpertSettingsController implements FxController {
@@ -27,28 +33,40 @@ public class RecoveryKeyExpertSettingsController implements FxController {
private final Stage window;
private final Lazy<Application> application;
private final Lazy<Scene> resetPasswordScene;
private final Lazy<Scene> recoverScene;
public CheckBox expertSettingsCheckBox;
public NumericTextField shorteningThresholdTextField;
private final Vault vault;
private final ObjectProperty<RecoveryActionType> recoverType;
private final IntegerProperty shorteningThreshold;
private final Lazy<Scene> resetPasswordScene;
private final Lazy<Scene> createScene;
private final Lazy<Scene> onBoardingScene;
private final Lazy<Scene> recoverScene;
private final BooleanBinding validShorteningThreshold;
@FXML public CheckBox expertSettingsCheckBox;
@FXML public NumericTextField shorteningThresholdTextField;
private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyExpertSettingsController.class);
@Inject
public RecoveryKeyExpertSettingsController(@RecoveryKeyWindow Stage window, //
Lazy<Application> application, //
@RecoveryKeyWindow Vault vault, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
@FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_CREATE) Lazy<Scene> createScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_ONBOARDING) Lazy<Scene> onBoardingScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverScene) {
this.window = window;
this.application = application;
this.resetPasswordScene = resetPasswordScene;
this.recoverScene = recoverScene;
this.vault = vault;
this.recoverType = recoverType;
this.shorteningThreshold = shorteningThreshold;
this.resetPasswordScene = resetPasswordScene;
this.createScene = createScene;
this.onBoardingScene = onBoardingScene;
this.recoverScene = recoverScene;
this.validShorteningThreshold = Bindings.createBooleanBinding(this::isValidShorteningThreshold, shorteningThreshold);
}
@FXML
@@ -87,11 +105,19 @@ public class RecoveryKeyExpertSettingsController implements FxController {
@FXML
public void back() {
window.setScene(recoverScene.get());
if(recoverType.get().equals(RecoveryActionType.RESTORE_ALL) && vault.getState().equals(VaultState.Value.VAULT_CONFIG_MISSING))
window.setScene(recoverScene.get());
else if(recoverType.get().equals(RecoveryActionType.RESTORE_ALL) && vault.getState().equals(VaultState.Value.ALL_MISSING))
window.setScene(recoverScene.get());
else if(recoverType.get().equals(RecoveryActionType.RESTORE_VAULT_CONFIG))
window.setScene(onBoardingScene.get());
}
@FXML
public void next() {
window.setScene(resetPasswordScene.get());
if(recoverType.get().equals(RecoveryActionType.RESTORE_VAULT_CONFIG))
window.setScene(createScene.get());
else
window.setScene(resetPasswordScene.get());
}
}

View File

@@ -176,7 +176,7 @@ abstract class RecoveryKeyModule {
@Provides
@IntoMap
@FxControllerKey(RecoveryKeyValidateController.class)
static FxController bindRecoveryKeyValidateController(@RecoveryKeyWindow Vault vault, @RecoveryKeyWindow @Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, @RecoveryKeyWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, @Named("recoverType") RecoveryActionType recoverType, @Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo, @Nullable MasterkeyFileAccess masterkeyFileAccess) {
static FxController bindRecoveryKeyValidateController(@RecoveryKeyWindow Vault vault, @RecoveryKeyWindow @Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, @RecoveryKeyWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, @Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, @Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo, @Nullable MasterkeyFileAccess masterkeyFileAccess) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory, recoverType, cipherCombo, masterkeyFileAccess);
}

View File

@@ -1,38 +1,119 @@
package org.cryptomator.ui.recoverykey;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.ResourceBundle;
import dagger.Lazy;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ResourceBundle;
import static org.cryptomator.common.recovery.RecoveryActionType.RESTORE_ALL;
import static org.cryptomator.common.recovery.RecoveryActionType.RESTORE_VAULT_CONFIG;
@RecoveryKeyScoped
public class RecoveryKeyOnboardingController implements FxController {
private final Stage window;
private final Lazy<Scene> recoverykeyRecoverScene;
private RecoveryActionType recoverType;
private final Lazy<Scene> recoverykeyExpertSettingsScene;
private ObjectProperty<RecoveryActionType> recoverType;
public Label titleLabel;
public Label messageLabel;
public Label secondTextDesc;
public Label thirdTextIndex;
public Label thirdTextDesc;
@FXML
private CheckBox affirmationBox;
@FXML
private RadioButton recoveryKeyRadio;
@FXML
private RadioButton passwordRadio;
@FXML
private Button nextButton;
@FXML
private VBox chooseMethodeBox;
private final ToggleGroup methodToggleGroup = new ToggleGroup();
@Inject
public RecoveryKeyOnboardingController(@RecoveryKeyWindow Stage window, //
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverykeyRecoverScene, //
@Named("recoverType") RecoveryActionType recoverType, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> recoverykeyExpertSettingsScene, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
ResourceBundle resourceBundle) {
this.window = window;
window.setTitle(resourceBundle.getString("recoveryKey.recoverVaultConfig.title"));
this.recoverykeyRecoverScene = recoverykeyRecoverScene;
this.recoverykeyExpertSettingsScene = recoverykeyExpertSettingsScene;
this.recoverType = recoverType;
}
@FXML
public void initialize() {
recoveryKeyRadio.setToggleGroup(methodToggleGroup);
passwordRadio.setToggleGroup(methodToggleGroup);
boolean showMethodSelection = (recoverType.get() == RecoveryActionType.RESTORE_VAULT_CONFIG);
chooseMethodeBox.setVisible(showMethodSelection);
chooseMethodeBox.setManaged(showMethodSelection);
nextButton.disableProperty().bind( //
affirmationBox.selectedProperty().not() //
.or(methodToggleGroup.selectedToggleProperty().isNull() //
.and(showMethodSelectionProperty())));
switch (recoverType.get()) {
case RESTORE_VAULT_CONFIG -> {
window.setTitle("Recover Vault Config");
messageLabel.setText("Read this:");
secondTextDesc.setText("You will need the vault password or recovery key, a new password and possible some expert settings.");
thirdTextIndex.setVisible(false);
thirdTextIndex.setManaged(false);
thirdTextDesc.setVisible(false);
thirdTextDesc.setManaged(false);
}
case RESTORE_MASTERKEY -> {
window.setTitle("Recover Masterkey");
messageLabel.setText("Read this:");
titleLabel.setText("Recover Masterkey");
secondTextDesc.setText("You will need the vault recovery key.");
thirdTextIndex.setVisible(false);
thirdTextIndex.setManaged(false);
thirdTextDesc.setVisible(false);
thirdTextDesc.setManaged(false);
}
default -> {
thirdTextIndex.setVisible(true);
thirdTextIndex.setManaged(true);
thirdTextDesc.setVisible(true);
thirdTextDesc.setManaged(true);
}
}
}
private BooleanProperty showMethodSelectionProperty() {
return new SimpleBooleanProperty(recoverType.get() == RecoveryActionType.RESTORE_VAULT_CONFIG);
}
@FXML
public void close() {
window.close();
@@ -40,7 +121,21 @@ public class RecoveryKeyOnboardingController implements FxController {
@FXML
public void next() {
recoverType = RecoveryActionType.RESTORE_VAULT_CONFIG;
window.setScene(recoverykeyRecoverScene.get());
switch (recoverType.get()) {
case RESTORE_VAULT_CONFIG, RESTORE_ALL -> {
Object selectedToggle = methodToggleGroup.getSelectedToggle();
if (selectedToggle == recoveryKeyRadio) {
recoverType.set(RESTORE_ALL);
window.setScene(recoverykeyRecoverScene.get());
} else if (selectedToggle == passwordRadio) {
recoverType.set(RESTORE_VAULT_CONFIG);
window.setScene(recoverykeyExpertSettingsScene.get());
} else {
window.setScene(recoverykeyRecoverScene.get());
window.centerOnScreen();
}
}
case RESTORE_MASTERKEY -> window.setScene(recoverykeyRecoverScene.get());
}
}
}

View File

@@ -11,6 +11,7 @@ import javax.inject.Named;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import java.util.ResourceBundle;
@@ -19,6 +20,12 @@ public class RecoveryKeyRecoverController implements FxController {
private final Stage window;
private final Lazy<Scene> nextScene;
private final Lazy<Scene> onBoardingScene;
private final ResourceBundle resourceBundle;
public ObjectProperty<RecoveryActionType> recoverType;
@FXML
private Button cancelButton;
@FXML
RecoveryKeyValidateController recoveryKeyValidateController;
@@ -27,11 +34,15 @@ public class RecoveryKeyRecoverController implements FxController {
public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, //
@FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> expertSettingsScene, //
ResourceBundle resourceBundle, @Named("recoverType") RecoveryActionType recoverType) {
@FxmlScene(FxmlFile.RECOVERYKEY_ONBOARDING) Lazy<Scene> onBoardingScene, //
ResourceBundle resourceBundle, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType) {
this.window = window;
this.nextScene = switch (recoverType) {
case RESTORE_VAULT_CONFIG -> {
this.recoverType = recoverType;
this.onBoardingScene = onBoardingScene;
this.resourceBundle = resourceBundle;
this.nextScene = switch (recoverType.get()) {
case RESTORE_ALL, RESTORE_VAULT_CONFIG -> {
window.setTitle(resourceBundle.getString("recoveryKey.recoverVaultConfig.title"));
yield expertSettingsScene;
}
@@ -56,11 +67,18 @@ public class RecoveryKeyRecoverController implements FxController {
@FXML
public void initialize() {
switch (recoverType.get()) {
case RESTORE_ALL, RESTORE_VAULT_CONFIG -> cancelButton.setText(resourceBundle.getString("generic.button.back"));
case RESET_PASSWORD -> cancelButton.setText(resourceBundle.getString("generic.button.cancel"));
}
}
@FXML
public void close() {
window.close();
switch (recoverType.get()) {
case RESTORE_ALL, RESTORE_VAULT_CONFIG -> window.setScene(onBoardingScene.get());
case RESET_PASSWORD -> window.close();
}
}
@FXML

View File

@@ -1,24 +1,5 @@
package org.cryptomator.ui.recoverykey;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import dagger.Lazy;
import org.cryptomator.common.recovery.CryptoFsInitializer;
import org.cryptomator.common.recovery.MasterkeyService;
@@ -39,6 +20,27 @@ import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
@RecoveryKeyScoped
public class RecoveryKeyResetPasswordController implements FxController {
@@ -50,11 +52,12 @@ public class RecoveryKeyResetPasswordController implements FxController {
private final ExecutorService executor;
private final StringProperty recoveryKey;
private final Lazy<Scene> recoverExpertSettingsScene;
private final Lazy<Scene> recoverykeyRecoverScene;
private final FxApplicationWindows appWindows;
private final MasterkeyFileAccess masterkeyFileAccess;
private final VaultListManager vaultListManager;
private final IntegerProperty shorteningThreshold;
private final RecoveryActionType recoverType;
private final ObjectProperty<RecoveryActionType> recoverType;
private final ObjectProperty<CryptorProvider.Scheme> cipherCombo;
private final ResourceBundle resourceBundle;
private final StringProperty buttonText = new SimpleStringProperty();
@@ -62,6 +65,8 @@ public class RecoveryKeyResetPasswordController implements FxController {
private final Stage owner;
public NewPasswordController newPasswordController;
public Button backButton;
public Button nextButton;
@Inject
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, //
@@ -70,11 +75,12 @@ public class RecoveryKeyResetPasswordController implements FxController {
ExecutorService executor, //
@Named("keyRecoveryOwner") Stage owner, @RecoveryKeyWindow StringProperty recoveryKey, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> recoverExpertSettingsScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverykeyRecoverScene, //
FxApplicationWindows appWindows, //
MasterkeyFileAccess masterkeyFileAccess, //
VaultListManager vaultListManager, //
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
@Named("recoverType") RecoveryActionType recoverType, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
@Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo,//
ResourceBundle resourceBundle, Dialogs dialogs) {
this.window = window;
@@ -83,6 +89,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
this.executor = executor;
this.recoveryKey = recoveryKey;
this.recoverExpertSettingsScene = recoverExpertSettingsScene;
this.recoverykeyRecoverScene = recoverykeyRecoverScene;
this.appWindows = appWindows;
this.masterkeyFileAccess = masterkeyFileAccess;
this.vaultListManager = vaultListManager;
@@ -92,25 +99,35 @@ public class RecoveryKeyResetPasswordController implements FxController {
this.resourceBundle = resourceBundle;
this.dialogs = dialogs;
this.owner = owner;
initButtonText(recoverType);
}
private void initButtonText(RecoveryActionType type) {
if (type == RecoveryActionType.RESTORE_MASTERKEY) {
buttonText.set(resourceBundle.getString("generic.button.close"));
} else {
buttonText.set(resourceBundle.getString("generic.button.back"));
@FXML
public void initialize() {
switch (recoverType.get()) {
case RESTORE_MASTERKEY -> {
nextButton.setText(resourceBundle.getString("recoveryKey.recover.recoverBtn"));
nextButton.setOnAction((_) -> resetPassword());
}
case RESTORE_ALL -> {
nextButton.setText(resourceBundle.getString("recoveryKey.recover.recoverBtn"));
nextButton.setOnAction((_) -> restorePassword());
}
case RESET_PASSWORD -> {
nextButton.setText(resourceBundle.getString("recoveryKey.recover.resetBtn"));
nextButton.setOnAction((_) -> resetPassword());
}
}
}
@FXML
public void close() {
if (recoverType.equals(RecoveryActionType.RESTORE_MASTERKEY)) {
window.close();
} else {
window.setScene(recoverExpertSettingsScene.get());
switch (recoverType.get()) {
case RESTORE_ALL -> window.setScene(recoverExpertSettingsScene.get());
case RESTORE_MASTERKEY, RESET_PASSWORD -> window.setScene(recoverykeyRecoverScene.get());
default -> window.close();
}
}
@FXML
public void restorePassword() {
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
@@ -123,16 +140,14 @@ public class RecoveryKeyResetPasswordController implements FxController {
CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), cipherCombo.get());
}
recoveryDirectory.moveRecoveredFiles();
recoveryDirectory.moveRecoveredFile(MASTERKEY_FILENAME);
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
if (!vaultListManager.containsVault(vault.getPath())) {
vaultListManager.add(vault.getPath());
}
window.close();
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle)
.setTitleKey("recoveryKey.recoverVaultConfig.title")
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message")
.build().showAndWait();
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle).setTitleKey("recoveryKey.recoverVaultConfig.title").setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message").build().showAndWait();
} catch (IOException | CryptoException e) {
LOG.error("Recovery process failed", e);
@@ -148,14 +163,12 @@ public class RecoveryKeyResetPasswordController implements FxController {
task.setOnSucceeded(_ -> {
LOG.info("Used recovery key to reset password for {}.", vault.getDisplayablePath());
if (vault.getState().equals(org.cryptomator.common.vaults.VaultState.Value.MASTERKEY_MISSING)) {
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle)
.setTitleKey("recoveryKey.recoverMasterkey.title")
.setMessageKey("recoveryKey.recover.resetMasterkeyFileSuccess.message")
.build().showAndWait();
if (recoverType.get().equals(RecoveryActionType.RESET_PASSWORD)) {
window.close();
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle).build().showAndWait();
} else {
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle)
.build().showAndWait();
window.close();
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle).setTitleKey("recoveryKey.recoverMasterkey.title").setMessageKey("recoveryKey.recover.resetMasterkeyFileSuccess.message").build().showAndWait();
}
window.close();
});
@@ -185,6 +198,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
public boolean isPasswordSufficientAndMatching() {
return newPasswordController.isGoodPassword();
}
private final ReadOnlyBooleanWrapper vaultConfigMissing = new ReadOnlyBooleanWrapper();
public ReadOnlyBooleanProperty vaultConfigMissingProperty() {

View File

@@ -44,7 +44,7 @@ public class RecoveryKeyValidateController implements FxController {
private final ObjectProperty<RecoveryKeyState> recoveryKeyState;
private final ObjectProperty<CryptorProvider.Scheme> cipherCombo;
private final AutoCompleter autoCompleter;
private final RecoveryActionType recoverType;
private final ObjectProperty<RecoveryActionType> recoverType;
private final MasterkeyFileAccess masterkeyFileAccess;
private volatile boolean isWrongKey;
@@ -55,7 +55,7 @@ public class RecoveryKeyValidateController implements FxController {
@Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, //
StringProperty recoveryKey, //
RecoveryKeyFactory recoveryKeyFactory, //
@Named("recoverType") RecoveryActionType recoverType, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
@Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo,//
MasterkeyFileAccess masterkeyFileAccess) {
this.vault = vault;
@@ -135,8 +135,8 @@ public class RecoveryKeyValidateController implements FxController {
}
private void validateRecoveryKey() {
switch (recoverType) {
case RESTORE_VAULT_CONFIG -> {
switch (recoverType.get()) {
case RESTORE_ALL, RESTORE_VAULT_CONFIG -> {
try{
var combo = MasterkeyService.validateRecoveryKeyAndDetectCombo(recoveryKeyFactory, vault, recoveryKey.get(), masterkeyFileAccess);
combo.ifPresent(cipherCombo::set);

View File

@@ -15,6 +15,7 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.jetbrains.annotations.Nullable;
import javax.inject.Named;
@@ -27,7 +28,7 @@ import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
@Module(subcomponents = {KeyLoadingComponent.class})
@Module(subcomponents = {KeyLoadingComponent.class, RecoveryKeyComponent.class})
abstract class UnlockModule {
@Provides

View File

@@ -9,7 +9,6 @@ import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
@@ -50,12 +49,12 @@ public class MasterkeyOptionsController implements FxController {
@FXML
public void showRecoveryKey() {
recoveryKeyWindow.create(vault, window, RecoveryActionType.SHOW_KEY).showRecoveryKeyCreationWindow();
recoveryKeyWindow.create(vault, window, new SimpleObjectProperty<>(RecoveryActionType.SHOW_KEY)).showRecoveryKeyCreationWindow();
}
@FXML
public void showRecoverVaultDialog() {
recoveryKeyWindow.create(vault, window, RecoveryActionType.RESET_PASSWORD).showRecoveryKeyRecoverWindow();
recoveryKeyWindow.create(vault, window, new SimpleObjectProperty<>(RecoveryActionType.RESET_PASSWORD)).showRecoveryKeyRecoverWindow();
}
@FXML