Restore `aci` field to `BatchIdentityCheckRequest`
This commit is contained in:
parent
a81c9681a0
commit
6e5ffbe7b5
|
@ -36,8 +36,8 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -371,24 +371,21 @@ public class ProfileController {
|
||||||
private void checkFingerprintAndAdd(BatchIdentityCheckRequest.Element element,
|
private void checkFingerprintAndAdd(BatchIdentityCheckRequest.Element element,
|
||||||
Collection<BatchIdentityCheckResponse.Element> responseElements, MessageDigest md) {
|
Collection<BatchIdentityCheckResponse.Element> responseElements, MessageDigest md) {
|
||||||
|
|
||||||
final Optional<Account> maybeAccount = accountsManager.getByServiceIdentifier(element.uuid());
|
final ServiceIdentifier identifier = Objects.requireNonNullElse(element.uuid(), element.aci());
|
||||||
|
final Optional<Account> maybeAccount = accountsManager.getByServiceIdentifier(identifier);
|
||||||
|
|
||||||
maybeAccount.ifPresent(account -> {
|
maybeAccount.ifPresent(account -> {
|
||||||
if (account.getIdentityKey() == null || account.getPhoneNumberIdentityKey() == null) {
|
final IdentityKey identityKey = account.getIdentityKey(identifier.identityType());
|
||||||
|
if (identityKey == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final IdentityKey identityKey = switch (element.uuid().identityType()) {
|
|
||||||
case ACI -> account.getIdentityKey();
|
|
||||||
case PNI -> account.getPhoneNumberIdentityKey();
|
|
||||||
};
|
|
||||||
|
|
||||||
md.reset();
|
md.reset();
|
||||||
byte[] digest = md.digest(identityKey.serialize());
|
byte[] digest = md.digest(identityKey.serialize());
|
||||||
byte[] fingerprint = Util.truncate(digest, 4);
|
byte[] fingerprint = Util.truncate(digest, 4);
|
||||||
|
|
||||||
if (!Arrays.equals(fingerprint, element.fingerprint())) {
|
if (!Arrays.equals(fingerprint, element.fingerprint())) {
|
||||||
responseElements.add(new BatchIdentityCheckResponse.Element(element.uuid(), identityKey));
|
responseElements.add(new BatchIdentityCheckResponse.Element(element.uuid(), element.aci(), identityKey));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,11 @@ package org.whispersystems.textsecuregcm.entities;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
||||||
import org.whispersystems.textsecuregcm.identity.ServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.ServiceIdentifier;
|
||||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||||
import org.whispersystems.textsecuregcm.util.ServiceIdentifierAdapter;
|
import org.whispersystems.textsecuregcm.util.ServiceIdentifierAdapter;
|
||||||
|
@ -22,13 +24,30 @@ public record BatchIdentityCheckRequest(@Valid @NotNull @Size(max = 1000) List<E
|
||||||
* @param fingerprint most significant 4 bytes of SHA-256 of the 33-byte identity key field (32-byte curve25519 public
|
* @param fingerprint most significant 4 bytes of SHA-256 of the 33-byte identity key field (32-byte curve25519 public
|
||||||
* key prefixed with 0x05)
|
* key prefixed with 0x05)
|
||||||
*/
|
*/
|
||||||
public record Element(@NotNull
|
public record Element(@Nullable
|
||||||
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
||||||
@JsonDeserialize(using = ServiceIdentifierAdapter.ServiceIdentifierDeserializer.class)
|
@JsonDeserialize(using = ServiceIdentifierAdapter.ServiceIdentifierDeserializer.class)
|
||||||
ServiceIdentifier uuid,
|
ServiceIdentifier uuid,
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Deprecated // remove after 2023-11-01
|
||||||
|
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
||||||
|
@JsonDeserialize(using = ServiceIdentifierAdapter.AciServiceIdentifierDeserializer.class)
|
||||||
|
AciServiceIdentifier aci,
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ExactlySize(4)
|
@ExactlySize(4)
|
||||||
byte[] fingerprint) {
|
byte[] fingerprint) {
|
||||||
|
|
||||||
|
public Element {
|
||||||
|
if (aci == null && uuid == null) {
|
||||||
|
throw new IllegalArgumentException("aci and uuid cannot both be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aci != null && uuid != null) {
|
||||||
|
throw new IllegalArgumentException("aci and uuid cannot both be non-null");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.signal.libsignal.protocol.IdentityKey;
|
import org.signal.libsignal.protocol.IdentityKey;
|
||||||
|
@ -21,12 +22,29 @@ public record BatchIdentityCheckResponse(@Valid List<Element> elements) {
|
||||||
public record Element(@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
public record Element(@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
||||||
@JsonDeserialize(using = ServiceIdentifierAdapter.ServiceIdentifierDeserializer.class)
|
@JsonDeserialize(using = ServiceIdentifierAdapter.ServiceIdentifierDeserializer.class)
|
||||||
@NotNull
|
@Nullable
|
||||||
ServiceIdentifier uuid,
|
ServiceIdentifier uuid,
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class)
|
||||||
|
@JsonDeserialize(using = ServiceIdentifierAdapter.AciServiceIdentifierDeserializer.class)
|
||||||
|
@Nullable
|
||||||
|
@Deprecated // remove after 2023-11-01
|
||||||
|
ServiceIdentifier aci,
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonSerialize(using = IdentityKeyAdapter.Serializer.class)
|
@JsonSerialize(using = IdentityKeyAdapter.Serializer.class)
|
||||||
@JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class)
|
@JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class)
|
||||||
IdentityKey identityKey) {
|
IdentityKey identityKey) {
|
||||||
|
|
||||||
|
public Element {
|
||||||
|
if (aci == null && uuid == null) {
|
||||||
|
throw new IllegalArgumentException("aci and uuid cannot both be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aci != null && uuid != null) {
|
||||||
|
throw new IllegalArgumentException("aci and uuid cannot both be non-null");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.Collections;
|
||||||
import java.util.HexFormat;
|
import java.util.HexFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
@ -91,6 +92,7 @@ import org.whispersystems.textsecuregcm.entities.ExpiringProfileKeyCredentialPro
|
||||||
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
|
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
|
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
|
||||||
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
||||||
|
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||||
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
|
||||||
import org.whispersystems.textsecuregcm.identity.ServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.ServiceIdentifier;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
|
@ -192,7 +194,9 @@ class ProfileControllerTest {
|
||||||
profileAccount = mock(Account.class);
|
profileAccount = mock(Account.class);
|
||||||
|
|
||||||
when(profileAccount.getIdentityKey()).thenReturn(ACCOUNT_TWO_IDENTITY_KEY);
|
when(profileAccount.getIdentityKey()).thenReturn(ACCOUNT_TWO_IDENTITY_KEY);
|
||||||
|
when(profileAccount.getIdentityKey(IdentityType.ACI)).thenReturn(ACCOUNT_TWO_IDENTITY_KEY);
|
||||||
when(profileAccount.getPhoneNumberIdentityKey()).thenReturn(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY);
|
when(profileAccount.getPhoneNumberIdentityKey()).thenReturn(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY);
|
||||||
|
when(profileAccount.getIdentityKey(IdentityType.PNI)).thenReturn(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY);
|
||||||
when(profileAccount.getUuid()).thenReturn(AuthHelper.VALID_UUID_TWO);
|
when(profileAccount.getUuid()).thenReturn(AuthHelper.VALID_UUID_TWO);
|
||||||
when(profileAccount.getPhoneNumberIdentifier()).thenReturn(AuthHelper.VALID_PNI_TWO);
|
when(profileAccount.getPhoneNumberIdentifier()).thenReturn(AuthHelper.VALID_PNI_TWO);
|
||||||
when(profileAccount.isEnabled()).thenReturn(true);
|
when(profileAccount.isEnabled()).thenReturn(true);
|
||||||
|
@ -207,7 +211,9 @@ class ProfileControllerTest {
|
||||||
|
|
||||||
when(capabilitiesAccount.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
when(capabilitiesAccount.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||||
when(capabilitiesAccount.getIdentityKey()).thenReturn(ACCOUNT_IDENTITY_KEY);
|
when(capabilitiesAccount.getIdentityKey()).thenReturn(ACCOUNT_IDENTITY_KEY);
|
||||||
|
when(capabilitiesAccount.getIdentityKey(IdentityType.ACI)).thenReturn(ACCOUNT_IDENTITY_KEY);
|
||||||
when(capabilitiesAccount.getPhoneNumberIdentityKey()).thenReturn(ACCOUNT_PHONE_NUMBER_IDENTITY_KEY);
|
when(capabilitiesAccount.getPhoneNumberIdentityKey()).thenReturn(ACCOUNT_PHONE_NUMBER_IDENTITY_KEY);
|
||||||
|
when(capabilitiesAccount.getIdentityKey(IdentityType.PNI)).thenReturn(ACCOUNT_PHONE_NUMBER_IDENTITY_KEY);
|
||||||
when(capabilitiesAccount.isEnabled()).thenReturn(true);
|
when(capabilitiesAccount.isEnabled()).thenReturn(true);
|
||||||
when(capabilitiesAccount.isSenderKeySupported()).thenReturn(true);
|
when(capabilitiesAccount.isSenderKeySupported()).thenReturn(true);
|
||||||
when(capabilitiesAccount.isAnnouncementGroupSupported()).thenReturn(true);
|
when(capabilitiesAccount.isAnnouncementGroupSupported()).thenReturn(true);
|
||||||
|
@ -1164,13 +1170,13 @@ class ProfileControllerTest {
|
||||||
void testBatchIdentityCheck() {
|
void testBatchIdentityCheck() {
|
||||||
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
|
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
|
||||||
.post(Entity.json(new BatchIdentityCheckRequest(List.of(
|
.post(Entity.json(new BatchIdentityCheckRequest(List.of(
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID),
|
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID), null,
|
||||||
convertKeyToFingerprint(ACCOUNT_IDENTITY_KEY)),
|
convertKeyToFingerprint(ACCOUNT_IDENTITY_KEY)),
|
||||||
new BatchIdentityCheckRequest.Element(new PniServiceIdentifier(AuthHelper.VALID_PNI_TWO),
|
new BatchIdentityCheckRequest.Element(new PniServiceIdentifier(AuthHelper.VALID_PNI_TWO), null,
|
||||||
convertKeyToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY)),
|
convertKeyToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY)),
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO),
|
new BatchIdentityCheckRequest.Element(null, new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO),
|
||||||
convertKeyToFingerprint(ACCOUNT_TWO_IDENTITY_KEY)),
|
convertKeyToFingerprint(ACCOUNT_TWO_IDENTITY_KEY)),
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.INVALID_UUID),
|
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.INVALID_UUID), null,
|
||||||
convertKeyToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY))
|
convertKeyToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY))
|
||||||
))))) {
|
))))) {
|
||||||
assertThat(response).isNotNull();
|
assertThat(response).isNotNull();
|
||||||
|
@ -1186,7 +1192,8 @@ class ProfileControllerTest {
|
||||||
new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO), ACCOUNT_TWO_IDENTITY_KEY);
|
new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO), ACCOUNT_TWO_IDENTITY_KEY);
|
||||||
|
|
||||||
final Condition<BatchIdentityCheckResponse.Element> isAnExpectedUuid =
|
final Condition<BatchIdentityCheckResponse.Element> isAnExpectedUuid =
|
||||||
new Condition<>(element -> element.identityKey().equals(expectedIdentityKeys.get(element.uuid())),
|
new Condition<>(element -> element.identityKey()
|
||||||
|
.equals(expectedIdentityKeys.get(Objects.requireNonNullElse(element.uuid(), element.aci()))),
|
||||||
"is an expected UUID with the correct identity key");
|
"is an expected UUID with the correct identity key");
|
||||||
|
|
||||||
final IdentityKey validAciIdentityKey = new IdentityKey(Curve.generateKeyPair().getPublicKey());
|
final IdentityKey validAciIdentityKey = new IdentityKey(Curve.generateKeyPair().getPublicKey());
|
||||||
|
@ -1196,13 +1203,13 @@ class ProfileControllerTest {
|
||||||
|
|
||||||
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
|
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
|
||||||
.post(Entity.json(new BatchIdentityCheckRequest(List.of(
|
.post(Entity.json(new BatchIdentityCheckRequest(List.of(
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID),
|
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID), null,
|
||||||
convertKeyToFingerprint(validAciIdentityKey)),
|
convertKeyToFingerprint(validAciIdentityKey)),
|
||||||
new BatchIdentityCheckRequest.Element(new PniServiceIdentifier(AuthHelper.VALID_PNI_TWO),
|
new BatchIdentityCheckRequest.Element(new PniServiceIdentifier(AuthHelper.VALID_PNI_TWO), null,
|
||||||
convertKeyToFingerprint(secondValidPniIdentityKey)),
|
convertKeyToFingerprint(secondValidPniIdentityKey)),
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO),
|
new BatchIdentityCheckRequest.Element(null, new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO),
|
||||||
convertKeyToFingerprint(secondValidAciIdentityKey)),
|
convertKeyToFingerprint(secondValidAciIdentityKey)),
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.INVALID_UUID),
|
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.INVALID_UUID), null,
|
||||||
convertKeyToFingerprint(invalidAciIdentityKey))
|
convertKeyToFingerprint(invalidAciIdentityKey))
|
||||||
))))) {
|
))))) {
|
||||||
assertThat(response).isNotNull();
|
assertThat(response).isNotNull();
|
||||||
|
@ -1216,13 +1223,17 @@ class ProfileControllerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<BatchIdentityCheckRequest.Element> largeElementList = new ArrayList<>(List.of(
|
final List<BatchIdentityCheckRequest.Element> largeElementList = new ArrayList<>(List.of(
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID), convertKeyToFingerprint(validAciIdentityKey)),
|
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.VALID_UUID), null,
|
||||||
new BatchIdentityCheckRequest.Element(new PniServiceIdentifier(AuthHelper.VALID_PNI_TWO), convertKeyToFingerprint(secondValidPniIdentityKey)),
|
convertKeyToFingerprint(validAciIdentityKey)),
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.INVALID_UUID), convertKeyToFingerprint(invalidAciIdentityKey))));
|
new BatchIdentityCheckRequest.Element(new PniServiceIdentifier(AuthHelper.VALID_PNI_TWO), null,
|
||||||
|
convertKeyToFingerprint(secondValidPniIdentityKey)),
|
||||||
|
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(AuthHelper.INVALID_UUID), null,
|
||||||
|
convertKeyToFingerprint(invalidAciIdentityKey))));
|
||||||
|
|
||||||
for (int i = 0; i < 900; i++) {
|
for (int i = 0; i < 900; i++) {
|
||||||
largeElementList.add(
|
largeElementList.add(
|
||||||
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(UUID.randomUUID()), convertKeyToFingerprint(new IdentityKey(Curve.generateKeyPair().getPublicKey()))));
|
new BatchIdentityCheckRequest.Element(new AciServiceIdentifier(UUID.randomUUID()), null,
|
||||||
|
convertKeyToFingerprint(new IdentityKey(Curve.generateKeyPair().getPublicKey()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
|
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
|
||||||
|
@ -1300,7 +1311,7 @@ class ProfileControllerTest {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
""", Base64.getEncoder().encodeToString(convertKeyToFingerprint(new IdentityKey(Curve.generateKeyPair().getPublicKey())))),
|
""", Base64.getEncoder().encodeToString(convertKeyToFingerprint(new IdentityKey(Curve.generateKeyPair().getPublicKey())))),
|
||||||
422),
|
400),
|
||||||
Arguments.of( // a blank string is invalid
|
Arguments.of( // a blank string is invalid
|
||||||
String.format("""
|
String.format("""
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue