move masterkey selection to subcomponent

and use CompletableFuture instead of UserInteractionLock + AtomicReference
This commit is contained in:
Sebastian Stenzel
2021-12-16 17:00:49 +01:00
parent e95853deac
commit 983a4d0b0f
6 changed files with 103 additions and 85 deletions

View File

@@ -0,0 +1,25 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import dagger.Subcomponent;
import javafx.scene.Scene;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
@ChooseMasterkeyFileScoped
@Subcomponent(modules = {ChooseMasterkeyFileModule.class})
public interface ChooseMasterkeyFileComponent {
@ChooseMasterkeyFileScoped
Scene chooseMasterkeyScene();
@ChooseMasterkeyFileScoped
CompletableFuture<Path> result();
@Subcomponent.Builder
interface Builder {
ChooseMasterkeyFileComponent build();
}
}

View File

@@ -0,0 +1,42 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
@Module
abstract class ChooseMasterkeyFileModule {
@Provides
@ChooseMasterkeyFileScoped
static CompletableFuture<Path> provideResult() {
return new CompletableFuture<>();
}
@Provides
@ChooseMasterkeyFileScoped
static Scene provideChooseMasterkeyScene(SelectMasterkeyFileController controller, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
// TODO: simplify FxmlLoaderFactory
try {
var url = FxmlLoaderFactory.class.getResource(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE.getRessourcePathString());
var loader = new FXMLLoader(url, resourceBundle, null, clazz -> controller);
Parent root = loader.load();
return sceneFactory.apply(root);
} catch (IOException e) {
throw new UncheckedIOException("Failed to load UnlockScene", e);
}
}
}

View File

@@ -0,0 +1,13 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface ChooseMasterkeyFileScoped {
}

View File

@@ -8,12 +8,6 @@ import dagger.multibindings.StringKey;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.UserInteractionLock;
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
@@ -22,27 +16,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javafx.scene.Scene;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
@Module(subcomponents = {ForgetPasswordComponent.class, PassphraseEntryComponent.class})
@Module(subcomponents = {ForgetPasswordComponent.class, PassphraseEntryComponent.class, ChooseMasterkeyFileComponent.class})
public abstract class MasterkeyFileLoadingModule {
private static final Logger LOG = LoggerFactory.getLogger(MasterkeyFileLoadingModule.class);
public enum MasterkeyFileProvision {
MASTERKEYFILE_PROVIDED,
CANCELED
}
@Provides
@KeyLoadingScoped
static UserInteractionLock<MasterkeyFileProvision> provideMasterkeyFileProvisionLock() {
return new UserInteractionLock<>(null);
}
@Provides
@Named("savedPassword")
@KeyLoadingScoped
@@ -59,24 +39,6 @@ public abstract class MasterkeyFileLoadingModule {
}
}
@Provides
@KeyLoadingScoped
static AtomicReference<Path> provideUserProvidedMasterkeyPath() {
return new AtomicReference<>();
}
@Provides
@FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE)
@KeyLoadingScoped
static Scene provideUnlockSelectMasterkeyFileScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE);
}
@Binds
@IntoMap
@FxControllerKey(SelectMasterkeyFileController.class)
abstract FxController bindUnlockSelectMasterkeyFileController(SelectMasterkeyFileController controller);
@Binds
@IntoMap
@KeyLoadingScoped

View File

