From aa898c634f7366c931e689a2861c2d30aacbc41e Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Fri, 23 Jan 2026 13:24:58 +0100 Subject: [PATCH] refactor recovery restore to sync logic with async task wrapper for testability --- .../RecoveryKeyCreationController.java | 65 ++++++++++--------- .../RecoveryKeyResetPasswordController.java | 60 +++++++++-------- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java index ac26fabd8..8b0e5f416 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java @@ -104,7 +104,7 @@ public class RecoveryKeyCreationController implements FxController { descriptionLabel.formatProperty().set(resourceBundle.getString("recoveryKey.recover.description")); cancelButton.setOnAction((_) -> back()); cancelButton.setText(resourceBundle.getString("generic.button.back")); - nextButton.setOnAction((_) -> restoreWithPassword()); + nextButton.setOnAction((_) -> restoreWithPasswordAsync()); } } @@ -137,8 +137,8 @@ public class RecoveryKeyCreationController implements FxController { } @FXML - public void restoreWithPassword() { - Task task = new RestoreWithPasswordTask(); + public void restoreWithPasswordAsync() { + Task task = createTask(this::restoreWithPassword); task.setOnScheduled(_ -> { LOG.debug("Restoring vault configuration with password for {}.", vault.getDisplayablePath()); @@ -175,11 +175,42 @@ public class RecoveryKeyCreationController implements FxController { executor.submit(task); } + void restoreWithPassword() 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); + } + } + @FXML public void close() { window.close(); } + @FunctionalInterface + private interface TaskAction { + void run() throws Exception; + } + + private Task createTask(TaskAction action) { + return new Task() { + @Override + protected Void call() throws Exception { + action.run(); + return null; + } + }; + } + private class RecoveryKeyCreationTask extends Task { private RecoveryKeyCreationTask() { @@ -193,34 +224,6 @@ 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 d904ea707..ccc7df0e3 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java @@ -117,15 +117,15 @@ public class RecoveryKeyResetPasswordController implements FxController { @FXML public void next() { switch (recoverType.get()) { - case RESTORE_ALL -> restorePassword(); + case RESTORE_ALL -> restorePasswordAsync(); case RESTORE_MASTERKEY, RESET_PASSWORD -> resetPassword(); default -> resetPassword(); // Fallback } } @FXML - public void restorePassword() { - Task task = new RestorePasswordTask(); + public void restorePasswordAsync() { + Task task = createTask(this::restorePassword); task.setOnScheduled(_ -> { LOG.debug("Restoring vault configuration for {}.", vault.getDisplayablePath()); @@ -156,6 +156,20 @@ public class RecoveryKeyResetPasswordController implements FxController { executor.submit(task); } + void restorePassword() 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); + } + } + @FXML public void resetPassword() { Task task = new ResetPasswordTask(); @@ -182,6 +196,21 @@ public class RecoveryKeyResetPasswordController implements FxController { executor.submit(task); } + @FunctionalInterface + private interface TaskAction { + void run() throws Exception; + } + + private Task createTask(TaskAction action) { + return new Task() { + @Override + protected Void call() throws Exception { + action.run(); + return null; + } + }; + } + private class ResetPasswordTask extends Task { private static final Logger LOG = LoggerFactory.getLogger(ResetPasswordTask.class); @@ -197,31 +226,6 @@ 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() {