Bläddra i källkod

Added unlock window (issue #928)

Sebastian Stenzel 6 år sedan
förälder
incheckning
3c574b97cb

+ 3 - 2
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

+ 28 - 1
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> vault;
+	private final ExecutorService executor;
+	private final UnlockComponent.Builder unlockWindow;
 
 	@Inject
-	VaultDetailController(ObjectProperty<Vault> vault) {
+	VaultDetailController(ObjectProperty<Vault> vault, ExecutorService executor, UnlockComponent.Builder unlockWindow) {
 		this.vault = vault;
+		this.executor = executor;
+		this.unlockWindow = unlockWindow;
 	}
 
 	public ReadOnlyObjectProperty<Vault> 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);
+	}
 }

+ 23 - 2
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> state = new SimpleObjectProperty<State>(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();

+ 1 - 1
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

+ 39 - 0
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();
+	}
+
+}

+ 99 - 0
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> vault;
+	private final ExecutorService executor;
+	private final ObjectBinding<ContentDisplay> unlockButtonState;
+	public SecPasswordField passwordField;
+
+	@Inject
+	public UnlockController(@UnlockWindow Stage window, @UnlockWindow ReadOnlyObjectProperty<Vault> 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<Vault> vaultProperty() {
+		return vault;
+	}
+
+	public Vault getVault() {
+		return vault.get();
+	}
+
+	public ObjectBinding<ContentDisplay> unlockButtonStateProperty() {
+		return unlockButtonState;
+	}
+
+	public ContentDisplay getUnlockButtonState() {
+		switch (vault.get().getState()) {
+			case PROCESSING:
+				return ContentDisplay.LEFT;
+			default:
+				return ContentDisplay.TEXT_ONLY;
+		}
+	}
+}

+ 55 - 0
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<Class<? extends FxController>, Provider<FxController>> 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<Vault> provideVaultProperty(@UnlockWindow Vault vault) {
+		return new SimpleObjectProperty<>(vault);
+	}
+
+	// ------------------
+
+	@Binds
+	@IntoMap
+	@FxControllerKey(UnlockController.class)
+	abstract FxController bindUnlockController(UnlockController controller);
+
+
+}

+ 13 - 0
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 {
+
+}

+ 14 - 0
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 {
+
+}

+ 55 - 0
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";
+}

+ 36 - 0
main/ui/src/main/resources/fxml/unlock2.fxml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.Region?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.text.Text?>
+<?import javafx.scene.text.TextFlow?>
+<?import org.cryptomator.ui.controls.SecPasswordField?>
+<?import javafx.scene.control.ProgressIndicator?>
+<VBox xmlns="http://javafx.com/javafx"
+	  xmlns:fx="http://javafx.com/fxml"
+	  fx:controller="org.cryptomator.ui.unlock.UnlockController"
+	  minWidth="300"
+	  spacing="6">
+	<padding>
+		<Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/>
+	</padding>
+	<children>
+		<TextFlow>
+			<Text text="TODO Enter Password for "/>
+			<Text text="${controller.vault.displayableName}"/>
+		</TextFlow>
+		<SecPasswordField fx:id="passwordField"/>
+		<HBox>
+			<Button text="TODO cancel" cancelButton="true" onAction="#cancel"/>
+			<Region HBox.hgrow="ALWAYS"/>
+			<Button text="TODO unlock" defaultButton="true" onAction="#unlock" contentDisplay="${controller.unlockButtonState}" disable="${passwordField.text.empty}">
+				<graphic>
+					<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
+				</graphic>
+			</Button>
+		</HBox>
+	</children>
+</VBox>

+ 5 - 1
main/ui/src/main/resources/fxml/vault_detail.fxml

@@ -3,6 +3,7 @@
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.layout.VBox?>
+<?import javafx.scene.control.Button?>
 <VBox xmlns="http://javafx.com/javafx"
 	  xmlns:fx="http://javafx.com/fxml"
 	  fx:controller="org.cryptomator.ui.mainwindow.VaultDetailController"
@@ -11,6 +12,9 @@
 		<Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/>
 	</padding>
 	<children>
-		<Label text="${controller.vault}"/>
+		<Label text="${controller.vault.displayableName}"/>
+
+		<Button text="TODO unlock" onAction="#unlock" visible="${controller.vault.locked}"/>
+		<Button text="TODO lock" onAction="#lock" visible="${controller.vault.unlocked}"/>
 	</children>
 </VBox>