@@ -1,7 +1,7 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.common.Passphrase;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.common.BackupHelper;
@@ -11,10 +11,6 @@ import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.common.Passphrase;
import org.cryptomator.ui.common.UserInteractionLock;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.unlock.UnlockCancelledException;
@@ -22,7 +18,6 @@ import org.cryptomator.ui.unlock.UnlockCancelledException;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.io.IOException;
@@ -32,7 +27,6 @@ import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
@KeyLoading
public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
@@ -42,10 +36,8 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
private final Vault vault;
private final MasterkeyFileAccess masterkeyFileAccess;
private final Stage window;
private final Lazy<Scene> selectMasterkeyFileScene;
private final PassphraseEntryComponent.Builder passphraseEntry;
private final UserInteractionLock<MasterkeyFileLoadingModule.MasterkeyFileProvision> masterkeyFileProvisionLock;
private final AtomicReference<Path> filePath;
private final ChooseMasterkeyFileComponent.Builder masterkeyChooser;
private final KeychainManager keychain;
private Passphrase passphrase;
@@ -53,14 +45,12 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
private boolean wrongPassphrase;
@Inject
public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy<Scene> selectMasterkeyFileScene, @Named("savedPassword") Optional<char[]> savedPassphrase, PassphraseEntryComponent.Builder passphraseEntry, UserInteractionLock<MasterkeyFileLoadingModule.MasterkeyFileProvision> masterkeyFileProvisionLock, AtomicReference<Path> filePath, KeychainManager keychain) {
public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Stage window, @Named("savedPassword") Optional<char[]> savedPassphrase, PassphraseEntryComponent.Builder passphraseEntry, ChooseMasterkeyFileComponent.Builder masterkeyChooser, KeychainManager keychain) {
this.vault = vault;
this.masterkeyFileAccess = masterkeyFileAccess;
this.window = window;
this.selectMasterkeyFileScene = selectMasterkeyFileScene;
this.passphraseEntry = passphraseEntry;
this.masterkeyFileProvisionLock = masterkeyFileProvisionLock;
this.filePath = filePath;
this.masterkeyChooser = masterkeyChooser;
this.keychain = keychain;
this.passphrase = savedPassphrase.map(Passphrase::new).orElse(null);
this.savePassphrase = savedPassphrase.isPresent();
@@ -72,7 +62,7 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
try {
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
if (!Files.exists(filePath)) {
filePath = getAlternateMasterkeyFilePath();
filePath = askUserForMasterkeyFilePath();
}
if (passphrase == null) {
askForPassphrase();
@@ -127,20 +117,10 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
}
}
private Path getAlternateMasterkeyFilePath() throws UnlockCancelledException, InterruptedException {
if (filePath.get() == null) {
return switch (askUserForMasterkeyFilePath()) {
case MASTERKEYFILE_PROVIDED -> filePath.get();
case CANCELED -> throw new UnlockCancelledException("Choosing masterkey file cancelled.");
};
} else {
return filePath.get();
}
}
private MasterkeyFileLoadingModule.MasterkeyFileProvision askUserForMasterkeyFilePath() throws InterruptedException {
private Path askUserForMasterkeyFilePath() throws InterruptedException {
var comp = masterkeyChooser.build();
Platform.runLater(() -> {
window.setScene(selectMasterkeyFileScene.get());
window.setScene(comp.chooseMasterkeyScene());
window.show();
Window owner = window.getOwner();
if (owner != null) {
@@ -150,7 +130,13 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
window.centerOnScreen();
}
});
return masterkeyFileProvisionLock.awaitInteraction();
try {
return comp.result().get();
} catch (CancellationException e) {
throw new UnlockCancelledException("Choosing masterkey file cancelled.");
} catch (ExecutionException e) {
throw new MasterkeyLoadingFailedException("Failed to select masterkey file.", e);
}
}
private void askForPassphrase() throws InterruptedException {

View File

@@ -1,10 +1,7 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.UserInteractionLock;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule.MasterkeyFileProvision;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -16,23 +13,21 @@ import javafx.stage.WindowEvent;
import java.io.File;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.CompletableFuture;
@KeyLoadingScoped
@ChooseMasterkeyFileScoped
public class SelectMasterkeyFileController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(SelectMasterkeyFileController.class);
private final Stage window;
private final AtomicReference<Path> masterkeyPath;
private final UserInteractionLock<MasterkeyFileProvision> masterkeyFileProvisionLock;
private final CompletableFuture<Path> result;
private final ResourceBundle resourceBundle;
@Inject
public SelectMasterkeyFileController(@KeyLoading Stage window, AtomicReference<Path> masterkeyPath, UserInteractionLock<MasterkeyFileProvision> masterkeyFileProvisionLock, ResourceBundle resourceBundle) {
public SelectMasterkeyFileController(@KeyLoading Stage window, CompletableFuture<Path> result, ResourceBundle resourceBundle) {
this.window = window;
this.masterkeyPath = masterkeyPath;
this.masterkeyFileProvisionLock = masterkeyFileProvisionLock;
this.result = result;
this.resourceBundle = resourceBundle;
this.window.setOnHiding(this::windowClosed);
}
@@ -43,11 +38,7 @@ public class SelectMasterkeyFileController implements FxController {
}
private void windowClosed(WindowEvent windowEvent) {
// if not already interacted, mark this workflow as cancelled:
if (masterkeyFileProvisionLock.awaitingInteraction().get()) {
LOG.debug("Unlock canceled by user.");
masterkeyFileProvisionLock.interacted(MasterkeyFileProvision.CANCELED);
}
result.cancel(true);
}
@FXML
@@ -59,8 +50,7 @@ public class SelectMasterkeyFileController implements FxController {
File masterkeyFile = fileChooser.showOpenDialog(window);
if (masterkeyFile != null) {
LOG.debug("Chose masterkey file: {}", masterkeyFile);
masterkeyPath.set(masterkeyFile.toPath());
masterkeyFileProvisionLock.interacted(MasterkeyFileProvision.MASTERKEYFILE_PROVIDED);
result.complete(masterkeyFile.toPath());
}
}