refactor recovery restore to sync logic with async task wrapper for testability

This commit is contained in:
Jan-Peter Klein
2026-01-23 13:24:58 +01:00
parent efd73e0d3e
commit aa898c634f
2 changed files with 66 additions and 59 deletions

View File

@@ -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<Void> task = new RestoreWithPasswordTask();
public void restoreWithPasswordAsync() {
Task<Void> 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<Void> createTask(TaskAction action) {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
action.run();
return null;
}
};
}
private class RecoveryKeyCreationTask extends Task<String> {
private RecoveryKeyCreationTask() {
@@ -193,34 +224,6 @@ public class RecoveryKeyCreationController implements FxController {
}
private class RestoreWithPasswordTask extends Task<Void> {
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() {

View File

@@ -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<Void> task = new RestorePasswordTask();
public void restorePasswordAsync() {
Task<Void> 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<Void> 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<Void> createTask(TaskAction action) {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
action.run();
return null;
}
};
}
private class ResetPasswordTask extends Task<Void> {
private static final Logger LOG = LoggerFactory.getLogger(ResetPasswordTask.class);
@@ -197,31 +226,6 @@ public class RecoveryKeyResetPasswordController implements FxController {
}
}
private class RestorePasswordTask extends Task<Void> {
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() {