Forráskód Böngészése

split package org.cryptoamtor.ui.unlock to allow for different kinds of unlock workflows

Sebastian Stenzel 4 éve
szülő
commit
78e43d401d

+ 1 - 1
main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java

@@ -26,7 +26,7 @@ public enum FxmlFile {
 	RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
 	RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
 	REMOVE_VAULT("/fxml/remove_vault.fxml"), //
-	UNLOCK("/fxml/unlock.fxml"),
+	UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
 	UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
 	UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
 	UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //

+ 14 - 0
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockCancelledException.java

@@ -0,0 +1,14 @@
+package org.cryptomator.ui.unlock;
+
+import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
+
+public class UnlockCancelledException extends MasterkeyLoadingFailedException {
+
+	public UnlockCancelledException(String message) {
+		super(message);
+	}
+
+	public UnlockCancelledException(String message, Throwable cause) {
+		super(message, cause);
+	}
+}

+ 3 - 5
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java

@@ -17,19 +17,17 @@ import javafx.stage.Stage;
 public class UnlockInvalidMountPointController implements FxController {
 
 	private final Stage window;
-	private final Lazy<Scene> unlockScene;
 	private final Vault vault;
 
 	@Inject
-	UnlockInvalidMountPointController(@UnlockWindow Stage window, @FxmlScene(FxmlFile.UNLOCK) Lazy<Scene> unlockScene, @UnlockWindow Vault vault) {
+	UnlockInvalidMountPointController(@UnlockWindow Stage window, @UnlockWindow Vault vault) {
 		this.window = window;
-		this.unlockScene = unlockScene;
 		this.vault = vault;
 	}
 
 	@FXML
-	public void back() {
-		window.setScene(unlockScene.get());
+	public void close() {
+		window.close();
 	}
 
 	/* Getter/Setter */

+ 3 - 94
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java

@@ -4,96 +4,29 @@ import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.IntoMap;
-import org.cryptomator.common.keychain.KeychainManager;
 import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.integrations.keychain.KeychainAccessException;
 import org.cryptomator.ui.common.DefaultSceneFactory;
-import org.cryptomator.ui.common.FxmlLoaderFactory;
 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.StageFactory;
-import org.cryptomator.ui.common.UserInteractionLock;
 import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.cryptomator.ui.unlock.masterkeyfile.MasterkeyFileLoadingComponent;
 
 import javax.inject.Named;
 import javax.inject.Provider;
 import javafx.scene.Scene;
 import javafx.stage.Modality;
 import javafx.stage.Stage;
-import java.nio.file.Path;
 import java.util.Map;
 import java.util.Optional;
 import java.util.ResourceBundle;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 
-@Module(subcomponents = {ForgetPasswordComponent.class})
+@Module(subcomponents = {MasterkeyFileLoadingComponent.class})
 abstract class UnlockModule {
 
-	private static final Logger LOG = LoggerFactory.getLogger(UnlockModule.class);
-
-	public enum PasswordEntry {
-		PASSWORD_ENTERED,
-		CANCELED
-	}
-
-	public enum MasterkeyFileProvision {
-		MASTERKEYFILE_PROVIDED,
-		CANCELED
-	}
-
-	@Provides
-	@UnlockScoped
-	static UserInteractionLock<PasswordEntry> providePasswordEntryLock() {
-		return new UserInteractionLock<>(null);
-	}
-
-	@Provides
-	@UnlockScoped
-	static UserInteractionLock<MasterkeyFileProvision> provideMasterkeyFileProvisionLock() {
-		return new UserInteractionLock<>(null);
-	}
-
-	@Provides
-	@Named("savedPassword")
-	@UnlockScoped
-	static Optional<char[]> provideStoredPassword(KeychainManager keychain, @UnlockWindow Vault vault) {
-		if (!keychain.isSupported()) {
-			return Optional.empty();
-		} else {
-			try {
-				return Optional.ofNullable(keychain.loadPassphrase(vault.getId()));
-			} catch (KeychainAccessException e) {
-				LOG.error("Failed to load entry from system keychain.", e);
-				return Optional.empty();
-			}
-		}
-	}
-
-	@Provides
-	@Named("userProvidedMasterkeyPath")
-	@UnlockScoped
-	static AtomicReference<Path> provideUserProvidedMasterkeyPath() {
-		return new AtomicReference();
-	}
-
-	@Provides
-	@UnlockScoped
-	static AtomicReference<char[]> providePassword(@Named("savedPassword") Optional<char[]> storedPassword) {
-		return new AtomicReference(storedPassword.orElse(null));
-	}
-
-	@Provides
-	@Named("savePassword")
-	@UnlockScoped
-	static AtomicBoolean provideSavePasswordFlag(@Named("savedPassword") Optional<char[]> storedPassword) {
-		return new AtomicBoolean(storedPassword.isPresent());
-	}
-
 	@Provides
 	@UnlockWindow
 	@UnlockScoped
@@ -117,20 +50,6 @@ abstract class UnlockModule {
 		return stage;
 	}
 
-	@Provides
-	@FxmlScene(FxmlFile.UNLOCK)
-	@UnlockScoped
-	static Scene provideUnlockScene(@UnlockWindow FxmlLoaderFactory fxmlLoaders) {
-		return fxmlLoaders.createScene(FxmlFile.UNLOCK);
-	}
-
-	@Provides
-	@FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE)
-	@UnlockScoped
-	static Scene provideUnlockSelectMasterkeyFileScene(@UnlockWindow FxmlLoaderFactory fxmlLoaders) {
-		return fxmlLoaders.createScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE);
-	}
-
 	@Provides
 	@FxmlScene(FxmlFile.UNLOCK_SUCCESS)
 	@UnlockScoped
@@ -147,16 +66,6 @@ abstract class UnlockModule {
 
 	// ------------------
 
-	@Binds
-	@IntoMap
-	@FxControllerKey(UnlockController.class)
-	abstract FxController bindUnlockController(UnlockController controller);
-
-	@Binds
-	@IntoMap
-	@FxControllerKey(UnlockSelectMasterkeyFileController.class)
-	abstract FxController bindUnlockSelectMasterkeyFileController(UnlockSelectMasterkeyFileController controller);
-
 	@Binds
 	@IntoMap
 	@FxControllerKey(UnlockSuccessController.class)

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 16 - 143
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java


+ 14 - 0
main/ui/src/main/java/org/cryptomator/ui/unlock/masterkeyfile/MasterkeyFileLoading.java

@@ -0,0 +1,14 @@
+package org.cryptomator.ui.unlock.masterkeyfile;
+
+import javax.inject.Qualifier;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface MasterkeyFileLoading {
+
+}

+ 38 - 0
main/ui/src/main/java/org/cryptomator/ui/unlock/masterkeyfile/MasterkeyFileLoadingComponent.java

@@ -0,0 +1,38 @@
+package org.cryptomator.ui.unlock.masterkeyfile;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.cryptolib.common.MasterkeyFileLoader;
+
+import javax.annotation.Nullable;
+import javafx.stage.Stage;
+
+@MasterkeyFileLoadingScoped
+@Subcomponent(modules = {MasterkeyFileLoadingModule.class})
+public interface MasterkeyFileLoadingComponent {
+
+	MasterkeyFileLoadingFinisher finisher();
+
+	MasterkeyFileLoader masterkeyLoader();
+
+	default void cleanup(boolean unlockedSuccessfully) {
+		finisher().cleanup(unlockedSuccessfully);
+	}
+
+	@Subcomponent.Builder
+	interface Builder {
+
+		@BindsInstance
+		Builder previousError(@Nullable Exception previousError);
+
+		@BindsInstance
+		Builder vault(@MasterkeyFileLoading Vault vault);
+
+		@BindsInstance
+		Builder unlockWindow(@MasterkeyFileLoading Stage unlockWindow);
+
+		MasterkeyFileLoadingComponent build();
+	}
+
+}

+ 118 - 0
main/ui/src/main/java/org/cryptomator/ui/unlock/masterkeyfile/MasterkeyFileLoadingContext.java

@@ -0,0 +1,118 @@
+package org.cryptomator.ui.unlock.masterkeyfile;
+
+import dagger.Lazy;
+import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.cryptolib.common.MasterkeyFileLoaderContext;
+import org.cryptomator.ui.common.Animations;
+import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.common.UserInteractionLock;
+import org.cryptomator.ui.unlock.UnlockCancelledException;
+import org.cryptomator.ui.unlock.masterkeyfile.MasterkeyFileLoadingModule.MasterkeyFileProvision;
+import org.cryptomator.ui.unlock.masterkeyfile.MasterkeyFileLoadingModule.PasswordEntry;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.security.auth.DestroyFailedException;
+import javax.security.auth.Destroyable;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import javafx.stage.Window;
+import java.nio.CharBuffer;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MasterkeyFileLoadingScoped
+class MasterkeyFileLoadingContext implements MasterkeyFileLoaderContext {
+
+	private final Stage window;
+	private final Lazy<Scene> passphraseEntryScene;
+	private final Lazy<Scene> selectMasterkeyFileScene;
+	private final UserInteractionLock<PasswordEntry> passwordEntryLock;
+	private final UserInteractionLock<MasterkeyFileProvision> masterkeyFileProvisionLock;
+	private final AtomicReference<char[]> password;
+	private final AtomicReference<Path> filePath;
+	private final Exception previousError;
+
+	@Inject
+	public MasterkeyFileLoadingContext(@MasterkeyFileLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD) Lazy<Scene> passphraseEntryScene, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy<Scene> selectMasterkeyFileScene, UserInteractionLock<PasswordEntry> passwordEntryLock, UserInteractionLock<MasterkeyFileProvision> masterkeyFileProvisionLock, AtomicReference<char[]> password, AtomicReference<Path> filePath, @Nullable Exception previousError) {
+		this.window = window;
+		this.passphraseEntryScene = passphraseEntryScene;
+		this.selectMasterkeyFileScene = selectMasterkeyFileScene;
+		this.passwordEntryLock = passwordEntryLock;
+		this.masterkeyFileProvisionLock = masterkeyFileProvisionLock;
+		this.password = password;
+		this.filePath = filePath;
+		this.previousError = previousError;
+	}
+
+	@Override
+	public Path getCorrectMasterkeyFilePath(String masterkeyFilePath) {
+		try {
+			if (askForCorrectMasterkeyFile() == MasterkeyFileProvision.MASTERKEYFILE_PROVIDED) {
+				return filePath.get();
+			} else {
+				throw new UnlockCancelledException("Choosing masterkey file cancelled.");
+			}
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			throw new UnlockCancelledException("Choosing masterkey file interrupted", e);
+		}
+	}
+
+	private MasterkeyFileProvision askForCorrectMasterkeyFile() throws InterruptedException {
+		Platform.runLater(() -> {
+			window.setScene(selectMasterkeyFileScene.get());
+			window.show();
+			Window owner = window.getOwner();
+			if (owner != null) {
+				window.setX(owner.getX() + (owner.getWidth() - window.getWidth()) / 2);
+				window.setY(owner.getY() + (owner.getHeight() - window.getHeight()) / 2);
+			} else {
+				window.centerOnScreen();
+			}
+		});
+		return masterkeyFileProvisionLock.awaitInteraction();
+	}
+
+	@Override
+	public CharSequence getPassphrase(Path path) throws UnlockCancelledException {
+		if (password.get() != null) { // e.g. pre-filled from keychain
+			return CharBuffer.wrap(password.get());
+		}
+
+		assert password.get() == null;
+		try {
+			if (askForPassphrase() == PasswordEntry.PASSWORD_ENTERED) {
+				assert password.get() != null;
+				return CharBuffer.wrap(password.get());
+			} else {
+				throw new UnlockCancelledException("Password entry cancelled.");
+			}
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			throw new UnlockCancelledException("Password entry interrupted", e);
+		}
+	}
+
+	private PasswordEntry askForPassphrase() throws InterruptedException {
+		Platform.runLater(() -> {
+			window.setScene(passphraseEntryScene.get());
+			window.show();
+			Window owner = window.getOwner();
+			if (owner != null) {
+				window.setX(owner.getX() + (owner.getWidth() - window.getWidth()) / 2);
+				window.setY(owner.getY() + (owner.getHeight() - window.getHeight()) / 2);
+			} else {
+				window.centerOnScreen();
+			}
+			if (previousError instanceof InvalidPassphraseException) {
+				Animations.createShakeWindowAnimation(window).play();
+			}
+		});
+		return passwordEntryLock.awaitInteraction();
+	}
+
+}

+ 60 - 0
main/ui/src/main/java/org/cryptomator/ui/unlock/masterkeyfile/MasterkeyFileLoadingFinisher.java

@@ -0,0 +1,60 @@
+package org.cryptomator.ui.unlock.masterkeyfile;
+
+import org.cryptomator.common.keychain.KeychainManager;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.integrations.keychain.KeychainAccessException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import java.nio.CharBuffer;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MasterkeyFileLoadingScoped
+public class MasterkeyFileLoadingFinisher {
+
+	private static final Logger LOG = LoggerFactory.getLogger(MasterkeyFileLoadingFinisher.class);
+
+	private final Vault vault;
+	private final Optional<char[]> storedPassword;
+	private final AtomicReference<char[]> enteredPassword;
+	private final boolean shouldSavePassword;
+	private final KeychainManager keychain;
+
+	@Inject
+	MasterkeyFileLoadingFinisher(@MasterkeyFileLoading Vault vault, @Named("savedPassword") Optional<char[]> storedPassword, AtomicReference<char[]> enteredPassword, @Named("savePassword")AtomicBoolean shouldSavePassword, KeychainManager keychain) {
+		this.vault = vault;
+		this.storedPassword = storedPassword;
+		this.enteredPassword = enteredPassword;
+		this.shouldSavePassword = shouldSavePassword.get();
+		this.keychain = keychain;
+	}
+
+	public void cleanup(boolean successfullyUnlocked) {
+		if (successfullyUnlocked && shouldSavePassword) {
+			savePasswordToSystemkeychain();
+		}
+		wipePassword(storedPassword.orElse(null));
+		wipePassword(enteredPassword.getAndSet(null));
+	}
+
+	private void savePasswordToSystemkeychain() {
+		if (keychain.isSupported()) {
+			try {
+				keychain.storePassphrase(vault.getId(), CharBuffer.wrap(enteredPassword.get()));
+			} catch (KeychainAccessException e) {
+				LOG.error("Failed to store passphrase in system keychain.", e);
+			}
+		}
+	}
+
+	private void wipePassword(char[] pw) {
+		if (pw != null) {
+			Arrays.fill(pw, ' ');
+		}
+	}
+}

+ 133 - 0
main/ui/src/main/java/org/cryptomator/ui/unlock/masterkeyfile/MasterkeyFileLoadingModule.java

@@ -0,0 +1,133 @@
+package org.cryptomator.ui.unlock.masterkeyfile;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoMap;
+import org.cryptomator.common.keychain.KeychainManager;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
+import org.cryptomator.cryptolib.common.MasterkeyFileLoader;
+import org.cryptomator.integrations.keychain.KeychainAccessException;
+import org.cryptomator.ui.common.DefaultSceneFactory;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+import javafx.scene.Scene;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+@Module(subcomponents = {ForgetPasswordComponent.class})
+abstract class MasterkeyFileLoadingModule {
+
+	private static final Logger LOG = LoggerFactory.getLogger(MasterkeyFileLoadingModule.class);
+
+	public enum PasswordEntry {
+		PASSWORD_ENTERED,
+		CANCELED
+	}
+
+	public enum MasterkeyFileProvision {
+		MASTERKEYFILE_PROVIDED,
+		CANCELED
+	}
+
+	@Provides
+	@MasterkeyFileLoadingScoped
+	static MasterkeyFileLoader provideMasterkeyFileLoader(MasterkeyFileAccess masterkeyFileAccess, @MasterkeyFileLoading Vault vault, MasterkeyFileLoadingContext context) {
+		return masterkeyFileAccess.keyLoader(vault.getPath(), context);
+	}
+
+	@Provides
+	@MasterkeyFileLoadingScoped
+	static UserInteractionLock<PasswordEntry> providePasswordEntryLock() {
+		return new UserInteractionLock<>(null);
+	}
+
+	@Provides
+	@MasterkeyFileLoadingScoped
+	static UserInteractionLock<MasterkeyFileProvision> provideMasterkeyFileProvisionLock() {
+		return new UserInteractionLock<>(null);
+	}
+
+	@Provides
+	@Named("savedPassword")
+	@MasterkeyFileLoadingScoped
+	static Optional<char[]> provideStoredPassword(KeychainManager keychain, @MasterkeyFileLoading Vault vault) {
+		if (!keychain.isSupported()) {
+			return Optional.empty();
+		} else {
+			try {
+				return Optional.ofNullable(keychain.loadPassphrase(vault.getId()));
+			} catch (KeychainAccessException e) {
+				LOG.error("Failed to load entry from system keychain.", e);
+				return Optional.empty();
+			}
+		}
+	}
+
+	@Provides
+	@MasterkeyFileLoadingScoped
+	static AtomicReference<Path> provideUserProvidedMasterkeyPath() {
+		return new AtomicReference<>();
+	}
+
+	@Provides
+	@MasterkeyFileLoadingScoped
+	static AtomicReference<char[]> providePassword(@Named("savedPassword") Optional<char[]> storedPassword) {
+		return new AtomicReference<>(storedPassword.orElse(null));
+	}
+
+	@Provides
+	@Named("savePassword")
+	@MasterkeyFileLoadingScoped
+	static AtomicBoolean provideSavePasswordFlag(@Named("savedPassword") Optional<char[]> storedPassword) {
+		return new AtomicBoolean(storedPassword.isPresent());
+	}
+
+	@Provides
+	@MasterkeyFileLoading
+	@MasterkeyFileLoadingScoped
+	static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
+		return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
+	}
+
+	@Provides
+	@FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD)
+	@MasterkeyFileLoadingScoped
+	static Scene provideUnlockScene(@MasterkeyFileLoading FxmlLoaderFactory fxmlLoaders) {
+		return fxmlLoaders.createScene(FxmlFile.UNLOCK_ENTER_PASSWORD);
+	}
+
+	@Provides
+	@FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE)
+	@MasterkeyFileLoadingScoped
+	static Scene provideUnlockSelectMasterkeyFileScene(@MasterkeyFileLoading FxmlLoaderFactory fxmlLoaders) {
+		return fxmlLoaders.createScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE);
+	}
+
+	@Binds
+	@IntoMap
+	@FxControllerKey(PassphraseEntryController.class)
+	abstract FxController bindUnlockController(PassphraseEntryController controller);
+
+	@Binds
+	@IntoMap
+	@FxControllerKey(SelectMasterkeyFileController.class)
+	abstract FxController bindUnlockSelectMasterkeyFileController(SelectMasterkeyFileController controller);
+
+
+}

