Merge pull request #3210 from cryptomator/feature/3203-device-already-registered

Feature: Adjust Hub workflow, if device is already registerd
This commit is contained in:
Sebastian Stenzel
2024-01-11 12:05:25 +01:00
committed by GitHub
11 changed files with 98 additions and 45 deletions

View File

@@ -22,8 +22,9 @@ public enum FxmlFile {
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
HUB_LEGACY_REGISTER_DEVICE("/fxml/hub_legacy_register_device.fxml"), //
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
HUB_REGISTER_DEVICE_ALREADY_EXISTS("/fxml/hub_register_device_already_exists.fxml"), //
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
HUB_SETUP_DEVICE("/fxml/hub_setup_device.fxml"), //
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
LOCK_FORCED("/fxml/lock_forced.fxml"), //

View File

@@ -0,0 +1,12 @@
package org.cryptomator.ui.keyloading.hub;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
/**
* Thrown, when Hub registerDevice-Request returns with 409
*/
class DeviceAlreadyExistsException extends MasterkeyLoadingFailedException {
public DeviceAlreadyExistsException() {
super("Device already registered on this Hub instance");
}
}

View File

@@ -135,10 +135,17 @@ public abstract class HubKeyLoadingModule {
}
@Provides
@FxmlScene(FxmlFile.HUB_SETUP_DEVICE)
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE)
@KeyLoadingScoped
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_SETUP_DEVICE);
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE);
}
@Provides
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS)
@KeyLoadingScoped
static Scene provideHubRegisterDeviceAlreadyExistsScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS);
}
@Provides

View File

@@ -1,7 +1,6 @@
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;
@@ -44,6 +43,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
this.window = window;
this.keychainManager = keychainManager;
window.setTitle(windowTitle);
window.setOnCloseRequest(_ -> result.cancel(true));
this.authFlowScene = authFlowScene;
this.noKeychainScene = noKeychainScene;
this.result = result;

View File

