From 25b9722019ddcabb847f53182691fe4470b75941 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 15 Dec 2021 17:19:59 +0100 Subject: [PATCH] Use CompletableFuture instead of UserInteractionLock + AtomicReference --- .../ui/keyloading/hub/AuthFlowController.java | 26 +++++----------- .../keyloading/hub/HubKeyLoadingModule.java | 18 ++--------- .../keyloading/hub/HubKeyLoadingStrategy.java | 31 +++++++++---------- .../keyloading/hub/ReceiveKeyController.java | 24 ++++---------- .../hub/RegisterDeviceController.java | 16 ++++------ .../hub/UnauthorizedDeviceController.java | 17 +++------- 6 files changed, 42 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/AuthFlowController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/AuthFlowController.java index 65c9a0df7..339030cf2 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/AuthFlowController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/AuthFlowController.java @@ -1,15 +1,12 @@ package org.cryptomator.ui.keyloading.hub; +import com.nimbusds.jose.JWEObject; import dagger.Lazy; -import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; -import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoadingScoped; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; @@ -24,29 +21,27 @@ import javafx.scene.Scene; import javafx.stage.Stage; import javafx.stage.WindowEvent; import java.net.URI; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicReference; @KeyLoadingScoped public class AuthFlowController implements FxController { - private static final Logger LOG = LoggerFactory.getLogger(AuthFlowController.class); - private final Application application; private final Stage window; private final ExecutorService executor; private final String deviceId; private final HubConfig hubConfig; private final AtomicReference tokenRef; - private final UserInteractionLock result; + private final CompletableFuture result; private final Lazy receiveKeyScene; - private final ErrorComponent.Builder errorComponent; private final ObjectProperty authUri; private final StringBinding authHost; private AuthFlowTask task; @Inject - public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference tokenRef, UserInteractionLock result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy receiveKeyScene, ErrorComponent.Builder errorComponent) { + public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference tokenRef, CompletableFuture result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy receiveKeyScene) { this.application = application; this.window = window; this.executor = executor; @@ -55,7 +50,6 @@ public class AuthFlowController implements FxController { this.tokenRef = tokenRef; this.result = result; this.receiveKeyScene = receiveKeyScene; - this.errorComponent = errorComponent; this.authUri = new SimpleObjectProperty<>(); this.authHost = Bindings.createStringBinding(this::getAuthHost, authUri); this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed); @@ -64,7 +58,7 @@ public class AuthFlowController implements FxController { @FXML public void initialize() { assert task == null; - task = new AuthFlowTask(hubConfig, new AuthFlowContext(deviceId), this::setAuthUri);; + task = new AuthFlowTask(hubConfig, new AuthFlowContext(deviceId), this::setAuthUri); task.setOnFailed(this::authFailed); task.setOnSucceeded(this::authSucceeded); executor.submit(task); @@ -88,11 +82,7 @@ public class AuthFlowController implements FxController { private void windowClosed(WindowEvent windowEvent) { // stop server, if it is still running task.cancel(); - // if not already interacted, mark this workflow as cancelled: - if (result.awaitingInteraction().get()) { - LOG.debug("Authorization cancelled by user."); - result.interacted(HubKeyLoadingModule.HubLoadingResult.CANCELLED); - } + result.cancel(true); } private void authSucceeded(WorkerStateEvent workerStateEvent) { @@ -102,11 +92,9 @@ public class AuthFlowController implements FxController { } private void authFailed(WorkerStateEvent workerStateEvent) { - result.interacted(HubKeyLoadingModule.HubLoadingResult.FAILED); window.requestFocus(); var exception = workerStateEvent.getSource().getException(); - LOG.error("Authentication failed", exception); - errorComponent.cause(exception).window(window).build().showErrorScene(); + result.completeExceptionally(exception); } /* Getter/Setter */ diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java index 04faa1493..4d4d810a3 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java @@ -17,7 +17,6 @@ import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.common.PasswordStrengthUtil; -import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoadingScoped; import org.cryptomator.ui.keyloading.KeyLoadingStrategy; @@ -28,17 +27,12 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.util.Objects; import java.util.ResourceBundle; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @Module public abstract class HubKeyLoadingModule { - public enum HubLoadingResult { - SUCCESS, - FAILED, - CANCELLED - } - @Provides @KeyLoadingScoped static HubConfig provideHubConfig(@KeyLoading Vault vault) { @@ -67,14 +61,8 @@ public abstract class HubKeyLoadingModule { @Provides @KeyLoadingScoped - static AtomicReference provideJweRef() { - return new AtomicReference<>(); - } - - @Provides - @KeyLoadingScoped - static UserInteractionLock provideResultLock() { - return new UserInteractionLock<>(null); + static CompletableFuture provideResult() { + return new CompletableFuture<>(); } @Binds diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java index 8316f640a..10d1399e2 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java @@ -8,7 +8,6 @@ import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; -import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.unlock.UnlockCancelledException; @@ -19,8 +18,9 @@ import javafx.scene.Scene; import javafx.stage.Stage; import javafx.stage.Window; import java.net.URI; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; @KeyLoading public class HubKeyLoadingStrategy implements KeyLoadingStrategy { @@ -31,37 +31,37 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy { private final Stage window; private final Lazy authFlowScene; - private final UserInteractionLock userInteraction; + private final CompletableFuture result; private final DeviceKey deviceKey; - private final AtomicReference jweRef; @Inject - public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene, UserInteractionLock userInteraction, DeviceKey deviceKey, AtomicReference jweRef) { + public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene, CompletableFuture result, DeviceKey deviceKey) { this.window = window; this.authFlowScene = authFlowScene; - this.userInteraction = userInteraction; + this.result = result; this.deviceKey = deviceKey; - this.jweRef = jweRef; } @Override public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException { Preconditions.checkArgument(keyId.getScheme().startsWith(SCHEME_PREFIX)); try { - return switch (auth()) { - case SUCCESS -> JWEHelper.decrypt(jweRef.get(), deviceKey.get().getPrivate()); - case FAILED -> throw new MasterkeyLoadingFailedException("failed to load keypair"); - case CANCELLED -> throw new UnlockCancelledException("User cancelled auth workflow"); - }; + startAuthFlow(); + var jwe = result.get(); + return JWEHelper.decrypt(jwe, deviceKey.get().getPrivate()); } catch (DeviceKey.DeviceKeyRetrievalException e) { - throw new MasterkeyLoadingFailedException("Failed to create or load device key pair", e); + throw new MasterkeyLoadingFailedException("Failed to load keypair", e); + } catch (CancellationException e) { + throw new UnlockCancelledException("User cancelled auth workflow", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new UnlockCancelledException("Loading interrupted", e); + } catch (ExecutionException e) { + throw new MasterkeyLoadingFailedException("Failed to retrieve key", e); } } - private HubKeyLoadingModule.HubLoadingResult auth() throws InterruptedException { + private void startAuthFlow() { Platform.runLater(() -> { window.setScene(authFlowScene.get()); window.show(); @@ -73,7 +73,6 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy { window.centerOnScreen(); } }); - return userInteraction.awaitInteraction(); } } diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java index 5bc8bc419..ee584b764 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java @@ -3,11 +3,9 @@ package org.cryptomator.ui.keyloading.hub; import com.nimbusds.jose.JWEObject; import dagger.Lazy; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; -import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoadingScoped; import org.eclipse.jetty.io.RuntimeIOException; @@ -31,6 +29,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.text.ParseException; import java.util.Objects; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicReference; @@ -43,24 +42,20 @@ public class ReceiveKeyController implements FxController { private final Stage window; private final String deviceId; private final String bearerToken; - private final AtomicReference jweRef; - private final UserInteractionLock result; + private final CompletableFuture result; private final Lazy registerDeviceScene; private final Lazy unauthorizedScene; - private final ErrorComponent.Builder errorComponent; private final URI vaultBaseUri; private final HttpClient httpClient; @Inject - public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference tokenRef, AtomicReference jweRef, UserInteractionLock result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy registerDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy unauthorizedScene, ErrorComponent.Builder errorComponent) { + public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference tokenRef, CompletableFuture result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy registerDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy unauthorizedScene) { this.window = window; this.deviceId = deviceId; this.bearerToken = Objects.requireNonNull(tokenRef.get()); - this.jweRef = jweRef; this.result = result; this.registerDeviceScene = registerDeviceScene; this.unauthorizedScene = unauthorizedScene; - this.errorComponent = errorComponent; this.vaultBaseUri = getVaultBaseUri(vault); this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed); this.httpClient = HttpClient.newBuilder().executor(executor).build(); @@ -94,8 +89,7 @@ public class ReceiveKeyController implements FxController { private void retrievalSucceeded(HttpResponse response) throws IOException { try { var string = HttpHelper.readBody(response); - jweRef.set(JWEObject.parse(string)); - result.interacted(HubKeyLoadingModule.HubLoadingResult.SUCCESS); + result.complete(JWEObject.parse(string)); window.close(); } catch (ParseException e) { throw new IOException("Failed to parse JWE", e); @@ -111,9 +105,7 @@ public class ReceiveKeyController implements FxController { } private Void retrievalFailed(Throwable cause) { - result.interacted(HubKeyLoadingModule.HubLoadingResult.FAILED); - LOG.error("Key retrieval failed", cause); - errorComponent.cause(cause).window(window).build().showErrorScene(); + result.completeExceptionally(cause); return null; } @@ -123,11 +115,7 @@ public class ReceiveKeyController implements FxController { } private void windowClosed(WindowEvent windowEvent) { - // if not already interacted, mark this workflow as cancelled: - if (result.awaitingInteraction().get()) { - LOG.debug("Authorization cancelled by user."); - result.interacted(HubKeyLoadingModule.HubLoadingResult.CANCELLED); - } + result.cancel(true); } private static URI appendPath(URI base, String path) { diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java index e22a4db18..721573755 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java @@ -5,10 +5,10 @@ import com.auth0.jwt.interfaces.DecodedJWT; import com.google.common.io.BaseEncoding; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.nimbusds.jose.JWEObject; import org.cryptomator.common.settings.DeviceKey; import org.cryptomator.cryptolib.common.P384KeyPair; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoadingScoped; import org.slf4j.Logger; @@ -27,6 +27,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.Objects; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicReference; @@ -41,14 +42,14 @@ public class RegisterDeviceController implements FxController { private final String bearerToken; private final String deviceId; private final P384KeyPair keyPair; - private final UserInteractionLock result; + private final CompletableFuture result; private final DecodedJWT jwt; private final HttpClient httpClient; public TextField deviceNameField; @Inject - public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, UserInteractionLock result, @Named("bearerToken") AtomicReference bearerToken) { + public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture result, @Named("bearerToken") AtomicReference bearerToken) { this.window = window; this.hubConfig = hubConfig; this.deviceId = deviceId; @@ -84,9 +85,7 @@ public class RegisterDeviceController implements FxController { } private Void registrationFailed(Throwable cause) { - result.interacted(HubKeyLoadingModule.HubLoadingResult.FAILED); - LOG.error("Key retrieval failed", cause); - // TODO errorComponent.cause(cause).window(window).build().showErrorScene(); + result.completeExceptionally(cause); return null; } @@ -96,10 +95,7 @@ public class RegisterDeviceController implements FxController { } private void windowClosed(WindowEvent windowEvent) { - // if not already interacted, mark this workflow as cancelled: - if (result.awaitingInteraction().get()) { - result.interacted(HubKeyLoadingModule.HubLoadingResult.CANCELLED); - } + result.cancel(true); } /* Getter */ diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/UnauthorizedDeviceController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/UnauthorizedDeviceController.java index a915f4016..1a7cbab02 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/UnauthorizedDeviceController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/UnauthorizedDeviceController.java @@ -1,27 +1,24 @@ package org.cryptomator.ui.keyloading.hub; +import com.nimbusds.jose.JWEObject; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoadingScoped; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.fxml.FXML; import javafx.stage.Stage; import javafx.stage.WindowEvent; +import java.util.concurrent.CompletableFuture; @KeyLoadingScoped public class UnauthorizedDeviceController implements FxController { - private static final Logger LOG = LoggerFactory.getLogger(UnauthorizedDeviceController.class); - private final Stage window; - private final UserInteractionLock result; + private final CompletableFuture result; @Inject - public UnauthorizedDeviceController(@KeyLoading Stage window, UserInteractionLock result) { + public UnauthorizedDeviceController(@KeyLoading Stage window, CompletableFuture result) { this.window = window; this.result = result; this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed); @@ -33,10 +30,6 @@ public class UnauthorizedDeviceController implements FxController { } private void windowClosed(WindowEvent windowEvent) { - // if not already interacted, mark this workflow as cancelled: - if (result.awaitingInteraction().get()) { - LOG.debug("Authorization cancelled. Device not authorized."); - result.interacted(HubKeyLoadingModule.HubLoadingResult.CANCELLED); - } + result.cancel(true); } }