From b1cb347ee3f22731572945841c45de538d59ff00 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Mon, 10 Feb 2025 14:27:48 +0100 Subject: [PATCH 1/9] Devices are optional in the UserDto while updating the user For legacy reasons --- .../cryptomator/hub/api/UsersResource.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java index 4fe07cb48..f6bf85149 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java @@ -104,18 +104,20 @@ public Response putMe(@Nullable @Valid UserDto dto) { * @param userDto The DTO */ private void updateDevices(User userEntity, UserDto userDto) { - var devices = userEntity.devices.stream().collect(Collectors.toUnmodifiableMap(Device::getId, Function.identity())); - var updatedDevices = userDto.devices.stream() - .filter(d -> devices.containsKey(d.id())) // only look at DTOs for which we find a matching existing entity - .map(dto -> { - var device = devices.get(dto.id()); - device.setType(dto.type()); - device.setName(dto.name()); - device.setPublickey(dto.publicKey()); - device.setUserPrivateKeys(dto.userPrivateKeys()); - return device; - }); - deviceRepo.persist(updatedDevices); + if (userDto.devices != null) { + var devices = userEntity.devices.stream().collect(Collectors.toUnmodifiableMap(Device::getId, Function.identity())); + var updatedDevices = userDto.devices.stream() + .filter(d -> devices.containsKey(d.id())) // only look at DTOs for which we find a matching existing entity + .map(dto -> { + var device = devices.get(dto.id()); + device.setType(dto.type()); + device.setName(dto.name()); + device.setPublickey(dto.publicKey()); + device.setUserPrivateKeys(dto.userPrivateKeys()); + return device; + }); + deviceRepo.persist(updatedDevices); + } } @POST From 04eb3f5dde35c87362b908c66ec968e719b911e8 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Mon, 10 Feb 2025 14:29:06 +0100 Subject: [PATCH 2/9] Accept and export also publicKey/privateKey in UserDto For legacy reasons --- .../java/org/cryptomator/hub/api/UserDto.java | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java index 38ef91600..898a18d84 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java @@ -1,5 +1,6 @@ package org.cryptomator.hub.api; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.annotation.Nullable; import org.cryptomator.hub.entities.User; @@ -17,24 +18,18 @@ public final class UserDto extends AuthorityDto { @JsonProperty("devices") public final Set devices; @JsonProperty("ecdhPublicKey") + @JsonAlias("publicKey") // name for history reasons (don't break client compatibility) public final String ecdhPublicKey; @JsonProperty("ecdsaPublicKey") public final String ecdsaPublicKey; - @JsonProperty("privateKey") // singular name for history reasons (don't break client compatibility) + @JsonProperty("privateKeys") + @JsonAlias("privateKey") // name for history reasons (don't break client compatibility) public final String privateKeys; @JsonProperty("setupCode") public final String setupCode; - /** - * Same as {@link #ecdhPublicKey}, kept for compatibility purposes - * @deprecated to be removed when all clients moved to the new DTO field names - */ - @Deprecated(forRemoval = true) - @JsonProperty("publicKey") - public final String legacyEcdhPublicKey; - - UserDto(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("pictureUrl") String pictureUrl, @JsonProperty("email") String email, @JsonProperty("language") String language, @JsonProperty("devices") Set devices, - @Nullable @JsonProperty("ecdhPublicKey") @OnlyBase64Chars String ecdhPublicKey, @Nullable @JsonProperty("ecdsaPublicKey") @OnlyBase64Chars String ecdsaPublicKey, @Nullable @JsonProperty("privateKeys") @ValidJWE String privateKeys, @Nullable @JsonProperty("setupCode") @ValidJWE String setupCode) { + public UserDto(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("pictureUrl") String pictureUrl, @JsonProperty("email") String email, @JsonProperty("language") String language, @JsonProperty("devices") Set devices, + @Nullable @JsonProperty("ecdhPublicKey") @JsonAlias("publicKey") @OnlyBase64Chars String ecdhPublicKey, @Nullable @JsonProperty("ecdsaPublicKey") @OnlyBase64Chars String ecdsaPublicKey, @Nullable @JsonProperty("privateKeys") @JsonAlias("privateKey") @ValidJWE String privateKeys, @Nullable @JsonProperty("setupCode") @ValidJWE String setupCode) { super(id, Type.USER, name, pictureUrl); this.email = email; this.language = language; @@ -43,12 +38,29 @@ public final class UserDto extends AuthorityDto { this.ecdsaPublicKey = ecdsaPublicKey; this.privateKeys = privateKeys; this.setupCode = setupCode; - - // duplicate fields to maintain backwards compatibility: - this.legacyEcdhPublicKey = ecdhPublicKey; } public static UserDto justPublicInfo(User user) { - return new UserDto(user.getId(), user.getName(), user.getPictureUrl(), user.getEmail(), user.getLanguage(), Set.of(), user.getEcdhPublicKey(), user.getEcdsaPublicKey(),null, null); + return new UserDto(user.getId(), user.getName(), user.getPictureUrl(), user.getEmail(), user.getLanguage(), Set.of(), user.getEcdhPublicKey(), user.getEcdsaPublicKey(), null, null); + } + + /** + * Same as {@link #ecdhPublicKey}, kept for compatibility purposes + * @deprecated to be removed when all clients moved to the new DTO field names + */ + @Deprecated(forRemoval = true) + @JsonProperty("publicKey") + public String getPublicKey() { + return ecdhPublicKey; + } + + /** + * Same as {@link #privateKeys}, kept for compatibility purposes + * @deprecated to be removed when all clients moved to the new DTO field names + */ + @Deprecated(forRemoval = true) + @JsonProperty("privateKey") + public String getPrivateKey() { + return privateKeys; } } From 34059dcdf7e5ed4cfd6d2a477e2a93812f7ef6d9 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Mon, 10 Feb 2025 16:06:38 +0100 Subject: [PATCH 3/9] Add create-vaults role to the hub cli client --- backend/src/main/resources/dev-realm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/resources/dev-realm.json b/backend/src/main/resources/dev-realm.json index 8f76ff838..6dbba00fb 100644 --- a/backend/src/main/resources/dev-realm.json +++ b/backend/src/main/resources/dev-realm.json @@ -145,7 +145,8 @@ "enabled": true, "serviceAccountClientId": "cryptomatorhub-cli", "realmRoles": [ - "user" + "user", + "create-vaults" ], "clientRoles" : { "realm-management" : [ "manage-users", "view-users" ] From 4f1fe6fb00d2019b9ffdfac695c84ef9d111f642 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Tue, 11 Feb 2025 09:20:16 +0100 Subject: [PATCH 4/9] Accept and export also publicKey/privateKey in justPublicInfo of UserDto For legacy reasons --- .../java/org/cryptomator/hub/api/UserDto.java | 124 +++++++++++++----- .../cryptomator/hub/api/UsersResource.java | 20 +-- 2 files changed, 103 insertions(+), 41 deletions(-) diff --git a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java index 898a18d84..298951599 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java @@ -1,6 +1,6 @@ package org.cryptomator.hub.api; -import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.annotation.Nullable; import org.cryptomator.hub.entities.User; @@ -11,56 +11,118 @@ public final class UserDto extends AuthorityDto { - @JsonProperty("email") - public final String email; - @JsonProperty("language") - public final String language; - @JsonProperty("devices") - public final Set devices; - @JsonProperty("ecdhPublicKey") - @JsonAlias("publicKey") // name for history reasons (don't break client compatibility) - public final String ecdhPublicKey; - @JsonProperty("ecdsaPublicKey") - public final String ecdsaPublicKey; - @JsonProperty("privateKeys") - @JsonAlias("privateKey") // name for history reasons (don't break client compatibility) - public final String privateKeys; - @JsonProperty("setupCode") - public final String setupCode; + private final String email; + private final String language; + private final Set devices; + private final String ecdhPublicKey; + private final String ecdsaPublicKey; + private final String privateKeys; + private final String setupCode; - public UserDto(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("pictureUrl") String pictureUrl, @JsonProperty("email") String email, @JsonProperty("language") String language, @JsonProperty("devices") Set devices, - @Nullable @JsonProperty("ecdhPublicKey") @JsonAlias("publicKey") @OnlyBase64Chars String ecdhPublicKey, @Nullable @JsonProperty("ecdsaPublicKey") @OnlyBase64Chars String ecdsaPublicKey, @Nullable @JsonProperty("privateKeys") @JsonAlias("privateKey") @ValidJWE String privateKeys, @Nullable @JsonProperty("setupCode") @ValidJWE String setupCode) { + @JsonCreator + public UserDto( + @JsonProperty("id") String id, + @JsonProperty("name") String name, + @JsonProperty("pictureUrl") String pictureUrl, + @JsonProperty("email") String email, + @JsonProperty("language") String language, + @JsonProperty("devices") Set devices, + // Accept either "ecdhPublicKey" or the legacy "publicKey" on input + @Nullable + @JsonProperty("ecdhPublicKey") @OnlyBase64Chars String ecdhPublicKey, + @Nullable + @JsonProperty("publicKey") @OnlyBase64Chars String publicKey, + @Nullable + @JsonProperty("ecdsaPublicKey") @OnlyBase64Chars String ecdsaPublicKey, + // Accept either "privateKeys" or the legacy "privateKey" on input + @Nullable + @JsonProperty("privateKeys") @ValidJWE String privateKeys, + @Nullable + @JsonProperty("privateKey") @ValidJWE String privateKey, + @Nullable + @JsonProperty("setupCode") @ValidJWE String setupCode) { super(id, Type.USER, name, pictureUrl); this.email = email; this.language = language; this.devices = devices; - this.ecdhPublicKey = ecdhPublicKey; + if (ecdhPublicKey != null) { + this.ecdhPublicKey = ecdhPublicKey; + } else { + this.ecdhPublicKey = publicKey; + } this.ecdsaPublicKey = ecdsaPublicKey; - this.privateKeys = privateKeys; + if (privateKeys != null) { + this.privateKeys = privateKeys; + } else { + this.privateKeys = privateKey; + } this.setupCode = setupCode; } - public static UserDto justPublicInfo(User user) { - return new UserDto(user.getId(), user.getName(), user.getPictureUrl(), user.getEmail(), user.getLanguage(), Set.of(), user.getEcdhPublicKey(), user.getEcdsaPublicKey(), null, null); + @JsonProperty("email") + public String getEmail() { + return email; + } + + @JsonProperty("language") + public String getLanguage() { + return language; + } + + @JsonProperty("devices") + public Set getDevices() { + return devices; + } + + // Primary getter for the new key name. + @JsonProperty("ecdhPublicKey") + public String getEcdhPublicKey() { + return ecdhPublicKey; } - /** - * Same as {@link #ecdhPublicKey}, kept for compatibility purposes - * @deprecated to be removed when all clients moved to the new DTO field names - */ + // Legacy getter for compatibility. Both getters return the same value. @Deprecated(forRemoval = true) @JsonProperty("publicKey") public String getPublicKey() { return ecdhPublicKey; } - /** - * Same as {@link #privateKeys}, kept for compatibility purposes - * @deprecated to be removed when all clients moved to the new DTO field names - */ + @JsonProperty("ecdsaPublicKey") + public String getEcdsaPublicKey() { + return ecdsaPublicKey; + } + + // Primary getter for private keys. + @JsonProperty("privateKeys") + public String getPrivateKeys() { + return privateKeys; + } + + // Legacy getter for private keys. @Deprecated(forRemoval = true) @JsonProperty("privateKey") public String getPrivateKey() { return privateKeys; } + + @JsonProperty("setupCode") + public String getSetupCode() { + return setupCode; + } + + public static UserDto justPublicInfo(User user) { + return new UserDto( + user.getId(), + user.getName(), + user.getPictureUrl(), + user.getEmail(), + user.getLanguage(), + Set.of(), + user.getEcdhPublicKey(), + user.getEcdhPublicKey(), + user.getEcdsaPublicKey(), + null, + null, + null); + } } diff --git a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java index f6bf85149..e178eff34 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java @@ -80,18 +80,18 @@ public Response putMe(@Nullable @Valid UserDto dto) { user.setPictureUrl(jwt.getClaim("picture")); user.setEmail(jwt.getClaim("email")); if (dto != null) { - if (!Objects.equals(user.getSetupCode(), dto.setupCode)) { - user.setSetupCode(dto.setupCode); + if (!Objects.equals(user.getSetupCode(), dto.getSetupCode())) { + user.setSetupCode(dto.getSetupCode()); eventLogger.logUserSetupCodeChanged(jwt.getSubject()); } - if (!Objects.equals(user.getEcdhPublicKey(), dto.ecdhPublicKey) || !Objects.equals(user.getEcdsaPublicKey(), dto.ecdsaPublicKey) || !Objects.equals(user.getPrivateKeys(), dto.privateKeys)) { - user.setEcdhPublicKey(dto.ecdhPublicKey); - user.setEcdsaPublicKey(dto.ecdsaPublicKey); - user.setPrivateKeys(dto.privateKeys); + if (!Objects.equals(user.getEcdhPublicKey(), dto.getEcdhPublicKey()) || !Objects.equals(user.getEcdsaPublicKey(), dto.getEcdsaPublicKey()) || !Objects.equals(user.getPrivateKeys(), dto.getPrivateKeys())) { + user.setEcdhPublicKey(dto.getEcdhPublicKey()); + user.setEcdsaPublicKey(dto.getEcdsaPublicKey()); + user.setPrivateKeys(dto.getPrivateKeys()); eventLogger.logUserKeysChanged(jwt.getSubject(), jwt.getName()); } updateDevices(user, dto); - user.setLanguage(dto.language); + user.setLanguage(dto.getLanguage()); } userRepo.persist(user); return Response.created(URI.create(".")).build(); @@ -104,9 +104,9 @@ public Response putMe(@Nullable @Valid UserDto dto) { * @param userDto The DTO */ private void updateDevices(User userEntity, UserDto userDto) { - if (userDto.devices != null) { + if (userDto.getDevices() != null) { var devices = userEntity.devices.stream().collect(Collectors.toUnmodifiableMap(Device::getId, Function.identity())); - var updatedDevices = userDto.devices.stream() + var updatedDevices = userDto.getDevices().stream() .filter(d -> devices.containsKey(d.id())) // only look at DTOs for which we find a matching existing entity .map(dto -> { var device = devices.get(dto.id()); @@ -160,7 +160,7 @@ public UserDto getMe(@QueryParam("withDevices") boolean withDevices) { User user = userRepo.findById(jwt.getSubject()); Function mapDevices = d -> new DeviceResource.DeviceDto(d.getId(), d.getName(), d.getType(), d.getPublickey(), d.getUserPrivateKeys(), d.getOwner().getId(), d.getCreationTime().truncatedTo(ChronoUnit.MILLIS)); var devices = withDevices ? user.devices.stream().map(mapDevices).collect(Collectors.toSet()) : Set.of(); - return new UserDto(user.getId(), user.getName(), user.getPictureUrl(), user.getEmail(), user.getLanguage(), devices, user.getEcdhPublicKey(), user.getEcdsaPublicKey(), user.getPrivateKeys(), user.getSetupCode()); + return new UserDto(user.getId(), user.getName(), user.getPictureUrl(), user.getEmail(), user.getLanguage(), devices, user.getEcdhPublicKey(), user.getEcdhPublicKey(), user.getEcdsaPublicKey(), user.getPrivateKeys(), user.getPrivateKeys(), user.getSetupCode()); } @POST From 6a227df352f488a33382206f00e68f5ad4529f76 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Tue, 11 Feb 2025 09:21:24 +0100 Subject: [PATCH 5/9] Use the new privateKeys property in the Frontend --- frontend/src/common/backend.ts | 2 +- frontend/src/common/userdata.ts | 6 +++--- frontend/src/components/InitialSetup.vue | 2 +- frontend/src/components/RegenerateSetupCodeDialog.vue | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/common/backend.ts b/frontend/src/common/backend.ts index 9ea1d43bf..a1be6a8f3 100644 --- a/frontend/src/common/backend.ts +++ b/frontend/src/common/backend.ts @@ -75,7 +75,7 @@ export type UserDto = { accessibleVaults: VaultDto[]; ecdhPublicKey?: string; ecdsaPublicKey?: string; - privateKey?: string; + privateKeys?: string; setupCode?: string; } diff --git a/frontend/src/common/userdata.ts b/frontend/src/common/userdata.ts index 2cb8cb694..df16be9a2 100644 --- a/frontend/src/common/userdata.ts +++ b/frontend/src/common/userdata.ts @@ -93,10 +93,10 @@ class UserData { */ public async decryptUserKeysWithSetupCode(setupCode: string): Promise { const me = await this.me; - if (!me.privateKey) { + if (!me.privateKeys) { throw new Error('User not initialized.'); } - const userKeys = await UserKeys.recover(me.privateKey, setupCode, await this.ecdhPublicKey, await this.ecdsaPublicKey); + const userKeys = await UserKeys.recover(me.privateKeys, setupCode, await this.ecdhPublicKey, await this.ecdsaPublicKey); await this.addEcdsaKeyIfMissing(userKeys); return userKeys; } @@ -128,7 +128,7 @@ class UserData { if (me.setupCode && !me.ecdsaPublicKey) { const payload: { setupCode: string } = await JWEParser.parse(me.setupCode).decryptEcdhEs(userKeys.ecdhKeyPair.privateKey); me.ecdsaPublicKey = await userKeys.encodedEcdsaPublicKey(); - me.privateKey = await userKeys.encryptWithSetupCode(payload.setupCode); + me.privateKeys = await userKeys.encryptWithSetupCode(payload.setupCode); for (const device of me.devices) { device.userPrivateKey = await userKeys.encryptForDevice(base64.parse(device.publicKey)); } diff --git a/frontend/src/components/InitialSetup.vue b/frontend/src/components/InitialSetup.vue index dd1966366..98323f027 100644 --- a/frontend/src/components/InitialSetup.vue +++ b/frontend/src/components/InitialSetup.vue @@ -263,7 +263,7 @@ async function createUserKey() { const userKeys = await UserKeys.create(); me.ecdhPublicKey = await userKeys.encodedEcdhPublicKey(); me.ecdsaPublicKey = await userKeys.encodedEcdsaPublicKey(); - me.privateKey = await userKeys.encryptWithSetupCode(setupCode.value); + me.privateKeys = await userKeys.encryptWithSetupCode(setupCode.value); me.setupCode = await JWEBuilder.ecdhEs(userKeys.ecdhKeyPair.publicKey).encrypt({ setupCode: setupCode.value }); const browserKeys = await userdata.createBrowserKeys(); await submitBrowserKeys(browserKeys, me, userKeys); diff --git a/frontend/src/components/RegenerateSetupCodeDialog.vue b/frontend/src/components/RegenerateSetupCodeDialog.vue index 84f278fd8..7cd05ca1b 100644 --- a/frontend/src/components/RegenerateSetupCodeDialog.vue +++ b/frontend/src/components/RegenerateSetupCodeDialog.vue @@ -153,7 +153,7 @@ async function regenerateSetupCode() { const me = await userdata.me; const newCode = crypto.randomUUID(); const userKeys = await userdata.decryptUserKeysWithBrowser(); - me.privateKey = await userKeys.encryptWithSetupCode(newCode); + me.privateKeys = await userKeys.encryptWithSetupCode(newCode); me.setupCode = await JWEBuilder.ecdhEs(userKeys.ecdhKeyPair.publicKey).encrypt({ setupCode: newCode }); await backend.users.putMe(me); setupCode.value = newCode; From b7a6a35d14166c6a3d934c7a4f2e487fe176fd8a Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Tue, 11 Feb 2025 09:37:19 +0100 Subject: [PATCH 6/9] Link to #316 --- .../main/java/org/cryptomator/hub/api/UserDto.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java index 298951599..34bbe82e3 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java @@ -74,13 +74,15 @@ public Set getDevices() { return devices; } - // Primary getter for the new key name. @JsonProperty("ecdhPublicKey") public String getEcdhPublicKey() { return ecdhPublicKey; } - // Legacy getter for compatibility. Both getters return the same value. + /** + * Same as {@link #ecdhPublicKey}, kept for compatibility purposes + * @deprecated to be removed in Hub 2.0.0, tracked in #316 + */ @Deprecated(forRemoval = true) @JsonProperty("publicKey") public String getPublicKey() { @@ -92,13 +94,15 @@ public String getEcdsaPublicKey() { return ecdsaPublicKey; } - // Primary getter for private keys. @JsonProperty("privateKeys") public String getPrivateKeys() { return privateKeys; } - // Legacy getter for private keys. + /** + * Same as {@link #privateKeys}, kept for compatibility purposes + * @deprecated to be removed in Hub 2.0.0, tracked in #316 + */ @Deprecated(forRemoval = true) @JsonProperty("privateKey") public String getPrivateKey() { From 349f52083f95e6b78e0e9842c31ca84697ba89e4 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Thu, 13 Feb 2025 11:22:24 +0100 Subject: [PATCH 7/9] Apply suggestions from code review --- .../java/org/cryptomator/hub/api/UserDto.java | 51 +++++++++++-------- .../cryptomator/hub/api/UsersResource.java | 2 +- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java index 34bbe82e3..c75e1a186 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java @@ -28,34 +28,41 @@ public UserDto( @JsonProperty("language") String language, @JsonProperty("devices") Set devices, // Accept either "ecdhPublicKey" or the legacy "publicKey" on input - @Nullable - @JsonProperty("ecdhPublicKey") @OnlyBase64Chars String ecdhPublicKey, - @Nullable - @JsonProperty("publicKey") @OnlyBase64Chars String publicKey, - @Nullable - @JsonProperty("ecdsaPublicKey") @OnlyBase64Chars String ecdsaPublicKey, + @Nullable @JsonProperty("ecdhPublicKey") @OnlyBase64Chars String ecdhPublicKey, + @Nullable @JsonProperty("publicKey") @OnlyBase64Chars String publicKey, + @Nullable @JsonProperty("ecdsaPublicKey") @OnlyBase64Chars String ecdsaPublicKey, // Accept either "privateKeys" or the legacy "privateKey" on input - @Nullable - @JsonProperty("privateKeys") @ValidJWE String privateKeys, - @Nullable - @JsonProperty("privateKey") @ValidJWE String privateKey, - @Nullable - @JsonProperty("setupCode") @ValidJWE String setupCode) { + @Nullable @JsonProperty("privateKeys") @ValidJWE String privateKeys, + @Nullable @JsonProperty("privateKey") @ValidJWE String privateKey, + @Nullable @JsonProperty("setupCode") @ValidJWE String setupCode) { super(id, Type.USER, name, pictureUrl); this.email = email; this.language = language; this.devices = devices; - if (ecdhPublicKey != null) { - this.ecdhPublicKey = ecdhPublicKey; - } else { - this.ecdhPublicKey = publicKey; - } + this.ecdhPublicKey = ecdhPublicKey != null ? ecdhPublicKey : publicKey; this.ecdsaPublicKey = ecdsaPublicKey; - if (privateKeys != null) { - this.privateKeys = privateKeys; - } else { - this.privateKeys = privateKey; - } + this.privateKeys = privateKeys != null ? privateKeys : privateKey; + this.setupCode = setupCode; + } + + public UserDto( + String id, + String name, + String pictureUrl, + String email, + String language, + Set devices, + String ecdhPublicKey, + String ecdsaPublicKey, + String privateKeys, + String setupCode) { + super(id, Type.USER, name, pictureUrl); + this.email = email; + this.language = language; + this.devices = devices; + this.ecdhPublicKey = ecdhPublicKey; + this.ecdsaPublicKey = ecdsaPublicKey; + this.privateKeys = privateKeys; this.setupCode = setupCode; } diff --git a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java index e178eff34..4f1893b7e 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java @@ -160,7 +160,7 @@ public UserDto getMe(@QueryParam("withDevices") boolean withDevices) { User user = userRepo.findById(jwt.getSubject()); Function mapDevices = d -> new DeviceResource.DeviceDto(d.getId(), d.getName(), d.getType(), d.getPublickey(), d.getUserPrivateKeys(), d.getOwner().getId(), d.getCreationTime().truncatedTo(ChronoUnit.MILLIS)); var devices = withDevices ? user.devices.stream().map(mapDevices).collect(Collectors.toSet()) : Set.of(); - return new UserDto(user.getId(), user.getName(), user.getPictureUrl(), user.getEmail(), user.getLanguage(), devices, user.getEcdhPublicKey(), user.getEcdhPublicKey(), user.getEcdsaPublicKey(), user.getPrivateKeys(), user.getPrivateKeys(), user.getSetupCode()); + return new UserDto(user.getId(), user.getName(), user.getPictureUrl(), user.getEmail(), user.getLanguage(), devices, user.getEcdhPublicKey(), user.getEcdsaPublicKey(), user.getPrivateKeys(), user.getSetupCode()); } @POST From ae261c2e7ce1c09751edbd1774e30eb131f0859c Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Thu, 13 Feb 2025 11:30:19 +0100 Subject: [PATCH 8/9] Call new constructor with justPublicInfo() as well --- backend/src/main/java/org/cryptomator/hub/api/UserDto.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java index c75e1a186..e6241b9ce 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java @@ -130,10 +130,8 @@ public static UserDto justPublicInfo(User user) { user.getLanguage(), Set.of(), user.getEcdhPublicKey(), - user.getEcdhPublicKey(), user.getEcdsaPublicKey(), null, - null, null); } } From fb5c91f7f719c4731c8f380e1d7b9abb4f467a72 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Thu, 13 Feb 2025 11:36:13 +0100 Subject: [PATCH 9/9] Apply further suggestions from code review --- .../src/main/java/org/cryptomator/hub/api/UserDto.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java index e6241b9ce..fe30437ce 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java +++ b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java @@ -56,14 +56,7 @@ public UserDto( String ecdsaPublicKey, String privateKeys, String setupCode) { - super(id, Type.USER, name, pictureUrl); - this.email = email; - this.language = language; - this.devices = devices; - this.ecdhPublicKey = ecdhPublicKey; - this.ecdsaPublicKey = ecdsaPublicKey; - this.privateKeys = privateKeys; - this.setupCode = setupCode; + this(id, name, pictureUrl, email, language, devices, ecdhPublicKey, ecdhPublicKey, ecdsaPublicKey, privateKeys, privateKeys, setupCode); } @JsonProperty("email")