@@ -11,7 +11,6 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +31,6 @@ import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -51,7 +49,7 @@ public class ReceiveKeyController implements FxController {
private final String deviceId;
private final String bearerToken;
private final CompletableFuture<ReceivedKey> result;
private final Lazy<Scene> setupDeviceScene;
private final Lazy<Scene> registerDeviceScene;
private final Lazy<Scene> legacyRegisterDeviceScene;
private final Lazy<Scene> unauthorizedScene;
private final Lazy<Scene> accountInitializationScene;
@@ -60,13 +58,13 @@ public class ReceiveKeyController implements FxController {
private final HttpClient httpClient;
@Inject
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_SETUP_DEVICE) Lazy<Scene> setupDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
this.window = window;
this.hubConfig = hubConfig;
this.deviceId = deviceId;
this.bearerToken = Objects.requireNonNull(tokenRef.get());
this.result = result;
this.setupDeviceScene = setupDeviceScene;
this.registerDeviceScene = registerDeviceScene;
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
this.unauthorizedScene = unauthorizedScene;
this.accountInitializationScene = accountInitializationScene;
@@ -141,7 +139,7 @@ public class ReceiveKeyController implements FxController {
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
receivedBothEncryptedKeys(encryptedVaultKey, device.userPrivateKey);
}
case 404 -> needsDeviceSetup(); // TODO: using the setup code, we can theoretically immediately unlock
case 404 -> needsDeviceRegistration(); // TODO: using the setup code, we can theoretically immediately unlock
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
}
} catch (IOException e) {
@@ -149,8 +147,8 @@ public class ReceiveKeyController implements FxController {
}
}
private void needsDeviceSetup() {
window.setScene(setupDeviceScene.get());
private void needsDeviceRegistration() {
window.setScene(registerDeviceScene.get());
}
private void receivedBothEncryptedKeys(String encryptedVaultKey, String encryptedUserKey) throws IOException {

View File

@@ -57,12 +57,12 @@ public class RegisterDeviceController implements FxController {
private final String bearerToken;
private final Lazy<Scene> registerSuccessScene;
private final Lazy<Scene> registerFailedScene;
private final Lazy<Scene> deviceAlreadyExistsScene;
private final String deviceId;
private final P384KeyPair deviceKeyPair;
private final CompletableFuture<ReceivedKey> result;
private final HttpClient httpClient;
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
private final BooleanProperty invalidSetupCode = new SimpleBooleanProperty(false);
private final BooleanProperty workInProgress = new SimpleBooleanProperty(false);
public TextField setupCodeField;
@@ -70,7 +70,7 @@ public class RegisterDeviceController implements FxController {
public Button registerBtn;
@Inject
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS) Lazy<Scene> deviceAlreadyExistsScene) {
this.window = window;
this.hubConfig = hubConfig;
this.deviceId = deviceId;
@@ -79,13 +79,13 @@ public class RegisterDeviceController implements FxController {
this.bearerToken = Objects.requireNonNull(bearerToken.get());
this.registerSuccessScene = registerSuccessScene;
this.registerFailedScene = registerFailedScene;
this.deviceAlreadyExistsScene = deviceAlreadyExistsScene;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
}
public void initialize() {
deviceNameField.setText(determineHostname());
deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
deviceNameField.disableProperty().bind(workInProgress);
setupCodeField.textProperty().addListener(observable -> invalidSetupCode.set(false));
setupCodeField.disableProperty().bind(workInProgress);
@@ -146,7 +146,7 @@ public class RegisterDeviceController implements FxController {
return httpClient.sendAsync(putDeviceReq, HttpResponse.BodyHandlers.discarding());
}).whenCompleteAsync((response, throwable) -> {
if (response != null) {
this.handleResponse(response);
this.handleRegisterDeviceResponse(response);
} else {
this.setupFailed(throwable);
}
@@ -170,12 +170,12 @@ public class RegisterDeviceController implements FxController {
}
}
private void handleResponse(HttpResponse<Void> response) {
private void handleRegisterDeviceResponse(HttpResponse<Void> response) {
if (response.statusCode() == 201) {
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
window.setScene(registerSuccessScene.get());
} else if (response.statusCode() == 409) {
deviceNameAlreadyExists.set(true);
setupFailed(new DeviceAlreadyExistsException());
} else {
setupFailed(new IllegalStateException("Unexpected http status code " + response.statusCode()));
}
@@ -184,10 +184,13 @@ public class RegisterDeviceController implements FxController {
private void setupFailed(Throwable cause) {
switch (cause) {
case CompletionException e when e.getCause() instanceof JWEHelper.InvalidJweKeyException -> invalidSetupCode.set(true);
case DeviceAlreadyExistsException e -> {
LOG.debug("Device already registered in hub instance {} for different user", hubConfig.authSuccessUrl);
window.setScene(deviceAlreadyExistsScene.get());
}
default -> {
LOG.warn("Device setup failed.", cause);
window.setScene(registerFailedScene.get());
result.completeExceptionally(cause);
}
}
}
@@ -202,15 +205,6 @@ public class RegisterDeviceController implements FxController {
}
//--- Getters & Setters
public BooleanProperty deviceNameAlreadyExistsProperty() {
return deviceNameAlreadyExists;
}
public boolean getDeviceNameAlreadyExists() {
return deviceNameAlreadyExists.get();
}
public BooleanProperty invalidSetupCodeProperty() {
return invalidSetupCode;
}

View File

@@ -1,6 +1,5 @@
package org.cryptomator.ui.keyloading.hub;
import com.nimbusds.jose.JWEObject;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading;
@@ -22,8 +21,8 @@ public class RegisterFailedController implements FxController {
@FXML
public void close() {
result.cancel(true);
window.close();
}
}

View File

@@ -57,15 +57,6 @@
<TextField fx:id="deviceNameField" HBox.hgrow="ALWAYS"/>
</HBox>
<HBox alignment="TOP_RIGHT">
<Label text="%hub.register.occupiedMsg" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.deviceNameAlreadyExists}" managed="${controller.deviceNameAlreadyExists}" graphicTextGap="6">
<padding>
<Insets top="6"/>
</padding>
<graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic>
</Label>
<Label text="%hub.register.invalidAccountKeyLabel" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.invalidSetupCode}" managed="${controller.invalidSetupCode}" graphicTextGap="6">
<padding>
<Insets top="6"/>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?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?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.keyloading.hub.RegisterFailedController"
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.registerFailed.message" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%hub.registerFailed.description.deviceAlreadyExists" wrapText="true"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
<buttons>
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#close"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</HBox>

View File

@@ -38,7 +38,7 @@
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%hub.registerFailed.description" wrapText="true"/>
<Label text="%hub.registerFailed.description.generic" wrapText="true"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+C">

View File

@@ -161,14 +161,14 @@ hub.register.message=New Device
hub.register.description=This is the first Hub access from this device. Please authorize it using your Account Key.
hub.register.nameLabel=Device Name
hub.register.invalidAccountKeyLabel=Invalid Account Key
hub.register.occupiedMsg=Name already in use
hub.register.registerBtn=Confirm
hub.register.registerBtn=Authorize
### Registration Success
hub.registerSuccess.message=Device named
hub.registerSuccess.message=Device registered
hub.registerSuccess.description=To access the vault, your device needs to be authorized by the vault owner.
### Registration Failed
hub.registerFailed.message=Device naming failed
hub.registerFailed.description=An error was thrown in the naming process. For more details, look into the application log.
hub.registerFailed.message=Device registration failed
hub.registerFailed.description.generic=An error was thrown in the registration process. For more details, look into the application log.
hub.registerFailed.description.deviceAlreadyExists=This device is already registered for a different user. Try to change the user account or use a different device.
### Unauthorized
hub.unauthorized.message=Access denied
hub.unauthorized.description=Your device has not yet been authorized to access this vault. Ask the vault owner to authorize it.