Explorar o código

Merge pull request #2328 from cryptomator/feature/hub-device-name-taken

Display message when device name is already in use
Armin Schrenk %!s(int64=2) %!d(string=hai) anos
pai
achega
86cf0d4d4e

+ 43 - 5
src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java

@@ -20,8 +20,12 @@ import org.slf4j.LoggerFactory;
 import javax.inject.Inject;
 import javax.inject.Named;
 import javafx.application.Platform;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
 import javafx.fxml.FXML;
 import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.ContentDisplay;
 import javafx.scene.control.TextField;
 import javafx.stage.Stage;
 import javafx.stage.WindowEvent;
@@ -32,6 +36,7 @@ import java.net.http.HttpClient;
 import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
 import java.nio.charset.StandardCharsets;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
@@ -42,6 +47,7 @@ public class RegisterDeviceController implements FxController {
 
 	private static final Logger LOG = LoggerFactory.getLogger(RegisterDeviceController.class);
 	private static final Gson GSON = new GsonBuilder().setLenient().create();
+	private static final List<Integer> EXPECTED_RESPONSE_CODES = List.of(201, 409);
 
 	private final Stage window;
 	private final HubConfig hubConfig;
@@ -53,8 +59,10 @@ public class RegisterDeviceController implements FxController {
 	private final CompletableFuture<JWEObject> result;
 	private final DecodedJWT jwt;
 	private final HttpClient httpClient;
+	private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
 
 	public TextField deviceNameField;
+	public Button registerBtn;
 
 	@Inject
 	public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<JWEObject> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
@@ -73,6 +81,7 @@ public class RegisterDeviceController implements FxController {
 
 	public void initialize() {
 		deviceNameField.setText(determineHostname());
+		deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
 	}
 
 	private String determineHostname() {
@@ -86,6 +95,10 @@ public class RegisterDeviceController implements FxController {
 
 	@FXML
 	public void register() {
+		deviceNameAlreadyExists.set(false);
+		registerBtn.setContentDisplay(ContentDisplay.LEFT);
+		registerBtn.setDisable(true);
+
 		var keyUri = URI.create(hubConfig.devicesResourceUrl + deviceId);
 		var deviceKey = keyPair.getPublic().getEncoded();
 		var dto = new CreateDeviceDto();
@@ -98,9 +111,15 @@ public class RegisterDeviceController implements FxController {
 				.header("Content-Type", "application/json").PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
 				.build();
 		httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()) //
-				.handleAsync((response, throwable) -> {
+				.thenApply(response -> {
+					if (EXPECTED_RESPONSE_CODES.contains(response.statusCode())) {
+						return response;
+					} else {
+						throw new RuntimeException("Server answered with unexpected status code " + response.statusCode());
+					}
+				}).handleAsync((response, throwable) -> {
 					if (response != null) {
-						this.registrationSucceeded(response);
+						this.handleResponse(response);
 					} else {
 						this.registrationFailed(throwable);
 					}
@@ -108,9 +127,17 @@ public class RegisterDeviceController implements FxController {
 				}, Platform::runLater);
 	}
 
-	private void registrationSucceeded(HttpResponse<Void> voidHttpResponse) {
-		LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
-		window.setScene(registerSuccessScene.get());
+	private void handleResponse(HttpResponse<Void> voidHttpResponse) {
+		assert EXPECTED_RESPONSE_CODES.contains(voidHttpResponse.statusCode());
+
+		if (voidHttpResponse.statusCode() == 409) {
+			deviceNameAlreadyExists.set(true);
+			registerBtn.setContentDisplay(ContentDisplay.TEXT_ONLY);
+			registerBtn.setDisable(false);
+		} else {
+			LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
+			window.setScene(registerSuccessScene.get());
+		}
 	}
 
 	private void registrationFailed(Throwable cause) {
@@ -135,4 +162,15 @@ public class RegisterDeviceController implements FxController {
 	}
 
 
+	//--- Getters & Setters
+
+	public BooleanProperty deviceNameAlreadyExistsProperty() {
+		return deviceNameAlreadyExists;
+	}
+
+	public boolean getDeviceNameAlreadyExists() {
+		return deviceNameAlreadyExists.get();
+	}
+
+
 }

+ 17 - 2
src/main/resources/fxml/hub_register_device.fxml

@@ -12,6 +12,7 @@
 <?import javafx.scene.layout.StackPane?>
 <?import javafx.scene.layout.VBox?>
 <?import javafx.scene.shape.Circle?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
 <HBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.keyloading.hub.RegisterDeviceController"
@@ -48,12 +49,26 @@
 				<Label text="%hub.register.nameLabel" labelFor="$deviceNameField"/>
 				<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}" graphicTextGap="6">
+					<padding>
+						<Insets top="6"/>
+					</padding>
+					<graphic>
+						<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
+					</graphic>
+				</Label>
+			</HBox>
 
 			<Region VBox.vgrow="ALWAYS" minHeight="18"/>
 			<ButtonBar buttonMinWidth="120" buttonOrder="+CU">
 				<buttons>
-					<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
-					<Button text="%hub.register.registerBtn" ButtonBar.buttonData="OTHER" defaultButton="true" onAction="#register"/>
+					<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
+					<Button fx:id="registerBtn" text="%hub.register.registerBtn" ButtonBar.buttonData="OTHER" defaultButton="true" onAction="#register" contentDisplay="TEXT_ONLY" >
+						<graphic>
+							<FontAwesome5Spinner glyphSize="12" />
+						</graphic>
+					</Button>
 				</buttons>
 			</ButtonBar>
 		</VBox>

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

@@ -136,6 +136,7 @@ hub.receive.description=Cryptomator is receiving and processing the response fro
 hub.register.message=Device name required
 hub.register.description=This seems to be the first Hub access from this device. In order to identify it for access authorization, you need to name this device.
 hub.register.nameLabel=Device Name
+hub.register.occupiedMsg=Name already in use
 hub.register.registerBtn=Confirm
 ### Registration Success
 hub.registerSuccess.message=Device named