diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java index c45884bf9..a433e2238 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java @@ -394,8 +394,15 @@ public class ProfileController { maybeAccount = accountsManager.getByAccountIdentifier(element.aci()); usePhoneNumberIdentity = false; } else { - maybeAccount = accountsManager.getByPhoneNumberIdentifier(element.pni()); - usePhoneNumberIdentity = true; + final Optional maybeAciAccount = accountsManager.getByAccountIdentifier(element.uuid()); + + if (maybeAciAccount.isEmpty()) { + maybeAccount = accountsManager.getByPhoneNumberIdentifier(element.uuid()); + usePhoneNumberIdentity = true; + } else { + maybeAccount = maybeAciAccount; + usePhoneNumberIdentity = false; + } } maybeAccount.ifPresent(account -> { @@ -414,7 +421,7 @@ public class ProfileController { byte[] fingerprint = Util.truncate(digest, 4); if (!Arrays.equals(fingerprint, element.fingerprint())) { - responseElements.add(new BatchIdentityCheckResponse.Element(element.aci(), element.pni(), identityKeyBytes)); + responseElements.add(new BatchIdentityCheckResponse.Element(element.aci(), element.uuid(), identityKeyBytes)); } }); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckRequest.java index 12ce35717..54bf15418 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckRequest.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckRequest.java @@ -16,22 +16,21 @@ import org.whispersystems.textsecuregcm.util.ExactlySize; public record BatchIdentityCheckRequest(@Valid @NotNull @Size(max = 1000) List elements) { /** - * Exactly one of {@code aci} and {@code pni} must be non-null - * - * @param aci account id - * @param pni phone number id + * @param uuid account id or phone number id * @param fingerprint most significant 4 bytes of SHA-256 of the 33-byte identity key field (32-byte curve25519 public * key prefixed with 0x05) */ - public record Element(@Nullable UUID aci, @Nullable UUID pni, @NotNull @ExactlySize(4) byte[] fingerprint) { + public record Element(@Deprecated @Nullable UUID aci, + @Nullable UUID uuid, + @NotNull @ExactlySize(4) byte[] fingerprint) { public Element { - if (aci == null && pni == null) { - throw new IllegalArgumentException("aci and pni cannot both be null"); + if (aci == null && uuid == null) { + throw new IllegalArgumentException("aci and uuid cannot both be null"); } - if (aci != null && pni != null) { - throw new IllegalArgumentException("aci and pni cannot both be non-null"); + if (aci != null && uuid != null) { + throw new IllegalArgumentException("aci and uuid cannot both be non-null"); } } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckResponse.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckResponse.java index 27f926f3d..becb15f73 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckResponse.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/BatchIdentityCheckResponse.java @@ -5,6 +5,7 @@ package org.whispersystems.textsecuregcm.entities; +import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; import java.util.UUID; import javax.annotation.Nullable; @@ -14,18 +15,17 @@ import org.whispersystems.textsecuregcm.util.ExactlySize; public record BatchIdentityCheckResponse(@Valid List elements) { - /** - * Exactly one of {@code aci} and {@code pni} must be non-null - */ - public record Element(@Nullable UUID aci, @Nullable UUID pni, @NotNull @ExactlySize(33) byte[] identityKey) { + public record Element(@Deprecated @JsonInclude(JsonInclude.Include.NON_EMPTY) @Nullable UUID aci, + @JsonInclude(JsonInclude.Include.NON_EMPTY) @Nullable UUID uuid, + @NotNull @ExactlySize(33) byte[] identityKey) { public Element { - if (aci == null && pni == null) { - throw new IllegalArgumentException("aci and pni cannot both be null"); + if (aci == null && uuid == null) { + throw new IllegalArgumentException("aci and uuid cannot both be null"); } - if (aci != null && pni != null) { - throw new IllegalArgumentException("aci and pni cannot both be non-null"); + if (aci != null && uuid != null) { + throw new IllegalArgumentException("aci and uuid cannot both be non-null"); } } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java index 0d283a3e4..36b39ba2e 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java @@ -1237,6 +1237,8 @@ class ProfileControllerTest { convertStringToFingerprint(ACCOUNT_IDENTITY_KEY)), new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO, convertStringToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY)), + new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_UUID_TWO, + convertStringToFingerprint(ACCOUNT_TWO_IDENTITY_KEY)), new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null, convertStringToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY)) ))))) { @@ -1250,8 +1252,10 @@ class ProfileControllerTest { Condition isAnExpectedUuid = new Condition<>(element -> { if (AuthHelper.VALID_UUID.equals(element.aci())) { return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_IDENTITY_KEY), element.identityKey()); - } else if (AuthHelper.VALID_PNI_TWO.equals(element.pni())) { + } else if (AuthHelper.VALID_PNI_TWO.equals(element.uuid())) { return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY), element.identityKey()); + } else if (AuthHelper.VALID_UUID_TWO.equals(element.uuid())) { + return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_TWO_IDENTITY_KEY), element.identityKey()); } else { return false; } @@ -1262,15 +1266,18 @@ class ProfileControllerTest { new BatchIdentityCheckRequest.Element(AuthHelper.VALID_UUID, null, convertStringToFingerprint("else1234")), new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO, convertStringToFingerprint("another1")), + new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_UUID_TWO, + convertStringToFingerprint("another2")), new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null, convertStringToFingerprint("456")) ))))) { assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(200); BatchIdentityCheckResponse identityCheckResponse = response.readEntity(BatchIdentityCheckResponse.class); assertThat(identityCheckResponse).isNotNull(); - assertThat(identityCheckResponse.elements()).isNotNull().hasSize(2); + assertThat(identityCheckResponse.elements()).isNotNull().hasSize(3); assertThat(identityCheckResponse.elements()).element(0).isNotNull().is(isAnExpectedUuid); assertThat(identityCheckResponse.elements()).element(1).isNotNull().is(isAnExpectedUuid); + assertThat(identityCheckResponse.elements()).element(2).isNotNull().is(isAnExpectedUuid); } List largeElementList = new ArrayList<>(List.of( @@ -1294,12 +1301,12 @@ class ProfileControllerTest { } @Test - void testBatchIdentityCheckDeserialization() { + void testBatchIdentityCheckDeserialization() throws Exception { Condition isAnExpectedUuid = new Condition<>(element -> { if (AuthHelper.VALID_UUID.equals(element.aci())) { return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_IDENTITY_KEY), element.identityKey()); - } else if (AuthHelper.VALID_PNI_TWO.equals(element.pni())) { + } else if (AuthHelper.VALID_PNI_TWO.equals(element.uuid())) { return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY), element.identityKey()); } else { return false; @@ -1311,7 +1318,7 @@ class ProfileControllerTest { { "elements": [ { "aci": "%s", "fingerprint": "%s" }, - { "pni": "%s", "fingerprint": "%s" }, + { "uuid": "%s", "fingerprint": "%s" }, { "aci": "%s", "fingerprint": "%s" } ] } @@ -1322,7 +1329,13 @@ class ProfileControllerTest { .post(Entity.entity(json, "application/json"))) { assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(200); - BatchIdentityCheckResponse identityCheckResponse = response.readEntity(BatchIdentityCheckResponse.class); + String responseJson = response.readEntity(String.class); + + // `null` properties should be omitted from the response + assertThat(responseJson).doesNotContain("null"); + + BatchIdentityCheckResponse identityCheckResponse = SystemMapper.getMapper() + .readValue(responseJson, BatchIdentityCheckResponse.class); assertThat(identityCheckResponse).isNotNull(); assertThat(identityCheckResponse.elements()).isNotNull().hasSize(2); assertThat(identityCheckResponse.elements()).element(0).isNotNull().is(isAnExpectedUuid); @@ -1342,11 +1355,11 @@ class ProfileControllerTest { static Stream testBatchIdentityCheckDeserializationBadRequest() { return Stream.of( - Arguments.of( // aci and pni cannot both be null + Arguments.of( // aci and uuid cannot both be null """ { "elements": [ - { "aci": null, "pni": null, "fingerprint": "%s" } + { "aci": null, "uuid": null, "fingerprint": "%s" } ] } """), @@ -1354,7 +1367,7 @@ class ProfileControllerTest { """ { "elements": [ - { "aci": "", "pni": null, "fingerprint": "%s" } + { "aci": "", "uuid": null, "fingerprint": "%s" } ] } """ @@ -1363,15 +1376,15 @@ class ProfileControllerTest { """ { "elements": [ - { "aci": null, "pni": " ", "fingerprint": "%s" } + { "aci": null, "uuid": " ", "fingerprint": "%s" } ] } """), - Arguments.of( // aci and pni cannot both be non-null + Arguments.of( // aci and uuid cannot both be non-null String.format(""" { "elements": [ - { "aci": "%s", "pni": "%s", "fingerprint": "%s" } + { "aci": "%s", "uuid": "%s", "fingerprint": "%s" } ] } """, AuthHelper.VALID_UUID, AuthHelper.VALID_PNI,