+ 13 - 0
main/ui/src/main/java/org/cryptomator/ui/unlock/masterkeyfile/MasterkeyFileLoadingScoped.java

@@ -0,0 +1,13 @@
+package org.cryptomator.ui.unlock.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 MasterkeyFileLoadingScoped {
+
+}

+ 9 - 8
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java

@@ -1,4 +1,4 @@
-package org.cryptomator.ui.unlock;
+package org.cryptomator.ui.unlock.masterkeyfile;
 
 import org.cryptomator.common.keychain.KeychainManager;
 import org.cryptomator.common.vaults.Vault;
@@ -7,6 +7,7 @@ import org.cryptomator.ui.common.UserInteractionLock;
 import org.cryptomator.ui.common.WeakBindings;
 import org.cryptomator.ui.controls.NiceSecurePasswordField;
 import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
+import org.cryptomator.ui.unlock.masterkeyfile.MasterkeyFileLoadingModule.PasswordEntry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,17 +39,17 @@ import java.util.Optional;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-@UnlockScoped
-public class UnlockController implements FxController {
+@MasterkeyFileLoadingScoped
+public class PassphraseEntryController implements FxController {
 
-	private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class);
+	private static final Logger LOG = LoggerFactory.getLogger(PassphraseEntryController.class);
 
 	private final Stage window;
 	private final Vault vault;
 	private final AtomicReference<char[]> password;
 	private final AtomicBoolean savePassword;
 	private final Optional<char[]> savedPassword;
-	private final UserInteractionLock<UnlockModule.PasswordEntry> passwordEntryLock;
+	private final UserInteractionLock<PasswordEntry> passwordEntryLock;
 	private final ForgetPasswordComponent.Builder forgetPassword;
 	private final KeychainManager keychain;
 	private final ObjectBinding<ContentDisplay> unlockButtonContentDisplay;
@@ -66,7 +67,7 @@ public class UnlockController implements FxController {
 	public Animation unlockAnimation;
 
 	@Inject
-	public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, AtomicReference<char[]> password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional<char[]> savedPassword, UserInteractionLock<UnlockModule.PasswordEntry> passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain) {
+	public PassphraseEntryController(@MasterkeyFileLoading Stage window, @MasterkeyFileLoading Vault vault, AtomicReference<char[]> password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional<char[]> savedPassword, UserInteractionLock<PasswordEntry> passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain) {
 		this.window = window;
 		this.vault = vault;
 		this.password = password;
@@ -138,7 +139,7 @@ public class UnlockController implements FxController {
 		// if not already interacted, mark this workflow as cancelled:
 		if (passwordEntryLock.awaitingInteraction().get()) {
 			LOG.debug("Unlock canceled by user.");
-			passwordEntryLock.interacted(UnlockModule.PasswordEntry.CANCELED);
+			passwordEntryLock.interacted(PasswordEntry.CANCELED);
 		}
 	}
 
@@ -154,7 +155,7 @@ public class UnlockController implements FxController {
 		if (oldPw != null) {
 			Arrays.fill(oldPw, ' ');
 		}
-		passwordEntryLock.interacted(UnlockModule.PasswordEntry.PASSWORD_ENTERED);
+		passwordEntryLock.interacted(PasswordEntry.PASSWORD_ENTERED);
 		startUnlockAnimation();
 	}
 

+ 6 - 6
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockSelectMasterkeyFileController.java

@@ -1,8 +1,8 @@
-package org.cryptomator.ui.unlock;
+package org.cryptomator.ui.unlock.masterkeyfile;
 
 import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.common.UserInteractionLock;
-import org.cryptomator.ui.unlock.UnlockModule.MasterkeyFileProvision;
+import org.cryptomator.ui.unlock.masterkeyfile.MasterkeyFileLoadingModule.MasterkeyFileProvision;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -17,10 +17,10 @@ import java.nio.file.Path;
 import java.util.ResourceBundle;
 import java.util.concurrent.atomic.AtomicReference;
 
-@UnlockScoped
-public class UnlockSelectMasterkeyFileController implements FxController {
+@MasterkeyFileLoadingScoped
+public class SelectMasterkeyFileController implements FxController {
 
-	private static final Logger LOG = LoggerFactory.getLogger(UnlockSelectMasterkeyFileController.class);
+	private static final Logger LOG = LoggerFactory.getLogger(SelectMasterkeyFileController.class);
 
 	private final Stage window;
 	private final AtomicReference<Path> masterkeyPath;
@@ -28,7 +28,7 @@ public class UnlockSelectMasterkeyFileController implements FxController {
 	private final ResourceBundle resourceBundle;
 
 	@Inject
-	public UnlockSelectMasterkeyFileController(@UnlockWindow Stage window, @Named("userProvidedMasterkeyPath") AtomicReference<Path> masterkeyPath, UserInteractionLock<MasterkeyFileProvision> masterkeyFileProvisionLock, ResourceBundle resourceBundle) {
+	public SelectMasterkeyFileController(@MasterkeyFileLoading Stage window, AtomicReference<Path> masterkeyPath, UserInteractionLock<MasterkeyFileProvision> masterkeyFileProvisionLock, ResourceBundle resourceBundle) {
 		this.window = window;
 		this.masterkeyPath = masterkeyPath;
 		this.masterkeyFileProvisionLock = masterkeyFileProvisionLock;

+ 1 - 1
main/ui/src/main/resources/fxml/unlock.fxml

@@ -14,7 +14,7 @@
 <?import javafx.scene.layout.VBox?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
-	  fx:controller="org.cryptomator.ui.unlock.UnlockController"
+	  fx:controller="org.cryptomator.ui.unlock.masterkeyfile.PassphraseEntryController"
 	  minWidth="400"
 	  maxWidth="400"
 	  minHeight="145"

+ 2 - 2
main/ui/src/main/resources/fxml/unlock_invalid_mount_point.fxml

@@ -37,10 +37,10 @@
 		<Region VBox.vgrow="ALWAYS"/>
 
 		<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
-			<ButtonBar buttonMinWidth="120" buttonOrder="B+U">
+			<ButtonBar buttonMinWidth="120" buttonOrder="U+C">
 				<buttons>
-					<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#back"/>
 					<Region ButtonBar.buttonData="OTHER"/>
+					<Button text="%generic.button.back" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
 				</buttons>
 			</ButtonBar>
 		</VBox>

+ 1 - 1
main/ui/src/main/resources/fxml/unlock_select_masterkeyfile.fxml

@@ -11,7 +11,7 @@
 <?import javafx.scene.shape.Circle?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
-	  fx:controller="org.cryptomator.ui.unlock.UnlockSelectMasterkeyFileController"
+	  fx:controller="org.cryptomator.ui.unlock.masterkeyfile.SelectMasterkeyFileController"
 	  minWidth="400"
 	  maxWidth="400"
 	  minHeight="145"