Browse Source

adjust to new migration API

Sebastian Stenzel 1 year ago
parent
commit
d66cfe0e7c

+ 29 - 28
src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java

@@ -43,12 +43,13 @@ import java.text.ParseException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Base64;
-import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 @KeyLoadingScoped
 public class RegisterDeviceController implements FxController {
@@ -162,23 +163,36 @@ public class RegisterDeviceController implements FxController {
 
 	private void migrateLegacyDevices(ECPublicKey userPublicKey) {
 		try {
-			var accessibleVaultsUri = hubConfig.URIs.API."vaults/accessible";
-			var getAccessibleDevicesReq = HttpRequest.newBuilder(accessibleVaultsUri).GET().timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
-			var getAccessibleDevicesRes = httpClient.send(getAccessibleDevicesReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
-			if (getAccessibleDevicesRes.statusCode() != 200) {
-				throw new IOException(STR."Unexpected response from GET \{getAccessibleDevicesReq.uri()}: \{getAccessibleDevicesRes.statusCode()}");
+			// GET legacy access tokens
+			var getUri = hubConfig.URIs.API."devices/\{deviceId}/legacy-access-tokens";
+			var getReq = HttpRequest.newBuilder(getUri).GET().timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
+			var getRes = httpClient.send(getReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
+			if (getRes.statusCode() != 200) {
+				LOG.debug("GET {} resulted in status code {}. Skipping migration.", getUri, getRes.statusCode());
+				return;
 			}
-			List<VaultDto> vaults = JSON.readerForListOf(VaultDto.class).readValue(getAccessibleDevicesRes.body());
-			for (var vault : vaults) {
-				LOG.debug("Attempt to migrate legacy access token for vault: {}...", vault.name);
-				var legacyAccessTokenUri = hubConfig.URIs.API."vaults/\{vault.id}/keys/\{deviceId}";
-				var getLegacyAccessTokenReq = HttpRequest.newBuilder(legacyAccessTokenUri).GET().timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
-				var getLegacyAccessTokenRes = httpClient.send(getLegacyAccessTokenReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
-				if (getLegacyAccessTokenRes.statusCode() == 200) {
-					migrateLegacyDevice(userPublicKey, vault, getLegacyAccessTokenRes.body());
+			Map<String, String> legacyAccessTokens = JSON.readerForMapOf(String.class).readValue(getRes.body());
+			if (legacyAccessTokens.isEmpty()) {
+				return; // no migration required
+			}
+
+			// POST new access tokens
+			Map<String, String> newAccessTokens = legacyAccessTokens.entrySet().stream().<Map.Entry<String, String>>mapMulti((entry, consumer) -> {
+				try (var vaultKey = JWEHelper.decryptVaultKey(JWEObject.parse(entry.getValue()), deviceKeyPair.getPrivate())) {
+					var newAccessToken = JWEHelper.encryptVaultKey(vaultKey, userPublicKey).serialize();
+					consumer.accept(Map.entry(entry.getKey(), newAccessToken));
+				} catch (ParseException | JWEHelper.InvalidJweKeyException e) {
+					LOG.warn("Failed to decrypt legacy access token for vault {}. Skipping migration.", entry.getKey());
 				}
+			}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+			var postUri = hubConfig.URIs.API."users/me/access-tokens";
+			var postBody = JSON.writer().writeValueAsString(newAccessTokens);
+			var postReq = HttpRequest.newBuilder(postUri).POST(HttpRequest.BodyPublishers.ofString(postBody)).timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
+			var postRes = httpClient.send(postReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
+			if (postRes.statusCode() != 200) {
+				throw new IOException(STR."Unexpected response from POST \{postUri}: \{postRes.statusCode()}");
 			}
-		} catch (IOException | JWEHelper.KeyDecodeFailedException e) {
+		} catch (IOException e) {
 			// log and ignore: this is merely a best-effort attempt of migrating legacy devices. Failure is uncritical as this is merely a convenience feature.
 			LOG.error("Legacy Device Migration failed.", e);
 		} catch (InterruptedException e) {
@@ -187,16 +201,6 @@ public class RegisterDeviceController implements FxController {
 		}
 	}
 
-	private void migrateLegacyDevice(ECPublicKey userPublicKey, VaultDto vault, String legacyAccessToken) {
-		try (var vaultKey = JWEHelper.decryptVaultKey(JWEObject.parse(legacyAccessToken), deviceKeyPair.getPrivate())) {
-			var newToken = JWEHelper.encryptVaultKey(vaultKey, userPublicKey).serialize();
-			// TODO: send new access token to backend
-			LOG.info("POST /api/vaults/{}/access-token {}", vault.id, newToken);
-		} catch (ParseException e) {
-			LOG.warn("Failed to parse legacy access token for vault {}. Skipping migration.", vault.name);
-		}
-	}
-
 	private UserDto fromJson(String json) {
 		try {
 			return JSON.reader().readValue(json, UserDto.class);
@@ -259,9 +263,6 @@ public class RegisterDeviceController implements FxController {
 	@JsonIgnoreProperties(ignoreUnknown = true)
 	private record UserDto(String id, String name, String publicKey, String privateKey, String setupCode) {}
 
-	@JsonIgnoreProperties(ignoreUnknown = true)
-	private record VaultDto(String id, String name) {}
-
 	private record CreateDeviceDto(@JsonProperty(required = true) String id, //
 								   @JsonProperty(required = true) String name, //
 								   @JsonProperty(required = true) String publicKey, //