adjust to new migration API

This commit is contained in:
Sebastian Stenzel
2024-01-26 16:48:08 +01:00
parent 693299a5d7
commit d66cfe0e7c

View File

@@ -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, //