recoverType fix and more use of dialogs

This commit is contained in:
Jan-Peter Klein
2025-03-18 14:25:44 +01:00
parent f9e8031acb
commit 087b6162ed
12 changed files with 129 additions and 131 deletions

View File

@@ -1,6 +1,32 @@
package org.cryptomator.common;
import dagger.Lazy;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.stream.Stream;
import static org.cryptomator.common.Constants.DEFAULT_KEY_ID;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
import static org.cryptomator.common.vaults.VaultState.Value.MASTERKEY_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME;
import static org.cryptomator.cryptolib.api.CryptorProvider.Scheme.SIV_CTRMAC;
import static org.cryptomator.cryptolib.api.CryptorProvider.Scheme.SIV_GCM;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Vault;
@@ -14,9 +40,9 @@ import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.ui.addvaultwizard.CreateNewVaultExpertSettingsController;
import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
@@ -24,45 +50,13 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import static org.cryptomator.common.Constants.DEFAULT_KEY_ID;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME;
import static org.cryptomator.cryptolib.api.CryptorProvider.Scheme.SIV_CTRMAC;
import static org.cryptomator.cryptolib.api.CryptorProvider.Scheme.SIV_GCM;
public class RecoverUtil {
private static final Logger LOG = LoggerFactory.getLogger(RecoverUtil.class);
public static CryptorProvider.Scheme detectCipherCombo(byte[] masterkey, Path pathToVault) {
try (Stream<Path> paths = Files.walk(pathToVault.resolve(DATA_DIR_NAME))) {
return paths.filter(path -> path.toString()
.endsWith(".c9r"))
.findFirst()
.map(c9rFile -> determineScheme(c9rFile, masterkey))
.orElseThrow(() -> new IllegalStateException("No .c9r file found."));
return paths.filter(path -> path.toString().endsWith(".c9r")).findFirst().map(c9rFile -> determineScheme(c9rFile, masterkey)).orElseThrow(() -> new IllegalStateException("No .c9r file found."));
} catch (IOException e) {
throw new IllegalStateException("Failed to detect cipher combo.", e);
}
@@ -140,11 +134,7 @@ public class RecoverUtil {
}
}
public static Optional<CryptorProvider.Scheme> validateRecoveryKeyAndGetCombo(
RecoveryKeyFactory recoveryKeyFactory,
Vault vault,
StringProperty recoveryKey,
MasterkeyFileAccess masterkeyFileAccess) {
public static Optional<CryptorProvider.Scheme> validateRecoveryKeyAndGetCombo(RecoveryKeyFactory recoveryKeyFactory, Vault vault, StringProperty recoveryKey, MasterkeyFileAccess masterkeyFileAccess) {
Path tempRecoveryPath = null;
CharSequence tmpPass = "asdasdasd";
@@ -193,7 +183,7 @@ public class RecoverUtil {
}
}
public static Task<Void> createResetPasswordTask(RecoveryKeyFactory recoveryKeyFactory, Vault vault, StringProperty recoveryKey, NewPasswordController newPasswordController, Stage window, Lazy<Scene> recoverResetPasswordSuccessScene, Lazy<Scene> recoverResetVaultConfigSuccessScene, FxApplicationWindows appWindows) {
public static Task<Void> createResetPasswordTask(ResourceBundle resourceBundle,Stage owner, RecoveryKeyFactory recoveryKeyFactory, Vault vault, StringProperty recoveryKey, NewPasswordController newPasswordController, Stage window, FxApplicationWindows appWindows, Dialogs dialogs) {
Task<Void> task = new ResetPasswordTask(recoveryKeyFactory, vault, recoveryKey, newPasswordController);
@@ -203,10 +193,12 @@ public class RecoverUtil {
task.setOnSucceeded(_ -> {
LOG.info("Used recovery key to reset password for {}.", vault.getDisplayablePath());
if (vault.getState().equals(VAULT_CONFIG_MISSING)) {
window.setScene(recoverResetVaultConfigSuccessScene.get());
if (vault.getState().equals(MASTERKEY_MISSING)) {
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle).setTitleKey("recoveryKey.recoverMasterkey.title").setMessageKey("recoveryKey.recover.resetMasterkeyFileSuccess.message").build().showAndWait();
window.close();
} else {
window.setScene(recoverResetPasswordSuccessScene.get());
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle).build().showAndWait();
window.close();
}
});
@@ -243,7 +235,7 @@ public class RecoverUtil {
}
}
public static Optional<Vault> prepareVaultFromDirectory(DirectoryChooser directoryChooser, Stage window, Dialogs dialogs, VaultComponent.Factory vaultComponentFactory, List<MountService> mountServices) {
public static Optional<Vault> checkAndPrepareVaultFromDirectory(DirectoryChooser directoryChooser, Stage window, Dialogs dialogs, VaultComponent.Factory vaultComponentFactory, List<MountService> mountServices) {
File selectedDirectory;
do {

View File

@@ -173,7 +173,18 @@ public class VaultListManager {
yield ERROR;
}
}
case ERROR, UNLOCKED, PROCESSING -> previousState;
case ERROR -> {
try {
var determinedState = determineVaultState(vault.getPath(), vault.getVaultSettings());
state.set(determinedState);
yield determinedState;
} catch (IOException e) {
LOG.warn("Failed to redetermine vault state for " + vault.getPath(), e);
vault.setLastKnownException(e);
yield ERROR;
}
}
case UNLOCKED, PROCESSING -> previousState;
};
}

View File

@@ -1,5 +1,28 @@
package org.cryptomator.ui.addvaultwizard;
import javax.inject.Inject;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.image.Image;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
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 dagger.Lazy;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.RecoverUtil;
@@ -18,28 +41,6 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.image.Image;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
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;
@AddVaultWizardScoped
public class ChooseExistingVaultController implements FxController {
@@ -91,9 +92,10 @@ public class ChooseExistingVaultController implements FxController {
this.dialogs = dialogs;
}
public void initialize(){
public void initialize() {
restoreButtonVisible.bind(restoreCheckBox.selectedProperty());
}
private Image selectScreenshot(Theme theme) {
String imageResourcePath;
if (SystemUtils.IS_OS_MAC) {
@@ -129,13 +131,11 @@ public class ChooseExistingVaultController implements FxController {
@FXML
public void restoreVaultConfigWithRecoveryKey() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle(resourceBundle.getString("generic.button.cancel"));
Optional<Vault> optionalVault = RecoverUtil.prepareVaultFromDirectory(directoryChooser, window, dialogs, vaultComponentFactory, mountServices);
Optional<Vault> optionalVault = RecoverUtil.checkAndPrepareVaultFromDirectory(directoryChooser, window, dialogs, vaultComponentFactory, mountServices);
optionalVault.ifPresent(vault -> {
recoveryKeyWindow.create(vault, window,RecoverUtil.Type.RESTORE_VAULT_CONFIG).showIsHubVaultDialogWindow();
ObjectProperty<RecoverUtil.Type> recoverTypeProperty = new SimpleObjectProperty<>(RecoverUtil.Type.RESTORE_VAULT_CONFIG);
recoveryKeyWindow.create(vault, window, recoverTypeProperty).showIsHubVaultDialogWindow();
});
}

View File

@@ -57,6 +57,22 @@ public class Dialogs {
.setCancelButtonKey("generic.button.cancel");
}
public SimpleDialog.Builder prepareRecoverPasswordSuccess(Stage window, Stage owner, ResourceBundle resourceBundle) {
return createDialogBuilder()
.setOwner(window) //
.setTitleKey("recoveryKey.recover.title") //
.setMessageKey("recoveryKey.recover.resetSuccess.message") //
.setDescriptionKey("recoveryKey.recover.resetSuccess.description") //
.setIcon(FontAwesome5Icon.CHECK)
.setOkAction(stage -> {
stage.close();
if (owner.getTitle().equals(resourceBundle.getString("addvaultwizard.existing.title"))) {
owner.close();
}
})
.setOkButtonKey("generic.button.close");
}
public SimpleDialog.Builder prepareRemoveCertDialog(Stage window, Settings settings) {
return createDialogBuilder() //
.setOwner(window) //
@@ -106,9 +122,7 @@ public class Dialogs {
.setDescriptionKey("recoveryKey.noDDirDetected.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) //
.setOkButtonKey("generic.button.change") //
.setCancelButtonKey("generic.button.close") //
.setOkAction(Stage::close) //
.setCancelAction(Stage::close);
.setOkAction(Stage::close);
}
}

View File

@@ -10,6 +10,7 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.stage.FileChooser;
@@ -60,13 +61,15 @@ public class VaultDetailMissingVaultController implements FxController {
dialogs.prepareContactHubAdmin(window).build().showAndWait();
}
else {
recoveryKeyWindow.create(vault.get(), window, RecoverUtil.Type.RESTORE_VAULT_CONFIG).showIsHubVaultDialogWindow();
ObjectProperty<RecoverUtil.Type> recoverTypeProperty = new SimpleObjectProperty<>(RecoverUtil.Type.RESTORE_VAULT_CONFIG);
recoveryKeyWindow.create(vault.get(), window, recoverTypeProperty).showIsHubVaultDialogWindow();
}
}
@FXML
void restoreMasterkey() {
recoveryKeyWindow.create(vault.get(), window,RecoverUtil.Type.RESTORE_MASTERKEY).showRecoveryKeyRecoverWindow();
ObjectProperty<RecoverUtil.Type> recoverTypeProperty = new SimpleObjectProperty<>(RecoverUtil.Type.RESTORE_MASTERKEY);
recoveryKeyWindow.create(vault.get(), window, recoverTypeProperty).showRecoveryKeyRecoverWindow();
}
@FXML

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") RecoverUtil.Type recoverType);
@BindsInstance @Named("recoverType") ObjectProperty<RecoverUtil.Type> recoverType);
}
}

View File

@@ -155,13 +155,6 @@ abstract class RecoveryKeyModule {
return new SimpleIntegerProperty(CreateNewVaultExpertSettingsController.MAX_SHORTENING_THRESHOLD);
}
@Provides
@Named("recoverType")
@RecoveryKeyScoped
static ObjectProperty<RecoverUtil.Type> provideRecoverType() {
return new SimpleObjectProperty<>(RecoverUtil.Type.RESTORE_MASTERKEY);
}
@Provides
@Named("cipherCombo")
@RecoveryKeyScoped
@@ -194,10 +187,6 @@ abstract class RecoveryKeyModule {
@FxControllerKey(RecoveryKeyResetPasswordController.class)
abstract FxController bindRecoveryKeyResetPasswordController(RecoveryKeyResetPasswordController controller);
@Binds
@IntoMap
@FxControllerKey(RecoveryKeyResetPasswordSuccessController.class)
abstract FxController bindRecoveryKeyResetPasswordSuccessController(RecoveryKeyResetPasswordSuccessController controller);
@Provides
@IntoMap

View File

@@ -12,6 +12,7 @@ import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.Optional;
import java.util.ResourceBundle;
@RecoveryKeyScoped
@@ -52,6 +53,7 @@ public class RecoveryKeyRecoverController implements FxController {
yield null;
}
};
}
@FXML

View File

@@ -12,6 +12,8 @@ import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.dialogs.SimpleDialog;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,6 +57,8 @@ public class RecoveryKeyResetPasswordController implements FxController {
private final ObjectProperty<CryptorProvider.Scheme> cipherCombo;
private final ResourceBundle resourceBundle;
private final StringProperty buttonText = new SimpleStringProperty();
private final Dialogs dialogs;
private final Stage owner;
public NewPasswordController newPasswordController;
@@ -63,6 +67,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
@RecoveryKeyWindow Vault vault, //
RecoveryKeyFactory recoveryKeyFactory, //
ExecutorService executor, //
@Named("keyRecoveryOwner") Stage owner,
@RecoveryKeyWindow StringProperty recoveryKey, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> recoverExpertSettingsScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD_SUCCESS) Lazy<Scene> recoverResetPasswordSuccessScene, //
@@ -73,7 +78,8 @@ public class RecoveryKeyResetPasswordController implements FxController {
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
@Named("recoverType") ObjectProperty<RecoverUtil.Type> recoverType,
@Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo,//
ResourceBundle resourceBundle) {
ResourceBundle resourceBundle,
Dialogs dialogs) {
this.window = window;
this.vault = vault;
this.recoveryKeyFactory = recoveryKeyFactory;
@@ -89,7 +95,8 @@ public class RecoveryKeyResetPasswordController implements FxController {
this.cipherCombo = cipherCombo;
this.recoverType = recoverType;
this.resourceBundle = resourceBundle;
this.dialogs = dialogs;
this.owner = owner;
initButtonText(recoverType.get());
}
@@ -127,20 +134,26 @@ public class RecoveryKeyResetPasswordController implements FxController {
RecoverUtil.deleteRecoveryDirectory(recoveryPath);
RecoverUtil.addVaultToList(vaultListManager, vault.getPath());
window.setScene(recoverResetVaultConfigSuccessScene.get());
dialogs.prepareRecoverPasswordSuccess(window, owner, resourceBundle)
.setTitleKey("recoveryKey.recoverVaultConfig.title")
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message")
.build().showAndWait();
window.close(); // Erst jetzt das Fenster schließen
} catch (IOException | CryptoException e) {
LOG.error("Recovery process failed", e);
}
} else {
Task<Void> task = RecoverUtil.createResetPasswordTask( //
resourceBundle,
owner,
recoveryKeyFactory, //
vault, //
recoveryKey, //
newPasswordController, //
window, //
recoverResetPasswordSuccessScene, //
recoverResetVaultConfigSuccessScene, //
appWindows);
appWindows,
dialogs);
executor.submit(task);
}
}

View File

@@ -1,32 +0,0 @@
package org.cryptomator.ui.recoverykey;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@RecoveryKeyScoped
public class RecoveryKeyResetPasswordSuccessController implements FxController {
private final Stage window;
private final Stage owner;
@Inject
public RecoveryKeyResetPasswordSuccessController(@RecoveryKeyWindow Stage window, //
@Named("keyRecoveryOwner") Stage owner) {
this.window = window;
this.owner = owner;
}
@FXML
public void close() {
if (!owner.getTitle().equals("Cryptomator")) {
owner.close();
}
window.close();
}
}

View File

@@ -11,7 +11,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@@ -56,12 +58,14 @@ public class MasterkeyOptionsController implements FxController {
@FXML
public void showRecoveryKey() {
recoveryKeyWindow.create(vault, window, RecoverUtil.Type.SHOW_KEY).showRecoveryKeyCreationWindow();
ObjectProperty<RecoverUtil.Type> recoverTypeProperty = new SimpleObjectProperty<>(RecoverUtil.Type.SHOW_KEY);
recoveryKeyWindow.create(vault, window, recoverTypeProperty).showRecoveryKeyCreationWindow();
}
@FXML
public void showRecoverVaultDialog() {
recoveryKeyWindow.create(vault, window,RecoverUtil.Type.RESET_PASSWORD).showRecoveryKeyRecoverWindow();
ObjectProperty<RecoverUtil.Type> recoverTypeProperty = new SimpleObjectProperty<>(RecoverUtil.Type.RESET_PASSWORD);
recoveryKeyWindow.create(vault, window, recoverTypeProperty).showRecoveryKeyRecoverWindow();
}
@FXML

View File

@@ -524,6 +524,7 @@ recoveryKey.recover.resetSuccess.message=Password reset successful
recoveryKey.recover.resetSuccess.description=You can unlock your vault with the new password.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetVaultConfigSuccess.message=Vault config reset successful
recoveryKey.recover.resetMasterkeyFileSuccess.message=Masterkey file reset successful
### Recover Kram