diff --git a/integration-tests/src/test/java/org/signal/integration/AccountTest.java b/integration-tests/src/test/java/org/signal/integration/AccountTest.java index aecaf18a1..e1e3cae7b 100644 --- a/integration-tests/src/test/java/org/signal/integration/AccountTest.java +++ b/integration-tests/src/test/java/org/signal/integration/AccountTest.java @@ -91,7 +91,7 @@ public class AccountTest { final ConfirmUsernameHashRequest confirmUsernameHashRequest = new ConfirmUsernameHashRequest( reservedUsername.getHash(), reservedUsername.generateProof(), - new EncryptedUsername("cluck cluck i'm a parrot".getBytes()) + "cluck cluck i'm a parrot".getBytes() ); // try unauthorized Operations diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java index 6f1539e59..c73c2f133 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java @@ -354,11 +354,11 @@ public class AccountController { final Account account = accounts.confirmReservedUsernameHash( auth.getAccount(), confirmRequest.usernameHash(), - Optional.ofNullable(confirmRequest.encryptedUsername()).map(EncryptedUsername::usernameLinkEncryptedValue).orElse(null)); + confirmRequest.encryptedUsername()); final UUID linkHandle = account.getUsernameLinkHandle(); return new UsernameHashResponse( account.getUsernameHash().orElseThrow(() -> new IllegalStateException("Could not get username after setting")), - linkHandle == null ? null : new UsernameLinkHandle(linkHandle)); + linkHandle); } catch (final UsernameReservationNotFoundException e) { throw new WebApplicationException(Status.CONFLICT); } catch (final UsernameHashNotAvailableException e) { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ConfirmUsernameHashRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ConfirmUsernameHashRequest.java index f1c75268e..a38a6efbc 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ConfirmUsernameHashRequest.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ConfirmUsernameHashRequest.java @@ -5,9 +5,9 @@ package org.whispersystems.textsecuregcm.entities; -import javax.validation.Valid; - import javax.annotation.Nullable; +import javax.validation.Valid; +import javax.validation.constraints.Size; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -27,8 +27,10 @@ public record ConfirmUsernameHashRequest( @JsonDeserialize(using = ByteArrayBase64UrlAdapter.Deserializing.class) byte[] zkProof, - @Schema(description = "The encrypted username to be stored for username links") + @Schema(type = "string", description = "The url-safe base64-encoded encrypted username to be stored for username links") @Nullable - @Valid - EncryptedUsername encryptedUsername + @JsonSerialize(using = ByteArrayBase64UrlAdapter.Serializing.class) + @JsonDeserialize(using = ByteArrayBase64UrlAdapter.Deserializing.class) + @Size(min = 1, max = 128) + byte[] encryptedUsername ) {} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/EncryptedUsername.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/EncryptedUsername.java index 0e913dd3d..93f257964 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/EncryptedUsername.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/EncryptedUsername.java @@ -5,8 +5,18 @@ package org.whispersystems.textsecuregcm.entities; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import org.whispersystems.textsecuregcm.util.ByteArrayBase64UrlAdapter; -public record EncryptedUsername(@NotNull @Size(min = 1, max = 128) byte[] usernameLinkEncryptedValue) { +public record EncryptedUsername( + @JsonSerialize(using = ByteArrayBase64UrlAdapter.Serializing.class) + @JsonDeserialize(using = ByteArrayBase64UrlAdapter.Deserializing.class) + @NotNull + @Size(min = 1, max = 128) + @Schema(type = "string", description = "the URL-safe base64 encoding of the encrypted username") + byte[] usernameLinkEncryptedValue) { } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameHashResponse.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameHashResponse.java index 527a4c9d6..f2738668e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameHashResponse.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameHashResponse.java @@ -11,7 +11,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import org.whispersystems.textsecuregcm.controllers.AccountController; import org.whispersystems.textsecuregcm.util.ByteArrayBase64UrlAdapter; import org.whispersystems.textsecuregcm.util.ExactlySize; - +import java.util.UUID; import javax.annotation.Nullable; import javax.validation.Valid; @@ -20,11 +20,11 @@ public record UsernameHashResponse( @JsonSerialize(using = ByteArrayBase64UrlAdapter.Serializing.class) @JsonDeserialize(using = ByteArrayBase64UrlAdapter.Deserializing.class) @ExactlySize(AccountController.USERNAME_HASH_LENGTH) - @Schema(description = "The hash of the confirmed username, as supplied in the request") + @Schema(type = "string", description = "The hash of the confirmed username, as supplied in the request") byte[] usernameHash, @Nullable @Valid - @Schema(description = "A handle that can be included in username links to retrieve the stored encrypted username") - UsernameLinkHandle usernameLinkHandle + @Schema(type = "string", description = "A handle that can be included in username links to retrieve the stored encrypted username") + UUID usernameLinkHandle ) {} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameLinkHandle.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameLinkHandle.java index 0a4114301..0840b4a77 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameLinkHandle.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameLinkHandle.java @@ -7,6 +7,11 @@ package org.whispersystems.textsecuregcm.entities; import java.util.UUID; import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import io.swagger.v3.oas.annotations.media.Schema; -public record UsernameLinkHandle(@NotNull UUID usernameLinkHandle) { +public record UsernameLinkHandle( + @Schema(description = "A handle that can be included in username links to retrieve the stored encrypted username") + @NotNull + UUID usernameLinkHandle) { } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java index 539915b70..d8651752a 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java @@ -639,12 +639,12 @@ class AccountControllerTest { .target("/v1/accounts/username_hash/confirm") .request() .header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, new EncryptedUsername(ENCRYPTED_USERNAME_1)))); + .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, ENCRYPTED_USERNAME_1))); assertThat(response.getStatus()).isEqualTo(200); final UsernameHashResponse respEntity = response.readEntity(UsernameHashResponse.class); assertArrayEquals(respEntity.usernameHash(), USERNAME_HASH_1); - assertEquals(respEntity.usernameLinkHandle().usernameLinkHandle(), uuid); + assertEquals(respEntity.usernameLinkHandle(), uuid); verify(usernameZkProofVerifier).verifyProof(ZK_PROOF, USERNAME_HASH_1); } @@ -680,7 +680,7 @@ class AccountControllerTest { .target("/v1/accounts/username_hash/confirm") .request() .header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, new EncryptedUsername(ENCRYPTED_USERNAME_1)))); + .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, ENCRYPTED_USERNAME_1))); assertThat(response.getStatus()).isEqualTo(409); verify(usernameZkProofVerifier).verifyProof(ZK_PROOF, USERNAME_HASH_1); } @@ -695,7 +695,7 @@ class AccountControllerTest { .target("/v1/accounts/username_hash/confirm") .request() .header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, new EncryptedUsername(ENCRYPTED_USERNAME_1)))); + .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, ENCRYPTED_USERNAME_1))); assertThat(response.getStatus()).isEqualTo(410); verify(usernameZkProofVerifier).verifyProof(ZK_PROOF, USERNAME_HASH_1); } @@ -727,7 +727,7 @@ class AccountControllerTest { .target("/v1/accounts/username_hash/confirm") .request() .header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.json(new ConfirmUsernameHashRequest(usernameHash, ZK_PROOF, new EncryptedUsername(ENCRYPTED_USERNAME_1)))); + .put(Entity.json(new ConfirmUsernameHashRequest(usernameHash, ZK_PROOF, ENCRYPTED_USERNAME_1))); assertThat(response.getStatus()).isEqualTo(422); verifyNoInteractions(usernameZkProofVerifier); } @@ -740,7 +740,7 @@ class AccountControllerTest { .target("/v1/accounts/username_hash/confirm") .request() .header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, new EncryptedUsername(ENCRYPTED_USERNAME_1)))); + .put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF, ENCRYPTED_USERNAME_1))); assertThat(response.getStatus()).isEqualTo(422); verify(usernameZkProofVerifier).verifyProof(ZK_PROOF, USERNAME_HASH_1); }