From 0bece0f59124231176473c790fdb157e635c079c Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 16 Dec 2021 13:56:59 +0100 Subject: [PATCH] move passphrase entry to subcomponent and use CompletableFuture instead of UserInteractionLock + AtomicReference --- .../org/cryptomator/ui/common/FxmlFile.java | 2 +- .../ui/keyloading/KeyLoadingModule.java | 2 +- .../MasterkeyFileLoadingFinisher.java | 62 -------------- .../MasterkeyFileLoadingModule.java | 39 +-------- .../MasterkeyFileLoadingStrategy.java | 81 ++++++++++++------- .../PassphraseEntryComponent.java | 35 ++++++++ .../PassphraseEntryController.java | 80 +++++++----------- .../masterkeyfile/PassphraseEntryModule.java | 41 ++++++++++ .../masterkeyfile/PassphraseEntryResult.java | 6 ++ .../masterkeyfile/PassphraseEntryScoped.java | 13 +++ 10 files changed, 179 insertions(+), 182 deletions(-) delete mode 100644 src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingFinisher.java create mode 100644 src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryComponent.java create mode 100644 src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryModule.java create mode 100644 src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryResult.java create mode 100644 src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryScoped.java diff --git a/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/src/main/java/org/cryptomator/ui/common/FxmlFile.java index b8d5bbff0..bc952f9d1 100644 --- a/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -42,7 +42,7 @@ public enum FxmlFile { this.ressourcePathString = ressourcePathString; } - String getRessourcePathString() { + public String getRessourcePathString() { return ressourcePathString; } } diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java index bff757b1a..616e7e5e0 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java @@ -26,7 +26,7 @@ abstract class KeyLoadingModule { @Provides @KeyLoading @KeyLoadingScoped - static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Vault vault, Map> strategies) { + static KeyLoadingStrategy provideKeyLoadingStrategy(@KeyLoading Vault vault, Map> strategies) { try { String scheme = vault.getVaultConfigCache().get().getKeyId().getScheme(); var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme)); diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingFinisher.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingFinisher.java deleted file mode 100644 index 44d7ebfb0..000000000 --- a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingFinisher.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.cryptomator.ui.keyloading.masterkeyfile; - -import org.cryptomator.common.keychain.KeychainManager; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.integrations.keychain.KeychainAccessException; -import org.cryptomator.ui.keyloading.KeyLoading; -import org.cryptomator.ui.keyloading.KeyLoadingScoped; -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; - -@KeyLoadingScoped -class MasterkeyFileLoadingFinisher { - - private static final Logger LOG = LoggerFactory.getLogger(MasterkeyFileLoadingFinisher.class); - - private final Vault vault; - private final Optional storedPassword; - private final AtomicReference enteredPassword; - private final AtomicBoolean shouldSavePassword; - private final KeychainManager keychain; - - @Inject - MasterkeyFileLoadingFinisher(@KeyLoading Vault vault, @Named("savedPassword") Optional storedPassword, AtomicReference enteredPassword, @Named("savePassword") AtomicBoolean shouldSavePassword, KeychainManager keychain) { - this.vault = vault; - this.storedPassword = storedPassword; - this.enteredPassword = enteredPassword; - this.shouldSavePassword = shouldSavePassword; - this.keychain = keychain; - } - - public void cleanup(boolean successfullyUnlocked) { - if (successfullyUnlocked && shouldSavePassword.get()) { - savePasswordToSystemkeychain(); - } - wipePassword(storedPassword.orElse(null)); - wipePassword(enteredPassword.getAndSet(null)); - } - - private void savePasswordToSystemkeychain() { - if (keychain.isSupported()) { - try { - keychain.storePassphrase(vault.getId(), vault.getDisplayName(), 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, ' '); - } - } -} diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingModule.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingModule.java index 901eacfb9..7f55de898 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingModule.java @@ -25,30 +25,18 @@ import javax.inject.Named; import javafx.scene.Scene; import java.nio.file.Path; import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -@Module(subcomponents = {ForgetPasswordComponent.class}) +@Module(subcomponents = {ForgetPasswordComponent.class, PassphraseEntryComponent.class}) public 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 - @KeyLoadingScoped - static UserInteractionLock providePasswordEntryLock() { - return new UserInteractionLock<>(null); - } - @Provides @KeyLoadingScoped static UserInteractionLock provideMasterkeyFileProvisionLock() { @@ -77,26 +65,6 @@ public abstract class MasterkeyFileLoadingModule { return new AtomicReference<>(); } - @Provides - @KeyLoadingScoped - static AtomicReference providePassword(@Named("savedPassword") Optional storedPassword) { - return new AtomicReference<>(storedPassword.orElse(null)); - } - - @Provides - @Named("savePassword") - @KeyLoadingScoped - static AtomicBoolean provideSavePasswordFlag(@Named("savedPassword") Optional storedPassword) { - return new AtomicBoolean(storedPassword.isPresent()); - } - - @Provides - @FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD) - @KeyLoadingScoped - static Scene provideUnlockScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) { - return fxmlLoaders.createScene(FxmlFile.UNLOCK_ENTER_PASSWORD); - } - @Provides @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) @KeyLoadingScoped @@ -104,11 +72,6 @@ public abstract class MasterkeyFileLoadingModule { return fxmlLoaders.createScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE); } - @Binds - @IntoMap - @FxControllerKey(PassphraseEntryController.class) - abstract FxController bindUnlockController(PassphraseEntryController controller); - @Binds @IntoMap @FxControllerKey(SelectMasterkeyFileController.class) diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java index 1fa7dd986..2ee17a744 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java @@ -2,12 +2,14 @@ package org.cryptomator.ui.keyloading.masterkeyfile; import com.google.common.base.Preconditions; import dagger.Lazy; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.common.BackupHelper; import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.Masterkey; 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; @@ -17,6 +19,7 @@ import org.cryptomator.ui.keyloading.KeyLoadingStrategy; 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; @@ -26,6 +29,10 @@ import java.net.URI; import java.nio.CharBuffer; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; +import java.util.Optional; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; @KeyLoading @@ -36,28 +43,28 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy { private final Vault vault; private final MasterkeyFileAccess masterkeyFileAccess; private final Stage window; - private final Lazy passphraseEntryScene; private final Lazy selectMasterkeyFileScene; - private final UserInteractionLock passwordEntryLock; + private final PassphraseEntryComponent.Builder passphraseEntry; private final UserInteractionLock masterkeyFileProvisionLock; - private final AtomicReference password; private final AtomicReference filePath; - private final MasterkeyFileLoadingFinisher finisher; + private final KeychainManager keychain; - private boolean wrongPassword; + private char[] passphrase; + private boolean savePassphrase; + private boolean wrongPassphrase; @Inject - public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD) Lazy passphraseEntryScene, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy selectMasterkeyFileScene, UserInteractionLock passwordEntryLock, UserInteractionLock masterkeyFileProvisionLock, AtomicReference password, AtomicReference filePath, MasterkeyFileLoadingFinisher finisher) { + public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy selectMasterkeyFileScene, @Named("savedPassword") Optional savedPassphrase, PassphraseEntryComponent.Builder passphraseEntry, UserInteractionLock masterkeyFileProvisionLock, AtomicReference filePath, KeychainManager keychain) { this.vault = vault; this.masterkeyFileAccess = masterkeyFileAccess; this.window = window; - this.passphraseEntryScene = passphraseEntryScene; this.selectMasterkeyFileScene = selectMasterkeyFileScene; - this.passwordEntryLock = passwordEntryLock; + this.passphraseEntry = passphraseEntry; this.masterkeyFileProvisionLock = masterkeyFileProvisionLock; - this.password = password; this.filePath = filePath; - this.finisher = finisher; + this.keychain = keychain; + this.passphrase = savedPassphrase.orElse(null); + this.savePassphrase = savedPassphrase.isPresent(); } @Override @@ -68,8 +75,10 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy { if (!Files.exists(filePath)) { filePath = getAlternateMasterkeyFilePath(); } - CharSequence passphrase = getPassphrase(); - var masterkey = masterkeyFileAccess.load(filePath, passphrase); + if (passphrase == null) { + askForPassphrase(); + } + var masterkey = masterkeyFileAccess.load(filePath, CharBuffer.wrap(passphrase)); //backup if (filePath.startsWith(vault.getPath())) { try { @@ -90,8 +99,8 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy { @Override public boolean recoverFromException(MasterkeyLoadingFailedException exception) { if (exception instanceof InvalidPassphraseException) { - this.wrongPassword = true; - password.set(null); + this.wrongPassphrase = true; + this.passphrase = null; return true; // reattempting key load } else { return false; // nothing we can do @@ -100,7 +109,20 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy { @Override public void cleanup(boolean unlockedSuccessfully) { - finisher.cleanup(unlockedSuccessfully); + if (unlockedSuccessfully && savePassphrase) { + savePasswordToSystemkeychain(passphrase); + } + Arrays.fill(passphrase, '\0'); + } + + private void savePasswordToSystemkeychain(char[] passphrase) { + if (keychain.isSupported()) { + try { + keychain.storePassphrase(vault.getId(), vault.getDisplayName(), CharBuffer.wrap(passphrase)); + } catch (KeychainAccessException e) { + LOG.error("Failed to store passphrase in system keychain.", e); + } + } } private Path getAlternateMasterkeyFilePath() throws UnlockCancelledException, InterruptedException { @@ -129,21 +151,10 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy { return masterkeyFileProvisionLock.awaitInteraction(); } - private CharSequence getPassphrase() throws UnlockCancelledException, InterruptedException { - if (password.get() == null) { - return switch (askForPassphrase()) { - case PASSWORD_ENTERED -> CharBuffer.wrap(password.get()); - case CANCELED -> throw new UnlockCancelledException("Password entry cancelled."); - }; - } else { - // e.g. pre-filled from keychain or previous unlock attempt - return CharBuffer.wrap(password.get()); - } - } - - private MasterkeyFileLoadingModule.PasswordEntry askForPassphrase() throws InterruptedException { + private void askForPassphrase() throws InterruptedException { + var comp = passphraseEntry.savedPassword(passphrase).build(); Platform.runLater(() -> { - window.setScene(passphraseEntryScene.get()); + window.setScene(comp.passphraseEntryScene()); window.show(); Window owner = window.getOwner(); if (owner != null) { @@ -152,11 +163,19 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy { } else { window.centerOnScreen(); } - if (wrongPassword) { + if (wrongPassphrase) { Animations.createShakeWindowAnimation(window).play(); } }); - return passwordEntryLock.awaitInteraction(); + try { + var result = comp.result().get(); + this.passphrase = result.passphrase(); + this.savePassphrase = result.savePassphrase(); + } catch (CancellationException e) { + throw new UnlockCancelledException("Password entry cancelled."); + } catch (ExecutionException e) { + throw new MasterkeyLoadingFailedException("Failed to ask for password.", e); + } } } diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryComponent.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryComponent.java new file mode 100644 index 000000000..637ffd5c6 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryComponent.java @@ -0,0 +1,35 @@ +package org.cryptomator.ui.keyloading.masterkeyfile; + +import dagger.BindsInstance; +import dagger.Subcomponent; +import org.cryptomator.common.Nullable; +import org.cryptomator.ui.common.Animations; + +import javax.inject.Named; +import javafx.scene.Scene; +import javafx.stage.Stage; +import javafx.stage.Window; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +@PassphraseEntryScoped +@Subcomponent(modules = {PassphraseEntryModule.class}) +public interface PassphraseEntryComponent { + + @PassphraseEntryScoped + Scene passphraseEntryScene(); + + @PassphraseEntryScoped + CompletableFuture result(); + + @Subcomponent.Builder + interface Builder { + + @BindsInstance + PassphraseEntryComponent.Builder savedPassword(@Nullable @Named("savedPassword") char[] savedPassword); + + PassphraseEntryComponent build(); + } + +} diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryController.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryController.java index f6ce79e51..d048cb776 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryController.java @@ -1,16 +1,13 @@ package org.cryptomator.ui.keyloading.masterkeyfile; +import org.cryptomator.common.Nullable; import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.common.WeakBindings; -import org.cryptomator.ui.controls.FontAwesome5IconView; import org.cryptomator.ui.controls.NiceSecurePasswordField; import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent; import org.cryptomator.ui.keyloading.KeyLoading; -import org.cryptomator.ui.keyloading.KeyLoadingScoped; -import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule.PasswordEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,8 +18,8 @@ import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.application.Platform; import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ObjectBinding; import javafx.beans.binding.StringBinding; import javafx.beans.property.BooleanProperty; @@ -37,33 +34,27 @@ import javafx.scene.transform.Translate; import javafx.stage.Stage; import javafx.stage.WindowEvent; import javafx.util.Duration; -import java.util.Arrays; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.CompletableFuture; -@KeyLoadingScoped +@PassphraseEntryScoped public class PassphraseEntryController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(PassphraseEntryController.class); private final Stage window; private final Vault vault; - private final AtomicReference password; - private final AtomicBoolean savePassword; - private final Optional savedPassword; - private final UserInteractionLock passwordEntryLock; + private final CompletableFuture result; + private final char[] savedPassword; private final ForgetPasswordComponent.Builder forgetPassword; private final KeychainManager keychain; - private final ObjectBinding unlockButtonContentDisplay; - private final BooleanBinding userInteractionDisabled; - private final BooleanProperty unlockButtonDisabled; private final StringBinding vaultName; + private final BooleanProperty unlockInProgress = new SimpleBooleanProperty(); + private final ObjectBinding unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, unlockInProgress); + private final BooleanProperty unlockButtonDisabled = new SimpleBooleanProperty(); /* FXML */ public NiceSecurePasswordField passwordField; public CheckBox savePasswordCheckbox; - public FontAwesome5IconView unlockInProgressView; public ImageView face; public ImageView leftArm; public ImageView rightArm; @@ -72,29 +63,25 @@ public class PassphraseEntryController implements FxController { public Animation unlockAnimation; @Inject - public PassphraseEntryController(@KeyLoading Stage window, @KeyLoading Vault vault, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain) { + public PassphraseEntryController(@KeyLoading Stage window, @KeyLoading Vault vault, CompletableFuture result, @Nullable @Named("savedPassword") char[] savedPassword, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain) { this.window = window; this.vault = vault; - this.password = password; - this.savePassword = savePassword; + this.result = result; this.savedPassword = savedPassword; - this.passwordEntryLock = passwordEntryLock; this.forgetPassword = forgetPassword; this.keychain = keychain; - this.unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, passwordEntryLock.awaitingInteraction()); - this.userInteractionDisabled = passwordEntryLock.awaitingInteraction().not(); - this.unlockButtonDisabled = new SimpleBooleanProperty(); this.vaultName = WeakBindings.bindString(vault.displayNameProperty()); - this.window.setOnHiding(this::windowClosed); + window.setOnHiding(this::windowClosed); + result.whenCompleteAsync((r, t) -> unlockInProgress.set(false), Platform::runLater); } @FXML public void initialize() { - savePasswordCheckbox.setSelected(savedPassword.isPresent()); - if (password.get() != null) { - passwordField.setPassword(password.get()); + if (savedPassword != null) { + savePasswordCheckbox.setSelected(true); + passwordField.setPassword(savedPassword); } - unlockButtonDisabled.bind(userInteractionDisabled.or(passwordField.textProperty().isEmpty())); + unlockButtonDisabled.bind(unlockInProgress.or(passwordField.textProperty().isEmpty())); var leftArmTranslation = new Translate(24, 0); var leftArmRotation = new Rotate(60, 16, 30, 0); @@ -132,7 +119,7 @@ public class PassphraseEntryController implements FxController { new KeyFrame(Duration.millis(1000), faceVisible) // ); - passwordEntryLock.awaitingInteraction().addListener(observable -> stopUnlockAnimation()); + result.whenCompleteAsync((r, t) -> stopUnlockAnimation()); } @FXML @@ -141,26 +128,20 @@ public class PassphraseEntryController implements FxController { } private void windowClosed(WindowEvent windowEvent) { - // if not already interacted, mark this workflow as cancelled: - if (passwordEntryLock.awaitingInteraction().get()) { - LOG.debug("Unlock canceled by user."); - passwordEntryLock.interacted(PasswordEntry.CANCELED); - } + LOG.debug("Unlock canceled by user."); + result.cancel(true); } @FXML public void unlock() { LOG.trace("UnlockController.unlock()"); + unlockInProgress.set(true); CharSequence pwFieldContents = passwordField.getCharacters(); - char[] newPw = new char[pwFieldContents.length()]; + char[] pw = new char[pwFieldContents.length()]; for (int i = 0; i < pwFieldContents.length(); i++) { - newPw[i] = pwFieldContents.charAt(i); + pw[i] = pwFieldContents.charAt(i); } - char[] oldPw = password.getAndSet(newPw); - if (oldPw != null) { - Arrays.fill(oldPw, ' '); - } - passwordEntryLock.interacted(PasswordEntry.PASSWORD_ENTERED); + result.complete(new PassphraseEntryResult(pw, savePasswordCheckbox.isSelected())); startUnlockAnimation(); } @@ -184,8 +165,7 @@ public class PassphraseEntryController implements FxController { @FXML private void didClickSavePasswordCheckbox() { - savePassword.set(savePasswordCheckbox.isSelected()); - if (!savePasswordCheckbox.isSelected() && savedPassword.isPresent()) { + if (!savePasswordCheckbox.isSelected() && savedPassword != null) { forgetPassword.vault(vault).owner(window).build().showForgetPassword().thenAccept(forgotten -> savePasswordCheckbox.setSelected(!forgotten)); } } @@ -205,15 +185,15 @@ public class PassphraseEntryController implements FxController { } public ContentDisplay getUnlockButtonContentDisplay() { - return passwordEntryLock.awaitingInteraction().get() ? ContentDisplay.TEXT_ONLY : ContentDisplay.LEFT; + return unlockInProgress.get() ? ContentDisplay.LEFT : ContentDisplay.TEXT_ONLY; } - public BooleanBinding userInteractionDisabledProperty() { - return userInteractionDisabled; + public ReadOnlyBooleanProperty userInteractionDisabledProperty() { + return unlockInProgress; } public boolean isUserInteractionDisabled() { - return userInteractionDisabled.get(); + return unlockInProgress.get(); } public ReadOnlyBooleanProperty unlockButtonDisabledProperty() { @@ -227,4 +207,6 @@ public class PassphraseEntryController implements FxController { public boolean isKeychainAccessAvailable() { return keychain.isSupported(); } + + } diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryModule.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryModule.java new file mode 100644 index 000000000..aec32e5e6 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryModule.java @@ -0,0 +1,41 @@ +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.util.ResourceBundle; +import java.util.concurrent.CompletableFuture; + +@Module +abstract class PassphraseEntryModule { + + @Provides + @PassphraseEntryScoped + static CompletableFuture provideResult() { + return new CompletableFuture<>(); + } + + @Provides + @PassphraseEntryScoped + static Scene provideUnlockScene(PassphraseEntryController controller, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) { + // TODO: simplify FxmlLoaderFactory + try { + var url = FxmlLoaderFactory.class.getResource(FxmlFile.UNLOCK_ENTER_PASSWORD.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); + } + } + + +} diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryResult.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryResult.java new file mode 100644 index 000000000..2c55c203e --- /dev/null +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryResult.java @@ -0,0 +1,6 @@ +package org.cryptomator.ui.keyloading.masterkeyfile; + +// TODO needs to be public due to Dagger -.- +public record PassphraseEntryResult(char[] passphrase, boolean savePassphrase) { + +} diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryScoped.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryScoped.java new file mode 100644 index 000000000..a077bcf81 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryScoped.java @@ -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 PassphraseEntryScoped { + +}