redirectUriConsumer) {
- this.redirectUriConsumer = redirectUriConsumer;
- }
-
- @Override
- protected EciesParams call() throws Exception {
- try (var receiver = AuthReceiver.start()) {
- var redirectUri = receiver.getRedirectURL();
- Platform.runLater(() -> redirectUriConsumer.accept(redirectUri));
- LOG.debug("Waiting for key on {}", redirectUri);
- return receiver.receive();
- }
- }
-}
diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/AuthReceiver.java b/src/main/java/org/cryptomator/ui/keyloading/hub/AuthReceiver.java
deleted file mode 100644
index bb2c5b76a..000000000
--- a/src/main/java/org/cryptomator/ui/keyloading/hub/AuthReceiver.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package org.cryptomator.ui.keyloading.hub;
-
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.servlets.CrossOriginFilter;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.util.EnumSet;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * A basic implementation for RFC 8252, Section 7.3:
- *
- * We're spawning a local http server on a system-assigned high port and
- * use http://127.0.0.1:{PORT}/success as a redirect URI.
- *
- * Furthermore, we can deliver a html response to inform the user that the
- * auth workflow finished and she can close the browser tab.
- */
-class AuthReceiver implements AutoCloseable {
-
- private static final String REDIRECT_SCHEME = "http";
- private static final String LOOPBACK_ADDR = "127.0.0.1";
- private static final String JSON_200 = """
- {"status": "success"}
- """;
- private static final String JSON_400 = """
- {"status": "missing param"}
- """;
-
- private final Server server;
- private final ServerConnector connector;
- private final CallbackServlet servlet;
-
- private AuthReceiver(Server server, ServerConnector connector, CallbackServlet servlet) {
- assert server.isRunning();
- this.server = server;
- this.connector = connector;
- this.servlet = servlet;
- }
-
- public URI getRedirectURL() {
- try {
- return new URI(REDIRECT_SCHEME, null, LOOPBACK_ADDR, connector.getLocalPort(), null, null, null);
- } catch (URISyntaxException e) {
- throw new IllegalStateException("URI constructed from well-formed components.", e);
- }
- }
-
- public static AuthReceiver start() throws Exception {
- var server = new Server();
- var context = new ServletContextHandler();
-
- var corsFilter = new FilterHolder(new CrossOriginFilter());
- corsFilter.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*"); // TODO restrict to hub host
- context.addFilter(corsFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
-
- var servlet = new CallbackServlet();
- context.addServlet(new ServletHolder(servlet), "/*");
-
- var connector = new ServerConnector(server);
- connector.setPort(0);
- connector.setHost(LOOPBACK_ADDR);
- server.setConnectors(new Connector[]{connector});
- server.setHandler(context);
- server.start();
- return new AuthReceiver(server, connector, servlet);
- }
-
- public EciesParams receive() throws InterruptedException {
- return servlet.receivedKeys.take();
- }
-
- @Override
- public void close() throws Exception {
- server.stop();
- }
-
- private static class CallbackServlet extends HttpServlet {
-
- private final BlockingQueue receivedKeys = new LinkedBlockingQueue<>();
-
- // TODO change to POST?
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
- var m = req.getParameter("m"); // encrypted masterkey
- var epk = req.getParameter("epk"); // ephemeral public key
- byte[] response;
- if (m != null && epk != null) {
- res.setStatus(HttpServletResponse.SC_OK);
- response = JSON_200.getBytes(StandardCharsets.UTF_8);
- } else {
- res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- response = JSON_400.getBytes(StandardCharsets.UTF_8);
- }
- res.setContentType("application/json;charset=utf-8");
- res.setContentLength(response.length);
- res.getOutputStream().write(response);
- res.getOutputStream().flush();
-
- // the following line might trigger a server shutdown,
- // so let's make sure the response is flushed first
- if (m != null && epk != null) {
- receivedKeys.add(new EciesParams(m, epk));
- }
- }
- }
-}
diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/HttpHelper.java b/src/main/java/org/cryptomator/ui/keyloading/hub/HttpHelper.java
new file mode 100644
index 000000000..7de2902be
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HttpHelper.java
@@ -0,0 +1,31 @@
+package org.cryptomator.ui.keyloading.hub;
+
+import com.google.common.io.CharStreams;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+
+class HttpHelper {
+
+ public static String readBody(HttpResponse response) throws IOException {
+ try (var in = response.body(); var reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
+ return CharStreams.toString(reader);
+ }
+ }
+
+ public static JsonElement parseBody(HttpResponse response) throws IOException {
+ try (InputStream in = response.body(); Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
+ return JsonParser.parseReader(reader);
+ } catch (JsonParseException e) {
+ throw new IOException("Failed to parse JSON", e);
+ }
+ }
+
+}
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 9c5d7dd94..5de793857 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java
@@ -17,6 +17,7 @@ import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
+import javax.inject.Named;
import javafx.scene.Scene;
import java.net.URI;
import java.security.KeyPair;
@@ -26,7 +27,7 @@ import java.util.concurrent.atomic.AtomicReference;
@Module
public abstract class HubKeyLoadingModule {
- public enum AuthFlow {
+ public enum HubLoadingResult {
SUCCESS,
FAILED,
CANCELLED
@@ -39,23 +40,24 @@ public abstract class HubKeyLoadingModule {
}
@Provides
+ @Named("bearerToken")
@KeyLoadingScoped
- static AtomicReference provideAuthParamsRef() {
+ static AtomicReference provideBearerTokenRef() {
return new AtomicReference<>();
}
@Provides
@KeyLoadingScoped
- static UserInteractionLock provideAuthFlowLock() {
+ static AtomicReference provideEciesParamsRef() {
+ return new AtomicReference<>();
+ }
+
+ @Provides
+ @KeyLoadingScoped
+ static UserInteractionLock provideResultLock() {
return new UserInteractionLock<>(null);
}
- @Provides
- @KeyLoadingScoped
- static AtomicReference provideHubUri() {
- return new AtomicReference<>();
- }
-
@Binds
@IntoMap
@KeyLoadingScoped
@@ -75,10 +77,18 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_P12);
}
+ @Provides
+ @FxmlScene(FxmlFile.HUB_AUTH_FLOW)
+ @KeyLoadingScoped
+ static Scene provideHubAuthFlowScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
+ return fxmlLoaders.createScene(FxmlFile.HUB_AUTH_FLOW);
+ }
+
+
@Provides
@FxmlScene(FxmlFile.HUB_RECEIVE_KEY)
@KeyLoadingScoped
- static Scene provideHubAuthScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
+ static Scene provideHubReceiveKeyScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_RECEIVE_KEY);
}
@@ -97,6 +107,11 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(P12CreateController.class)
abstract FxController bindP12CreateController(P12CreateController controller);
+ @Binds
+ @IntoMap
+ @FxControllerKey(AuthFlowController.class)
+ abstract FxController bindAuthFlowController(AuthFlowController controller);
+
@Provides
@IntoMap
@FxControllerKey(NewPasswordController.class)
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 a7f8f41e0..1343f8cc9 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java
@@ -19,7 +19,6 @@ import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.net.URI;
-import java.net.URISyntaxException;
import java.security.KeyPair;
import java.util.concurrent.atomic.AtomicReference;
@@ -33,28 +32,26 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
private final Vault vault;
private final Stage window;
private final Lazy p12LoadingScene;
- private final UserInteractionLock userInteraction;
- private final AtomicReference hubUriRef;
+ private final UserInteractionLock userInteraction;
private final AtomicReference keyPairRef;
- private final AtomicReference authParamsRef;
+ private final AtomicReference eciesParams;
@Inject
- public HubKeyLoadingStrategy(@KeyLoading Vault vault, @KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_P12) Lazy p12LoadingScene, UserInteractionLock userInteraction, AtomicReference hubUriRef, AtomicReference keyPairRef, AtomicReference authParamsRef) {
+ public HubKeyLoadingStrategy(@KeyLoading Vault vault, @KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_P12) Lazy p12LoadingScene, UserInteractionLock userInteraction, AtomicReference keyPairRef, AtomicReference eciesParams) {
this.vault = vault;
this.window = window;
this.p12LoadingScene = p12LoadingScene;
this.userInteraction = userInteraction;
- this.hubUriRef = hubUriRef;
this.keyPairRef = keyPairRef;
- this.authParamsRef = authParamsRef;
+ this.eciesParams = eciesParams;
}
@Override
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
- hubUriRef.set(getHubUri(keyId));
+ Preconditions.checkArgument(keyId.getScheme().startsWith(SCHEME_PREFIX));
try {
return switch (auth()) {
- case SUCCESS -> EciesHelper.decryptMasterkey(keyPairRef.get(), authParamsRef.get());
+ case SUCCESS -> EciesHelper.decryptMasterkey(keyPairRef.get(), eciesParams.get());
case FAILED -> throw new MasterkeyLoadingFailedException("failed to load keypair");
case CANCELLED -> throw new UnlockCancelledException("User cancelled auth workflow");
};
@@ -72,17 +69,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
}
}
- private URI getHubUri(URI keyId) {
- Preconditions.checkArgument(keyId.getScheme().startsWith(SCHEME_PREFIX));
- var hubUriScheme = keyId.getScheme().substring(SCHEME_PREFIX.length());
- try {
- return new URI(hubUriScheme, keyId.getSchemeSpecificPart(), keyId.getFragment());
- } catch (URISyntaxException e) {
- throw new IllegalStateException("URI constructed from params known to be valid", e);
- }
- }
-
- private HubKeyLoadingModule.AuthFlow auth() throws InterruptedException {
+ private HubKeyLoadingModule.HubLoadingResult auth() throws InterruptedException {
Platform.runLater(() -> {
window.setScene(p12LoadingScene.get());
window.show();
diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/P12Controller.java b/src/main/java/org/cryptomator/ui/keyloading/hub/P12Controller.java
index 84df68749..e9a70dc8d 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/hub/P12Controller.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/P12Controller.java
@@ -20,10 +20,10 @@ public class P12Controller implements FxController {
private final Stage window;
private final Environment env;
- private final UserInteractionLock userInteraction;
+ private final UserInteractionLock userInteraction;
@Inject
- public P12Controller(@KeyLoading Stage window, Environment env, UserInteractionLock userInteraction) {
+ public P12Controller(@KeyLoading Stage window, Environment env, UserInteractionLock userInteraction) {
this.window = window;
this.env = env;
this.userInteraction = userInteraction;
@@ -34,7 +34,7 @@ public class P12Controller implements FxController {
// if not already interacted, mark this workflow as cancelled:
if (userInteraction.awaitingInteraction().get()) {
LOG.debug("P12 loading cancelled by user.");
- userInteraction.interacted(HubKeyLoadingModule.AuthFlow.CANCELLED);
+ userInteraction.interacted(HubKeyLoadingModule.HubLoadingResult.CANCELLED);
}
}
diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/P12CreateController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/P12CreateController.java
index 0700d496b..3f98e62ca 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/hub/P12CreateController.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/P12CreateController.java
@@ -39,7 +39,7 @@ public class P12CreateController implements FxController {
private final Stage window;
private final Environment env;
private final AtomicReference keyPairRef;
- private final Lazy receiveKeyScene;
+ private final Lazy authFlowScene;
private final BooleanProperty userInteractionDisabled = new SimpleBooleanProperty();
private final ObjectBinding unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, userInteractionDisabled);
@@ -48,11 +48,11 @@ public class P12CreateController implements FxController {
public NewPasswordController newPasswordController;
@Inject
- public P12CreateController(@KeyLoading Stage window, Environment env, AtomicReference keyPairRef, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy receiveKeyScene) {
+ public P12CreateController(@KeyLoading Stage window, Environment env, AtomicReference keyPairRef, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene) {
this.window = window;
this.env = env;
this.keyPairRef = keyPairRef;
- this.receiveKeyScene = receiveKeyScene;
+ this.authFlowScene = authFlowScene;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
}
@@ -81,7 +81,7 @@ public class P12CreateController implements FxController {
var keyPair = P12AccessHelper.createNew(p12File, pw);
setKeyPair(keyPair);
LOG.debug("Created .p12 file {}", p12File);
- window.setScene(receiveKeyScene.get());
+ window.setScene(authFlowScene.get());
} catch (IOException e) {
LOG.error("Failed to load .p12 file.", e);
// TODO
diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/P12LoadController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/P12LoadController.java
index 9759e92e9..24af08384 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/hub/P12LoadController.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/P12LoadController.java
@@ -41,18 +41,18 @@ public class P12LoadController implements FxController {
private final Stage window;
private final Environment env;
private final AtomicReference keyPairRef;
- private final Lazy receiveKeyScene;
+ private final Lazy authFlowScene;
private final BooleanProperty userInteractionDisabled = new SimpleBooleanProperty();
private final ObjectBinding unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, userInteractionDisabled);
public NiceSecurePasswordField passwordField;
@Inject
- public P12LoadController(@KeyLoading Stage window, Environment env, AtomicReference keyPairRef, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy receiveKeyScene) {
+ public P12LoadController(@KeyLoading Stage window, Environment env, AtomicReference keyPairRef, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene) {
this.window = window;
this.env = env;
this.keyPairRef = keyPairRef;
- this.receiveKeyScene = receiveKeyScene;
+ this.authFlowScene = authFlowScene;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
}
@@ -78,7 +78,7 @@ public class P12LoadController implements FxController {
var keyPair = P12AccessHelper.loadExisting(p12File, pw);
setKeyPair(keyPair);
LOG.debug("Loaded .p12 file {}", p12File);
- window.setScene(receiveKeyScene.get());
+ window.setScene(authFlowScene.get());
} catch (InvalidPassphraseException e) {
LOG.warn("Invalid passphrase entered for .p12 file");
Animations.createShakeWindowAnimation(window).playFromStart();
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 77f33ee5e..7597f2b6e 100644
--- a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java
@@ -2,6 +2,8 @@ package org.cryptomator.ui.keyloading.hub;
import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
+import com.google.gson.JsonElement;
+import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.UserInteractionLock;
@@ -11,17 +13,25 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
+import javax.inject.Named;
import javafx.application.Application;
-import javafx.beans.binding.BooleanBinding;
+import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
-import javafx.concurrent.WorkerStateEvent;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
+import javafx.scene.control.TextField;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
import java.net.URI;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
import java.security.KeyPair;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
@@ -31,53 +41,88 @@ import java.util.concurrent.atomic.AtomicReference;
public class ReceiveKeyController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class);
+ private static final String SCHEME_PREFIX = "hub+";
- private final Application application;
- private final ExecutorService executor;
private final Stage window;
- private final KeyPair keyPair;
- private final AtomicReference authParamsRef;
- private final UserInteractionLock authFlowLock;
- private final AtomicReference hubUriRef;
+ private final String bearerToken;
+ private final AtomicReference eciesParamsRef;
+ private final UserInteractionLock result;
private final ErrorComponent.Builder errorComponent;
- private final ObjectProperty redirectUriRef;
- private final BooleanBinding ready;
- private final AuthReceiveTask receiveTask;
+ private final URI vaultBaseUri;
+ private final ObjectProperty state = new SimpleObjectProperty<>(ReceiveKeyState.LOADING);
+ private final HttpClient httpClient;
+
+ public TextField deviceName;
@Inject
- public ReceiveKeyController(Application application, ExecutorService executor, @KeyLoading Stage window, AtomicReference keyPairRef, AtomicReference authParamsRef, UserInteractionLock authFlowLock, AtomicReference hubUriRef, ErrorComponent.Builder errorComponent) {
- this.application = application;
- this.executor = executor;
+ public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, AtomicReference keyPairRef, @Named("bearerToken") AtomicReference tokenRef, AtomicReference eciesParamsRef, UserInteractionLock result, ErrorComponent.Builder errorComponent) {
this.window = window;
- this.keyPair = Objects.requireNonNull(keyPairRef.get());
- this.authParamsRef = authParamsRef;
- this.authFlowLock = authFlowLock;
- this.hubUriRef = hubUriRef;
+ this.bearerToken = Objects.requireNonNull(tokenRef.get());
+ this.eciesParamsRef = eciesParamsRef;
+ this.result = result;
this.errorComponent = errorComponent;
- this.redirectUriRef = new SimpleObjectProperty<>();
- this.ready = redirectUriRef.isNotNull();
- this.receiveTask = new AuthReceiveTask(redirectUriRef::set);
+ this.vaultBaseUri = getVaultBaseUri(vault);
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
+ this.httpClient = HttpClient.newBuilder().executor(executor).build();
+// var deviceKey = BaseEncoding.base64Url().omitPadding().encode(keyPairRef.get().getPublic().getEncoded());
+// LOG.info("deviceKey {}", deviceKey);
}
@FXML
public void initialize() {
- Preconditions.checkState(hubUriRef.get() != null);
- receiveTask.setOnSucceeded(this::receivedKey);
- receiveTask.setOnFailed(this::keyRetrievalFailed);
- executor.submit(receiveTask);
+ var keyUri = appendPath(vaultBaseUri, "/keys/desktop-app-3000"); // TODO use actual device id
+ var request = HttpRequest.newBuilder(keyUri) //
+ .header("Authorization", "Bearer " + bearerToken) //
+ .GET() //
+ .build();
+ httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) //
+ .whenCompleteAsync(this::loadedExistingKey, Platform::runLater);
}
- private void keyRetrievalFailed(WorkerStateEvent workerStateEvent) {
- LOG.error("Cryptomator Hub login failed with error", receiveTask.getException());
- authFlowLock.interacted(HubKeyLoadingModule.AuthFlow.FAILED);
- errorComponent.cause(receiveTask.getException()).window(window).build().showErrorScene();
+ private void loadedExistingKey(HttpResponse response, Throwable error) {
+ if (error != null) {
+ retrievalFailed(error);
+ } else {
+ switch (response.statusCode()) {
+ case 200 -> retrievalSucceeded(response);
+ case 404 -> state.set(ReceiveKeyState.NEEDS_REGISTRATION);
+ default -> retrievalFailed(new IOException("Unexpected response " + response.statusCode()));
+ }
+ }
}
- private void receivedKey(WorkerStateEvent workerStateEvent) {
- authParamsRef.set(Objects.requireNonNull(receiveTask.getValue()));
- authFlowLock.interacted(HubKeyLoadingModule.AuthFlow.SUCCESS);
- window.close();
+ @FXML
+ public void register() {
+ Preconditions.checkArgument(deviceName.textProperty().isNotEmpty().get(), "device name must not be empty");
+// var keyUri = appendPath(vaultBaseUri, "../../devices/desktop-app-3000");
+// var request = HttpRequest.newBuilder(keyUri) //
+// .header("Authorization", "Bearer " + bearerToken) //
+// .GET() //
+// .build();
+// httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) //
+// .whenCompleteAsync(this::loadedExistingKey, Platform::runLater);
+ }
+
+ private void retrievalSucceeded(HttpResponse response) {
+ try {
+ var json = HttpHelper.parseBody(response);
+ Preconditions.checkArgument(json.isJsonObject());
+ Preconditions.checkArgument(json.getAsJsonObject().has("device_specific_masterkey"));
+ Preconditions.checkArgument(json.getAsJsonObject().has("ephemeral_public_key"));
+ var m = json.getAsJsonObject().get("device_specific_masterkey").getAsString();
+ var epk = json.getAsJsonObject().get("ephemeral_public_key").getAsString();
+ eciesParamsRef.set(new EciesParams(m, epk));
+ result.interacted(HubKeyLoadingModule.HubLoadingResult.SUCCESS);
+ window.close();
+ } catch (IOException | IllegalArgumentException e) {
+ retrievalFailed(e);
+ }
+ }
+
+ private void retrievalFailed(Throwable cause) {
+ result.interacted(HubKeyLoadingModule.HubLoadingResult.FAILED);
+ LOG.error("Key retrieval failed", cause);
+ errorComponent.cause(cause).window(window).build().showErrorScene();
}
@FXML
@@ -86,44 +131,41 @@ public class ReceiveKeyController implements FxController {
}
private void windowClosed(WindowEvent windowEvent) {
- // stop server, if it is still running
- receiveTask.cancel();
// if not already interacted, mark this workflow as cancelled:
- if (authFlowLock.awaitingInteraction().get()) {
+ if (result.awaitingInteraction().get()) {
LOG.debug("Authorization cancelled by user.");
- authFlowLock.interacted(HubKeyLoadingModule.AuthFlow.CANCELLED);
+ result.interacted(HubKeyLoadingModule.HubLoadingResult.CANCELLED);
}
}
- @FXML
- public void openBrowser() {
- assert ready.get();
- var hubUri = Objects.requireNonNull(hubUriRef.get());
- var redirectUri = Objects.requireNonNull(redirectUriRef.get());
- var sb = new StringBuilder(hubUri.toString());
- sb.append("?redirect_uri=").append(URLEncoder.encode(redirectUri.toString(), StandardCharsets.US_ASCII));
- sb.append("&device_id=").append("desktop-app-3000");
- sb.append("&device_key=").append(BaseEncoding.base64Url().omitPadding().encode(keyPair.getPublic().getEncoded()));
- var url = sb.toString();
- application.getHostServices().showDocument(url);
+ private static URI appendPath(URI base, String path) {
+ try {
+ var newPath = base.getPath() + path;
+ return new URI(base.getScheme(), base.getAuthority(), newPath, base.getQuery(), base.getFragment());
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Can't append '" + path + "' to URI: " + base, e);
+ }
}
+ private static URI getVaultBaseUri(Vault vault) {
+ try {
+ var kid = vault.getUnverifiedVaultConfig().getKeyId();
+ assert kid.getScheme().startsWith(SCHEME_PREFIX);
+ var hubUriScheme = kid.getScheme().substring(SCHEME_PREFIX.length());
+ return new URI(hubUriScheme, kid.getSchemeSpecificPart(), kid.getFragment());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("URI constructed from params known to be valid", e);
+ }
+ }
/* Getter/Setter */
- public String getHubUriHost() {
- var hubUri = hubUriRef.get();
- if (hubUri == null) {
- return null;
- } else {
- return hubUri.getHost();
- }
+ public ObjectProperty stateProperty() {
+ return state;
}
- public BooleanBinding readyProperty() {
- return ready;
- }
-
- public boolean isReady() {
- return ready.get();
+ public ReceiveKeyState getState() {
+ return state.get();
}
}
diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyState.java b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyState.java
new file mode 100644
index 000000000..ae1562ded
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyState.java
@@ -0,0 +1,6 @@
+package org.cryptomator.ui.keyloading.hub;
+
+public enum ReceiveKeyState {
+ LOADING,
+ NEEDS_REGISTRATION
+}
diff --git a/src/main/resources/fxml/hub_auth_flow.fxml b/src/main/resources/fxml/hub_auth_flow.fxml
new file mode 100644
index 000000000..a6d086ea3
--- /dev/null
+++ b/src/main/resources/fxml/hub_auth_flow.fxml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/fxml/hub_receive_key.fxml b/src/main/resources/fxml/hub_receive_key.fxml
index e36731dc5..ef10291e7 100644
--- a/src/main/resources/fxml/hub_receive_key.fxml
+++ b/src/main/resources/fxml/hub_receive_key.fxml
@@ -1,16 +1,16 @@
+
-
+
+
-
-
+
+
+
+
@@ -26,17 +30,20 @@
-
-
-
-
-
+
+
+
+
+
+
+
-
+
+
diff --git a/src/main/resources/license/THIRD-PARTY.txt b/src/main/resources/license/THIRD-PARTY.txt
index e109eb172..0ed1b3cff 100644
--- a/src/main/resources/license/THIRD-PARTY.txt
+++ b/src/main/resources/license/THIRD-PARTY.txt
@@ -11,7 +11,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
-Cryptomator uses 43 third-party dependencies under the following licenses:
+Cryptomator uses 46 third-party dependencies under the following licenses:
Apache License v2.0:
- jffi (com.github.jnr:jffi:1.2.23 - http://github.com/jnr/jffi)
- jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm)
@@ -33,7 +33,10 @@ Cryptomator uses 43 third-party dependencies under the following licenses:
- Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.6 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.6 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.6 - https://eclipse.org/jetty/jetty-servlet)
+ - Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:10.0.6 - https://eclipse.org/jetty/jetty-servlets)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.6 - https://eclipse.org/jetty/jetty-util)
+ - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:10.0.6 - https://eclipse.org/jetty/jetty-webapp)
+ - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:10.0.6 - https://eclipse.org/jetty/jetty-xml)
- Jetty :: Servlet API and Schemas for JPMS and OSGi (org.eclipse.jetty.toolchain:jetty-servlet-api:4.0.6 - https://eclipse.org/jetty/jetty-servlet-api)
BSD:
- asm (org.ow2.asm:asm:7.1 - http://asm.ow2.org/)
@@ -53,7 +56,10 @@ Cryptomator uses 43 third-party dependencies under the following licenses:
- Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.6 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.6 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.6 - https://eclipse.org/jetty/jetty-servlet)
+ - Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:10.0.6 - https://eclipse.org/jetty/jetty-servlets)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.6 - https://eclipse.org/jetty/jetty-util)
+ - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:10.0.6 - https://eclipse.org/jetty/jetty-webapp)
+ - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:10.0.6 - https://eclipse.org/jetty/jetty-xml)
Eclipse Public License - v 1.0:
- Logback Classic Module (ch.qos.logback:logback-classic:1.2.3 - http://logback.qos.ch/logback-classic)
- Logback Core Module (ch.qos.logback:logback-core:1.2.3 - http://logback.qos.ch/logback-core)
diff --git a/src/test/java/org/cryptomator/ui/keyloading/hub/AuthReceiverTest.java b/src/test/java/org/cryptomator/ui/keyloading/hub/AuthReceiverTest.java
deleted file mode 100644
index 531f63415..000000000
--- a/src/test/java/org/cryptomator/ui/keyloading/hub/AuthReceiverTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.cryptomator.ui.keyloading.hub;
-
-public class AuthReceiverTest {
-
- static {
- System.setProperty("LOGLEVEL", "INFO");
- }
-
- public static void main(String[] args) {
- try (var receiver = AuthReceiver.start()) {
- System.out.println("Waiting on " + receiver.getRedirectURL());
- var token = receiver.receive();
- System.out.println("SUCCESS: " + token);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- System.out.println("CANCELLED");
- } catch (Exception e) {
- System.out.println("ERROR");
- e.printStackTrace();
- }
- }
-
-}
\ No newline at end of file