From efd73e0d3ea6d1d90260413746b76ada11577401 Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Wed, 14 Jan 2026 17:14:23 +0100 Subject: [PATCH] move recovery IO operations to background --- .../RecoveryKeyCreationController.java | 83 +++++++++++++------ .../RecoveryKeyResetPasswordController.java | 70 +++++++++++----- 2 files changed, 107 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java index 456de11f4..ac26fabd8 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java @@ -138,38 +138,41 @@ public class RecoveryKeyCreationController implements FxController { @FXML public void restoreWithPassword() { + Task task = new RestoreWithPasswordTask(); - try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) { - Path recoveryPath = recoveryDirectory.getRecoveryPath(); + task.setOnScheduled(_ -> { + LOG.debug("Restoring vault configuration with password for {}.", vault.getDisplayablePath()); + }); - Path masterkeyFilePath = vault.getPath().resolve(MASTERKEY_FILENAME); - - try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, masterkeyFilePath, passwordField.getCharacters())) { - var combo = MasterkeyService.detect(masterkey, vault.getPath()) - .orElseThrow(() -> new IllegalStateException("Could not detect combo for vault path: " + vault.getPath())); - - CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), combo); + task.setOnSucceeded(_ -> { + LOG.debug("Restored vault configuration for {}.", vault.getDisplayablePath()); + try { + if (!vaultListManager.isAlreadyAdded(vault.getPath())) { + vaultListManager.add(vault.getPath()); + } + window.close(); + dialogs.prepareRecoverPasswordSuccess((Stage) window.getOwner()) // + .setTitleKey("recover.recoverVaultConfig.title") // + .setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") // + .setDescriptionKey("recoveryKey.recover.resetMasterkeyFileSuccess.description") + .build().showAndWait(); + } catch (IOException e) { + LOG.error("Failed to add vault to list.", e); + appWindows.showErrorWindow(e, window, null); } + }); - recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME); - - if (!vaultListManager.isAlreadyAdded(vault.getPath())) { - vaultListManager.add(vault.getPath()); + task.setOnFailed(_ -> { + if (task.getException() instanceof InvalidPassphraseException e) { + LOG.info("Password invalid", e); + Animations.createShakeWindowAnimation(window).play(); + } else { + LOG.error("Recovery process failed.", task.getException()); + appWindows.showErrorWindow(task.getException(), window, null); } - window.close(); - dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()) // - .setTitleKey("recover.recoverVaultConfig.title") // - .setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") // - .setDescriptionKey("recoveryKey.recover.resetMasterkeyFileSuccess.description") - .build().showAndWait(); + }); - } catch (InvalidPassphraseException e) { - LOG.info("Password invalid", e); - Animations.createShakeWindowAnimation(window).play(); - } catch (IOException | CryptoException | IllegalStateException e) { - LOG.error("Recovery process failed", e); - appWindows.showErrorWindow(e, window, null); - } + executor.submit(task); } @FXML @@ -190,6 +193,34 @@ public class RecoveryKeyCreationController implements FxController { } + private class RestoreWithPasswordTask extends Task { + + private static final Logger LOG = LoggerFactory.getLogger(RestoreWithPasswordTask.class); + + private RestoreWithPasswordTask() { + setOnFailed(_ -> LOG.error("Failed to restore vault configuration with password", getException())); + } + + @Override + protected Void call() throws IOException, CryptoException { + 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, vault.getPath()) + .orElseThrow(() -> new IllegalStateException("Could not detect combo for vault path: " + vault.getPath())); + + CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), combo); + } + + recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME); + } + return null; + } + + } + /* Getter/Setter */ public Vault getVault() { diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java index 0c06ba9b2..d904ea707 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java @@ -125,30 +125,35 @@ public class RecoveryKeyResetPasswordController implements FxController { @FXML public void restorePassword() { - try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) { - Path recoveryPath = recoveryDirectory.getRecoveryPath(); - MasterkeyService.recoverFromRecoveryKey(recoveryKey.get(), recoveryKeyFactory, recoveryPath, newPasswordController.passwordField.getCharacters()); + Task task = new RestorePasswordTask(); - try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, recoveryPath.resolve(MASTERKEY_FILENAME), newPasswordController.passwordField.getCharacters())) { - CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), cipherCombo.get()); + task.setOnScheduled(_ -> { + LOG.debug("Restoring vault configuration for {}.", vault.getDisplayablePath()); + }); + + task.setOnSucceeded(_ -> { + LOG.debug("Restored vault configuration for {}.", vault.getDisplayablePath()); + try { + if (!vaultListManager.isAlreadyAdded(vault.getPath())) { + vaultListManager.add(vault.getPath()); + } + window.close(); + dialogs.prepareRecoverPasswordSuccess((Stage) window.getOwner()) // + .setTitleKey("recover.recoverVaultConfig.title") // + .setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") // + .build().showAndWait(); + } catch (IOException e) { + LOG.error("Failed to add vault to list.", e); + appWindows.showErrorWindow(e, window, null); } + }); - recoveryDirectory.moveRecoveredFile(MASTERKEY_FILENAME); - recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME); + task.setOnFailed(_ -> { + LOG.error("Recovery process failed.", task.getException()); + appWindows.showErrorWindow(task.getException(), window, null); + }); - if (!vaultListManager.isAlreadyAdded(vault.getPath())) { - vaultListManager.add(vault.getPath()); - } - window.close(); - dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()) // - .setTitleKey("recover.recoverVaultConfig.title") // - .setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") // - .build().showAndWait(); - - } catch (IOException | CryptoException e) { - LOG.error("Recovery process failed", e); - appWindows.showErrorWindow(e, window, null); - } + executor.submit(task); } @FXML @@ -192,6 +197,31 @@ public class RecoveryKeyResetPasswordController implements FxController { } } + private class RestorePasswordTask extends Task { + + private static final Logger LOG = LoggerFactory.getLogger(RestorePasswordTask.class); + + public RestorePasswordTask() { + setOnFailed(_ -> LOG.error("Failed to restore vault configuration", getException())); + } + + @Override + protected Void call() throws IOException, CryptoException { + try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) { + Path recoveryPath = recoveryDirectory.getRecoveryPath(); + MasterkeyService.recoverFromRecoveryKey(recoveryKey.get(), recoveryKeyFactory, recoveryPath, newPasswordController.passwordField.getCharacters()); + + try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, recoveryPath.resolve(MASTERKEY_FILENAME), newPasswordController.passwordField.getCharacters())) { + CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), cipherCombo.get()); + } + + recoveryDirectory.moveRecoveredFile(MASTERKEY_FILENAME); + recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME); + } + return null; + } + } + /* Getter/Setter */ public ReadOnlyBooleanProperty passwordSufficientAndMatchingProperty() {