From 3c574b97cb07219013c7e8f88b428598b8876203 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 24 Jul 2019 14:58:43 +0200 Subject: [PATCH] Added unlock window (issue #928) --- .../ui/mainwindow/MainWindowModule.java | 5 +- .../ui/mainwindow/VaultDetailController.java | 29 +++++- .../java/org/cryptomator/ui/model/Vault.java | 25 ++++- .../ui/preferences/PreferencesModule.java | 2 +- .../ui/unlock/UnlockComponent.java | 39 ++++++++ .../ui/unlock/UnlockController.java | 99 +++++++++++++++++++ .../cryptomator/ui/unlock/UnlockModule.java | 55 +++++++++++ .../cryptomator/ui/unlock/UnlockScoped.java | 13 +++ .../cryptomator/ui/unlock/UnlockWindow.java | 14 +++ main/ui/src/main/resources/css/theme.css | 55 +++++++++++ main/ui/src/main/resources/fxml/unlock2.fxml | 36 +++++++ .../src/main/resources/fxml/vault_detail.fxml | 6 +- 12 files changed, 371 insertions(+), 7 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockScoped.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWindow.java create mode 100644 main/ui/src/main/resources/fxml/unlock2.fxml diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java index 66c134c3b..d4be46c4d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java @@ -10,12 +10,13 @@ import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; import org.cryptomator.ui.common.FXMLLoaderFactory; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxControllerKey; +import org.cryptomator.ui.unlock.UnlockComponent; import javax.inject.Provider; import java.util.Map; -@Module(subcomponents = {AddVaultWizardComponent.class}) -public abstract class MainWindowModule { +@Module(subcomponents = {AddVaultWizardComponent.class, UnlockComponent.class}) +abstract class MainWindowModule { @Provides @MainWindow diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java index 87ecd363c..e5e539f12 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java @@ -2,19 +2,31 @@ package org.cryptomator.ui.mainwindow; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.fxml.FXML; import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.Tasks; import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.unlock.UnlockComponent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.util.concurrent.ExecutorService; @MainWindowScoped public class VaultDetailController implements FxController { + private static final Logger LOG = LoggerFactory.getLogger(VaultDetailController.class); + private final ReadOnlyObjectProperty vault; + private final ExecutorService executor; + private final UnlockComponent.Builder unlockWindow; @Inject - VaultDetailController(ObjectProperty vault) { + VaultDetailController(ObjectProperty vault, ExecutorService executor, UnlockComponent.Builder unlockWindow) { this.vault = vault; + this.executor = executor; + this.unlockWindow = unlockWindow; } public ReadOnlyObjectProperty vaultProperty() { @@ -25,4 +37,19 @@ public class VaultDetailController implements FxController { return vault.get(); } + @FXML + public void unlock() { + unlockWindow.vault(vault.get()).build().showUnlockWindow(); + } + + @FXML + public void lock() { + Tasks.create(() -> { + vault.get().lock(false); + }).onSuccess(() -> { + LOG.trace("Regular unmount succeeded."); + }).onError(Exception.class, e -> { + // TODO + }).runOnce(executor); + } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java index 24f5f7626..06a42fcb3 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java @@ -13,6 +13,7 @@ import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.Binding; import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.StringBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; @@ -62,6 +63,8 @@ public class Vault { private final ObjectProperty state = new SimpleObjectProperty(State.LOCKED); private final StringBinding displayableName; private final StringBinding displayablePath; + private final BooleanBinding locked; + private final BooleanBinding unlocked; private Volume volume; @@ -77,6 +80,8 @@ public class Vault { this.displayableName = Bindings.createStringBinding(this::getDisplayableName, vaultSettings.path()); this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path()); + this.locked = Bindings.createBooleanBinding(this::isLocked, state); + this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state); } // ****************************************************************************** @@ -192,11 +197,27 @@ public class Vault { public State getState() { return state.get(); } - + + public BooleanBinding lockedProperty() { + return locked; + } + + public boolean isLocked() { + return state.get() == State.LOCKED; + } + + public BooleanBinding unlockedProperty() { + return unlocked; + } + + public boolean isUnlocked() { + return state.get() == State.UNLOCKED; + } + public StringBinding displayableNameProperty() { return displayableName; } - + public String getDisplayableName() { Path p = vaultSettings.path().get(); return p.getFileName().toString(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java index c3fee4c10..ac42c9887 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java @@ -14,7 +14,7 @@ import javax.inject.Provider; import java.util.Map; @Module -public abstract class PreferencesModule { +abstract class PreferencesModule { @Provides @PreferencesWindow diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java new file mode 100644 index 000000000..eb8255216 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ +package org.cryptomator.ui.unlock; + +import dagger.BindsInstance; +import dagger.Subcomponent; +import javafx.stage.Stage; +import org.cryptomator.ui.common.FXMLLoaderFactory; +import org.cryptomator.ui.model.Vault; + +@UnlockScoped +@Subcomponent(modules = {UnlockModule.class}) +public interface UnlockComponent { + + @UnlockWindow + Stage window(); + + @UnlockWindow + FXMLLoaderFactory fxmlLoaders(); + + default void showUnlockWindow() { + Stage stage = window(); + fxmlLoaders().setScene("/fxml/unlock2.fxml", stage); // TODO rename fxml file + stage.show(); + } + + @Subcomponent.Builder + interface Builder { + + @BindsInstance + Builder vault(@UnlockWindow Vault vault); + + UnlockComponent build(); + } + +} 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 new file mode 100644 index 000000000..cf093d3a1 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -0,0 +1,99 @@ +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.ContentDisplay; +import javafx.stage.Stage; +import org.cryptomator.cryptolib.api.InvalidPassphraseException; +import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.NotDirectoryException; +import java.util.concurrent.ExecutorService; + +@UnlockScoped +public class UnlockController implements FxController { + + private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class); + + private final Stage window; + private final ReadOnlyObjectProperty vault; + private final ExecutorService executor; + private final ObjectBinding unlockButtonState; + public SecPasswordField passwordField; + + @Inject + public UnlockController(@UnlockWindow Stage window, @UnlockWindow ReadOnlyObjectProperty vault, ExecutorService executor) { + this.window = window; + this.vault = vault; + this.executor = executor; + this.unlockButtonState = Bindings.createObjectBinding(this::getUnlockButtonState, vault.get().stateProperty()); + } + + @FXML + public void cancel() { + LOG.debug("Unlock canceled by user."); + window.close(); + } + + @FXML + public void unlock() { + CharSequence password = passwordField.getCharacters(); + Tasks.create(() -> { + vault.get().unlock(password); +// if (keychainAccess.isPresent() && savePassword.isSelected()) { +// keychainAccess.get().storePassphrase(vault.getId(), password); +// } + }).onSuccess(() -> { + passwordField.swipe(); + LOG.info("Unlock of '{}' succeeded.", vault.get().getDisplayableName()); + window.close(); + }).onError(InvalidPassphraseException.class, e -> { + passwordField.selectAll(); + passwordField.requestFocus(); + }).onError(UnsupportedVaultFormatException.class, e -> { + // TODO + }).onError(NotDirectoryException.class, e -> { + LOG.error("Unlock failed. Mount point not a directory: {}", e.getMessage()); + // TODO + }).onError(DirectoryNotEmptyException.class, e -> { + LOG.error("Unlock failed. Mount point not empty: {}", e.getMessage()); + // TODO + }).onError(Exception.class, e -> { // including RuntimeExceptions + LOG.error("Unlock failed for technical reasons.", e); + // TODO + }).runOnce(executor); + } + + /* Getter/Setter */ + + public ReadOnlyObjectProperty vaultProperty() { + return vault; + } + + public Vault getVault() { + return vault.get(); + } + + public ObjectBinding unlockButtonStateProperty() { + return unlockButtonState; + } + + public ContentDisplay getUnlockButtonState() { + switch (vault.get().getState()) { + case PROCESSING: + return ContentDisplay.LEFT; + default: + return ContentDisplay.TEXT_ONLY; + } + } +} 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 new file mode 100644 index 000000000..b925e404d --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -0,0 +1,55 @@ +package org.cryptomator.ui.unlock; + +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; + +@Module +abstract class UnlockModule { + + @Provides + @UnlockWindow + @UnlockScoped + static FXMLLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories) { + return new FXMLLoaderFactory(factories); + } + + @Provides + @UnlockWindow + @UnlockScoped + static Stage provideStage() { + Stage stage = new Stage(); + stage.setMinWidth(300); + stage.setMinHeight(200); + stage.initModality(Modality.APPLICATION_MODAL); + return stage; + } + + @Provides + @UnlockWindow + @UnlockScoped + static ReadOnlyObjectProperty provideVaultProperty(@UnlockWindow Vault vault) { + return new SimpleObjectProperty<>(vault); + } + + // ------------------ + + @Binds + @IntoMap + @FxControllerKey(UnlockController.class) + abstract FxController bindUnlockController(UnlockController controller); + + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockScoped.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockScoped.java new file mode 100644 index 000000000..4899d2246 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockScoped.java @@ -0,0 +1,13 @@ +package org.cryptomator.ui.unlock; + +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 UnlockScoped { + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWindow.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWindow.java new file mode 100644 index 000000000..67451591b --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWindow.java @@ -0,0 +1,14 @@ +package org.cryptomator.ui.unlock; + +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) +public @interface UnlockWindow { + +} diff --git a/main/ui/src/main/resources/css/theme.css b/main/ui/src/main/resources/css/theme.css index c960f5853..46a2d41e3 100644 --- a/main/ui/src/main/resources/css/theme.css +++ b/main/ui/src/main/resources/css/theme.css @@ -265,3 +265,58 @@ .menu-item:focused { -fx-background-color: CONTROL_BG_ARMED; } + +/**************************************************************************** + * * + * ProgressIndicator * + * Derived from aquafx-project.com, (C) Claudine Zillmann, see NOTICE.md * + * * + ****************************************************************************/ + +.progress-indicator { + -fx-indeterminate-segment-count: 12; + -fx-spin-enabled: true; +} +.progress-indicator:indeterminate > .spinner { + -fx-padding: 0.083333em; +} +.progress-indicator:indeterminate .segment { + -fx-background-color: rgb(95.0, 95.0, 98.0), rgb(122.0, 122.0, 125.0); + -fx-background-insets:0.0, 0.5; +} +.progress-indicator:indeterminate .segment0 { + -fx-shape:"m 12.007729,4.9541827 c -0.49762,0.7596865 0.893181,1.6216808 1.327833,0.7666252 L 15.456199,2.0477574 C 15.942094,1.2061627 14.61426,0.43953765 14.128365,1.2811324 z"; +} +.progress-indicator:indeterminate .segment1 { + -fx-shape:"m 9.2224559,4.62535 c -0.051108,0.9067177 1.5843581,0.957826 1.5332501,0 l 0,-4.24127319 c 0,-0.9717899 -1.5332501,-0.9717899 -1.5332501,0 z"; +} +.progress-indicator:indeterminate .segment2 { + -fx-shape:"M 8.0465401,4.9030617 C 8.5441601,5.6627485 7.1533584,6.5247425 6.7187068,5.6696872 L 4.5980702,1.9966363 C 4.1121752,1.1550418 5.4400085,0.38841683 5.9259035,1.2300114 z"; +} +.progress-indicator:indeterminate .segment3 { + -fx-shape:"M 5.7330066,6.5305598 C 6.5579512,6.9103162 5.8366865,8.3790371 5.0144939,7.8850315 L 1.2677551,5.8974832 C 0.409277,5.4420823 1.1277888,4.0876101 1.9862674,4.5430105 z"; +} +.progress-indicator:indeterminate .segment4 { + -fx-shape:"m 0.42171041,9.2083842 c -0.90671825,-0.051108 -0.95782608,1.5843588 0,1.5332498 l 4.24127319,0 c 0.9717899,0 0.9717899,-1.5332498 0,-1.5332498 z"; +} +.progress-indicator:indeterminate .segment5 { + -fx-shape:"M 5.7330066,13.443113 C 6.5579512,13.063356 5.8366865,11.594635 5.0144939,12.088641 L 1.2677551,14.076189 C 0.409277,14.53159 1.1277888,15.886062 1.9862674,15.430662 z"; +} +.progress-indicator:indeterminate .segment6 { + -fx-shape:"M 8.0465401,15.070611 C 8.5441601,14.310924 7.1533584,13.44893 6.7187068,14.303985 l -2.1206366,3.673051 c -0.485895,0.841595 0.8419383,1.60822 1.3278333,0.766625 z"; +} +.progress-indicator:indeterminate .segment7 { + -fx-shape:"m 9.2224559,19.539943 c -0.051108,0.906718 1.5843581,0.957826 1.5332501,0 l 0,-4.241273 c 0,-0.97179 -1.5332501,-0.97179 -1.5332501,0 z"; +} +.progress-indicator:indeterminate .segment8 { + -fx-shape:"m 12.10997,15.070611 c -0.49762,-0.759687 0.893182,-1.621681 1.327834,-0.766626 l 2.120636,3.673051 c 0.485895,0.841595 -0.841938,1.60822 -1.327833,0.766625 z"; +} +.progress-indicator:indeterminate .segment9 { + -fx-shape:"m 14.423504,13.443113 c -0.824945,-0.379757 -0.10368,-1.848478 0.718512,-1.354472 l 3.746739,1.987548 c 0.858478,0.455401 0.139967,1.809873 -0.718512,1.354473 z"; +} +.progress-indicator:indeterminate .segment10 { + -fx-shape:"m 15.372451,9.2445322 c -0.906719,-0.051108 -0.957826,1.5843588 0,1.5332498 l 4.241273,0 c 0.97179,0 0.97179,-1.5332498 0,-1.5332498 z"; +} +.progress-indicator:indeterminate .segment11 { + -fx-shape:"m 14.321262,6.5816808 c -0.824944,0.3797564 -0.10368,1.8484772 0.718513,1.3544717 L 18.786514,5.9486042 C 19.644992,5.4932031 18.92648,4.1387308 18.068001,4.5941315 z"; +} \ No newline at end of file diff --git a/main/ui/src/main/resources/fxml/unlock2.fxml b/main/ui/src/main/resources/fxml/unlock2.fxml new file mode 100644 index 000000000..65ec047bf --- /dev/null +++ b/main/ui/src/main/resources/fxml/unlock2.fxml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/ui/src/main/resources/fxml/vault_detail.fxml b/main/ui/src/main/resources/fxml/vault_detail.fxml index 123387984..4fa72504b 100644 --- a/main/ui/src/main/resources/fxml/vault_detail.fxml +++ b/main/ui/src/main/resources/fxml/vault_detail.fxml @@ -3,6 +3,7 @@ + -