From ff66e07c65bacbe8e9d0e649146174879093a327 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 25 Jul 2019 13:26:34 +0200 Subject: [PATCH] Added save password checkbox to unlock dialog --- .../ui/unlock/UnlockController.java | 86 ++++++++++++++++--- .../cryptomator/ui/unlock/UnlockModule.java | 10 --- main/ui/src/main/resources/fxml/unlock2.fxml | 2 + .../main/resources/i18n/strings.properties | 3 + 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index cf093d3a1..7b8d0aa70 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -2,22 +2,30 @@ package org.cryptomator.ui.unlock; import javafx.beans.binding.Bindings; import javafx.beans.binding.ObjectBinding; -import javafx.beans.property.ReadOnlyObjectProperty; import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; import javafx.stage.Stage; +import org.apache.commons.lang3.SystemUtils; import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException; +import org.cryptomator.keychain.KeychainAccess; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.Tasks; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.util.DialogBuilderUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.NotDirectoryException; +import java.util.Arrays; +import java.util.Optional; +import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; @UnlockScoped @@ -26,17 +34,30 @@ public class UnlockController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class); private final Stage window; - private final ReadOnlyObjectProperty vault; + private final Vault vault; private final ExecutorService executor; private final ObjectBinding unlockButtonState; + private final Optional keychainAccess; + private final ResourceBundle resourceBundle; public SecPasswordField passwordField; + public CheckBox savePassword; @Inject - public UnlockController(@UnlockWindow Stage window, @UnlockWindow ReadOnlyObjectProperty vault, ExecutorService executor) { + public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional keychainAccess, ResourceBundle resourceBundle) { this.window = window; this.vault = vault; this.executor = executor; - this.unlockButtonState = Bindings.createObjectBinding(this::getUnlockButtonState, vault.get().stateProperty()); + this.unlockButtonState = Bindings.createObjectBinding(this::getUnlockButtonState, vault.stateProperty()); + this.keychainAccess = keychainAccess; + this.resourceBundle = resourceBundle; + } + + public void initialize() { + if (keychainAccess.isPresent()) { + loadStoredPassword(); + } else { + savePassword.setDisable(true); + } } @FXML @@ -49,13 +70,13 @@ public class UnlockController implements FxController { public void unlock() { CharSequence password = passwordField.getCharacters(); Tasks.create(() -> { - vault.get().unlock(password); -// if (keychainAccess.isPresent() && savePassword.isSelected()) { -// keychainAccess.get().storePassphrase(vault.getId(), password); -// } + vault.unlock(password); + if (keychainAccess.isPresent() && savePassword.isSelected()) { + keychainAccess.get().storePassphrase(vault.getId(), password); + } }).onSuccess(() -> { passwordField.swipe(); - LOG.info("Unlock of '{}' succeeded.", vault.get().getDisplayableName()); + LOG.info("Unlock of '{}' succeeded.", vault.getDisplayableName()); window.close(); }).onError(InvalidPassphraseException.class, e -> { passwordField.selectAll(); @@ -74,14 +95,51 @@ public class UnlockController implements FxController { }).runOnce(executor); } - /* Getter/Setter */ + /* Save Password */ - public ReadOnlyObjectProperty vaultProperty() { - return vault; + @FXML + private void didClickSavePasswordCheckbox() { + if (!savePassword.isSelected() && hasStoredPassword()) { + Alert confirmDialog = DialogBuilderUtil.buildConfirmationDialog( // + resourceBundle.getString("unlock.savePassword.delete.confirmation.title"), // + resourceBundle.getString("unlock.savePassword.delete.confirmation.header"), // + resourceBundle.getString("unlock.savePassword.delete.confirmation.content"), // + SystemUtils.IS_OS_MAC_OSX ? ButtonType.CANCEL : ButtonType.OK); + Optional choice = confirmDialog.showAndWait(); + if (ButtonType.OK.equals(choice.get())) { + keychainAccess.get().deletePassphrase(vault.getId()); + } else if (ButtonType.CANCEL.equals(choice.get())) { + savePassword.setSelected(true); + } + } } + private void loadStoredPassword() { + assert keychainAccess.isPresent(); + char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId()); + if (storedPw != null) { + savePassword.setSelected(true); + passwordField.setPassword(storedPw); + passwordField.selectRange(storedPw.length, storedPw.length); + Arrays.fill(storedPw, ' '); + } + } + + private boolean hasStoredPassword() { + assert keychainAccess.isPresent(); + char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId()); + if (storedPw != null) { + Arrays.fill(storedPw, ' '); + return true; + } else { + return false; + } + } + + /* Getter/Setter */ + public Vault getVault() { - return vault.get(); + return vault; } public ObjectBinding unlockButtonStateProperty() { @@ -89,7 +147,7 @@ public class UnlockController implements FxController { } public ContentDisplay getUnlockButtonState() { - switch (vault.get().getState()) { + switch (vault.getState()) { case PROCESSING: return ContentDisplay.LEFT; default: diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index eeee3b127..77ae2ab7c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -4,14 +4,11 @@ import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.SimpleObjectProperty; import javafx.stage.Modality; import javafx.stage.Stage; import org.cryptomator.ui.common.FXMLLoaderFactory; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxControllerKey; -import org.cryptomator.ui.model.Vault; import javax.inject.Provider; import java.util.Map; @@ -38,13 +35,6 @@ abstract class UnlockModule { return stage; } - @Provides - @UnlockWindow - @UnlockScoped - static ReadOnlyObjectProperty provideVaultProperty(@UnlockWindow Vault vault) { - return new SimpleObjectProperty<>(vault); - } - // ------------------ @Binds diff --git a/main/ui/src/main/resources/fxml/unlock2.fxml b/main/ui/src/main/resources/fxml/unlock2.fxml index 65ec047bf..4d3dcc82b 100644 --- a/main/ui/src/main/resources/fxml/unlock2.fxml +++ b/main/ui/src/main/resources/fxml/unlock2.fxml @@ -9,6 +9,7 @@ + +