Added save password checkbox to unlock dialog

This commit is contained in:
Sebastian Stenzel
2019-07-25 13:26:34 +02:00
parent 4138747fc3
commit ff66e07c65
4 changed files with 77 additions and 24 deletions

View File

@@ -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> vault;
private final Vault vault;
private final ExecutorService executor;
private final ObjectBinding<ContentDisplay> unlockButtonState;
private final Optional<KeychainAccess> keychainAccess;
private final ResourceBundle resourceBundle;
public SecPasswordField passwordField;
public CheckBox savePassword;
@Inject
public UnlockController(@UnlockWindow Stage window, @UnlockWindow ReadOnlyObjectProperty<Vault> vault, ExecutorService executor) {
public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional<KeychainAccess> 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<Vault> 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<ButtonType> 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<ContentDisplay> 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:

View File

@@ -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<Vault> provideVaultProperty(@UnlockWindow Vault vault) {
return new SimpleObjectProperty<>(vault);
}
// ------------------
@Binds

View File

@@ -9,6 +9,7 @@
<?import javafx.scene.text.TextFlow?>
<?import org.cryptomator.ui.controls.SecPasswordField?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.CheckBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.unlock.UnlockController"
@@ -23,6 +24,7 @@
<Text text="${controller.vault.displayableName}"/>
</TextFlow>
<SecPasswordField fx:id="passwordField"/>
<CheckBox fx:id="savePassword" text="TODO save password" onAction="#didClickSavePasswordCheckbox"/>
<HBox>
<Button text="TODO cancel" cancelButton="true" onAction="#cancel"/>
<Region HBox.hgrow="ALWAYS"/>

View File

@@ -6,4 +6,7 @@ preferences.autoUpdateCheck=Check for updates automatically
preferences.debugLogging=Enable debug logging
preferences.theme=Look & Feel
preferences.volumeType=Volume type
unlock.savePassword.delete.confirmation.title=Delete Saved Password
unlock.savePassword.delete.confirmation.header=Do you really want to delete the saved password of this vault?
unlock.savePassword.delete.confirmation.content=The saved password of this vault will be immediately deleted from your system keychain. If you'd like to save your password again, you'd have to unlock your vault with the "Save Password" option enabled.
vaultlist.emptyList.onboardingInstruction=Click here to add a vault