Browse Source

Merge pull request #2612 from cryptomator/feature/2597-no-keychain-dialog

Feature: Show dialog for hub vaults, if keychain is disabled'

Closes #2597
Armin Schrenk 2 years ago
parent
commit
6704bb1f8c

+ 1 - 0
src/main/java/org/cryptomator/ui/common/FxmlFile.java

@@ -13,6 +13,7 @@ public enum FxmlFile {
 	FORGET_PASSWORD("/fxml/forget_password.fxml"), //
 	HEALTH_START("/fxml/health_start.fxml"), //
 	HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
+	HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.fxml"), //
 	HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
 	HUB_LICENSE_EXCEEDED("/fxml/hub_license_exceeded.fxml"), //
 	HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //

+ 12 - 0
src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java

@@ -87,6 +87,13 @@ public abstract class HubKeyLoadingModule {
 	@StringKey(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS)
 	abstract KeyLoadingStrategy bindHubKeyLoadingStrategyToHubHttps(HubKeyLoadingStrategy strategy);
 
+	@Provides
+	@FxmlScene(FxmlFile.HUB_NO_KEYCHAIN)
+	@KeyLoadingScoped
+	static Scene provideHubNoKeychainScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
+		return fxmlLoaders.createScene(FxmlFile.HUB_NO_KEYCHAIN);
+	}
+
 	@Provides
 	@FxmlScene(FxmlFile.HUB_AUTH_FLOW)
 	@KeyLoadingScoped
@@ -136,6 +143,11 @@ public abstract class HubKeyLoadingModule {
 		return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
 	}
 
+	@Binds
+	@IntoMap
+	@FxControllerKey(NoKeychainController.class)
+	abstract FxController bindNoKeychainController(NoKeychainController controller);
+
 	@Binds
 	@IntoMap
 	@FxControllerKey(AuthFlowController.class)

+ 16 - 4
src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java

@@ -3,6 +3,8 @@ package org.cryptomator.ui.keyloading.hub;
 import com.google.common.base.Preconditions;
 import com.nimbusds.jose.JWEObject;
 import dagger.Lazy;
+import org.cryptomator.common.keychain.KeychainManager;
+import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
 import org.cryptomator.common.settings.DeviceKey;
 import org.cryptomator.cryptolib.api.Masterkey;
 import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
@@ -31,15 +33,19 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
 	static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https";
 
 	private final Stage window;
+	private final KeychainManager keychainManager;
 	private final Lazy<Scene> authFlowScene;
+	private final Lazy<Scene> noKeychainScene;
 	private final CompletableFuture<JWEObject> result;
 	private final DeviceKey deviceKey;
 
 	@Inject
-	public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, CompletableFuture<JWEObject> result, DeviceKey deviceKey, @Named("windowTitle") String windowTitle) {
+	public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<JWEObject> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
 		this.window = window;
+		this.keychainManager = keychainManager;
 		window.setTitle(windowTitle);
 		this.authFlowScene = authFlowScene;
+		this.noKeychainScene = noKeychainScene;
 		this.result = result;
 		this.deviceKey = deviceKey;
 	}
@@ -48,10 +54,16 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
 	public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
 		Preconditions.checkArgument(keyId.getScheme().startsWith(SCHEME_PREFIX));
 		try {
+			if (!keychainManager.isSupported()) {
+				throw new NoKeychainAccessProviderException();
+			}
 			var keypair = deviceKey.get();
-			startAuthFlow();
+			showWindow(authFlowScene);
 			var jwe = result.get();
 			return JWEHelper.decrypt(jwe, keypair.getPrivate());
+		} catch (NoKeychainAccessProviderException e) {
+			showWindow(noKeychainScene);
+			throw new UnlockCancelledException("Unlock canceled due to missing prerequisites", e);
 		} catch (DeviceKey.DeviceKeyRetrievalException e) {
 			throw new MasterkeyLoadingFailedException("Failed to load keypair", e);
 		} catch (CancellationException e) {
@@ -64,9 +76,9 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
 		}
 	}
 
-	private void startAuthFlow() {
+	private void showWindow(Lazy<Scene> scene) {
 		Platform.runLater(() -> {
-			window.setScene(authFlowScene.get());
+			window.setScene(scene.get());
 			window.show();
 			Window owner = window.getOwner();
 			if (owner != null) {

+ 31 - 0
src/main/java/org/cryptomator/ui/keyloading/hub/NoKeychainController.java

@@ -0,0 +1,31 @@
+package org.cryptomator.ui.keyloading.hub;
+
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.fxapp.FxApplicationWindows;
+import org.cryptomator.ui.keyloading.KeyLoading;
+import org.cryptomator.ui.preferences.SelectedPreferencesTab;
+
+import javax.inject.Inject;
+import javafx.stage.Stage;
+
+public class NoKeychainController implements FxController {
+
+	private final Stage window;
+	private final FxApplicationWindows appWindows;
+
+	@Inject
+	public NoKeychainController(@KeyLoading Stage window, FxApplicationWindows appWindows) {
+		this.window = window;
+		this.appWindows = appWindows;
+	}
+
+
+	public void cancel() {
+		window.close();
+	}
+
+	public void openPreferences() {
+		appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
+		window.close();
+	}
+}

+ 56 - 0
src/main/resources/fxml/hub_no_keychain.fxml

@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ButtonBar?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.Group?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.Region?>
+<?import javafx.scene.layout.StackPane?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.shape.Circle?>
+<?import org.cryptomator.ui.controls.FormattedLabel?>
+<HBox xmlns:fx="http://javafx.com/fxml"
+	  xmlns="http://javafx.com/javafx"
+	  fx:controller="org.cryptomator.ui.keyloading.hub.NoKeychainController"
+	  minWidth="400"
+	  maxWidth="400"
+	  minHeight="145"
+	  spacing="12"
+	  alignment="TOP_LEFT">
+	<padding>
+		<Insets topRightBottomLeft="12"/>
+	</padding>
+	<children>
+		<Group>
+			<StackPane>
+				<padding>
+					<Insets topRightBottomLeft="6"/>
+				</padding>
+				<Circle styleClass="glyph-icon-primary" radius="24"/>
+				<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
+			</StackPane>
+		</Group>
+
+		<VBox HBox.hgrow="ALWAYS">
+			<Label styleClass="label-large" text="%hub.noKeychain.message" wrapText="true" textAlignment="LEFT">
+				<padding>
+					<Insets bottom="6" top="6"/>
+				</padding>
+			</Label>
+
+			<FormattedLabel format="%hub.noKeychain.description" arg1="%preferences.general.keychainBackend" wrapText="true" textAlignment="LEFT"/>
+
+			<Region VBox.vgrow="ALWAYS" minHeight="18"/>
+			<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
+				<buttons>
+					<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="false" cancelButton="true" onAction="#cancel"/>
+					<Button text="%hub.noKeychain.openBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#openPreferences"/>
+				</buttons>
+			</ButtonBar>
+		</VBox>
+	</children>
+</HBox>

+ 3 - 0
src/main/resources/i18n/strings.properties

@@ -130,6 +130,9 @@ unlock.error.invalidMountPoint.notExisting=Mount point "%s" is not a directory,
 unlock.error.invalidMountPoint.existing=Mount point "%s" already exists or parent folder is missing.
 unlock.error.invalidMountPoint.driveLetterOccupied=Drive Letter "%s" is already in use.
 ## Hub
+hub.noKeychain.message=Unable to access device key
+hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.
+hub.noKeychain.openBtn=Open Preferences
 ### Waiting
 hub.auth.message=Waiting for authentication…
 hub.auth.description=You should automatically be redirected to the login page.