diff --git a/integration-tests/src/main/java/org/signal/integration/Operations.java b/integration-tests/src/main/java/org/signal/integration/Operations.java index 87527ba32..afb96b4d4 100644 --- a/integration-tests/src/main/java/org/signal/integration/Operations.java +++ b/integration-tests/src/main/java/org/signal/integration/Operations.java @@ -45,6 +45,7 @@ import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration; import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse; +import org.whispersystems.textsecuregcm.entities.DeviceActivationRequest; import org.whispersystems.textsecuregcm.entities.ECSignedPreKey; import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey; import org.whispersystems.textsecuregcm.entities.RegistrationRequest; @@ -88,12 +89,12 @@ public final class Operations { true, new IdentityKey(aciIdentityKeyPair.getPublicKey()), new IdentityKey(pniIdentityKeyPair.getPublicKey()), - generateSignedECPreKey(1, aciIdentityKeyPair), - generateSignedECPreKey(2, pniIdentityKeyPair), - generateSignedKEMPreKey(3, aciIdentityKeyPair), - generateSignedKEMPreKey(4, pniIdentityKeyPair), - Optional.empty(), - Optional.empty()); + new DeviceActivationRequest(generateSignedECPreKey(1, aciIdentityKeyPair), + generateSignedECPreKey(2, pniIdentityKeyPair), + generateSignedKEMPreKey(3, aciIdentityKeyPair), + generateSignedKEMPreKey(4, pniIdentityKeyPair), + Optional.empty(), + Optional.empty())); final AccountIdentityResponse registrationResponse = apiPost("/v1/registration", registrationRequest) .authorized(number, accountPassword) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java index fd4ec02fd..211a32e78 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java @@ -64,7 +64,6 @@ public class RegistrationController { private static final String COUNTRY_CODE_TAG_NAME = "countryCode"; private static final String REGION_CODE_TAG_NAME = "regionCode"; private static final String VERIFICATION_TYPE_TAG_NAME = "verification"; - private static final String INVALID_ACCOUNT_ATTRS_COUNTER_NAME = name(RegistrationController.class, "invalidAccountAttrs"); private final AccountsManager accounts; private final PhoneVerificationTokenManager phoneVerificationTokenManager; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java index ab8f62ad2..58a79d49c 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java @@ -178,11 +178,11 @@ public class VerificationController { throws RateLimitExceededException, ObsoletePhoneNumberFormatException { final Pair pushTokenAndType = validateAndExtractPushToken( - request.getUpdateVerificationSessionRequest()); + request.updateVerificationSessionRequest()); final Phonenumber.PhoneNumber phoneNumber; try { - phoneNumber = Util.canonicalizePhoneNumber(PhoneNumberUtil.getInstance().parse(request.getNumber(), null)); + phoneNumber = Util.canonicalizePhoneNumber(PhoneNumberUtil.getInstance().parse(request.number(), null)); } catch (final NumberParseException e) { throw new ServerErrorException("could not parse already validated number", Response.Status.INTERNAL_SERVER_ERROR); } @@ -192,7 +192,7 @@ public class VerificationController { final String sourceHost = (String) requestContext.getProperty(RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME); registrationServiceSession = registrationServiceClient.createRegistrationSession(phoneNumber, sourceHost, - accountsManager.getByE164(request.getNumber()).isPresent(), + accountsManager.getByE164(request.number()).isPresent(), REGISTRATION_RPC_TIMEOUT).join(); } catch (final CancellationException e) { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountCreationResponse.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountCreationResponse.java index 8ad56962d..3e5fe947d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountCreationResponse.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountCreationResponse.java @@ -8,25 +8,11 @@ package org.whispersystems.textsecuregcm.entities; import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.swagger.v3.oas.annotations.media.Schema; -// Note, this class cannot be converted into a record because @JsonUnwrapped does not work with records until 2.19. We are on 2.18 until Dropwizard’s BOM updates. -// https://github.com/FasterXML/jackson-databind/issues/1467 -public class AccountCreationResponse { +public record AccountCreationResponse( - @JsonUnwrapped - private AccountIdentityResponse identityResponse; + @JsonUnwrapped + AccountIdentityResponse identityResponse, - @Schema(description = "If true, there was an existing account registered for this number") - private boolean reregistration; - - public AccountCreationResponse() { - } - - public AccountCreationResponse(AccountIdentityResponse identityResponse, boolean reregistration) { - this.identityResponse = identityResponse; - this.reregistration = reregistration; - } - - public boolean isReregistration() { - return reregistration; - } + @Schema(description = "If true, there was an existing account registered for this number") + boolean reregistration) { } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/CreateVerificationSessionRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/CreateVerificationSessionRequest.java index 09abf785e..ee006510e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/CreateVerificationSessionRequest.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/CreateVerificationSessionRequest.java @@ -7,41 +7,23 @@ package org.whispersystems.textsecuregcm.entities; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.google.common.annotations.VisibleForTesting; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import org.whispersystems.textsecuregcm.util.E164; -// Not a record, because Jackson does not support @JsonUnwrapped with records -// https://github.com/FasterXML/jackson-databind/issues/1497 -public final class CreateVerificationSessionRequest { +public record CreateVerificationSessionRequest( - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "The e164-formatted phone number to be verified") - @E164 - @NotBlank - @JsonProperty - private String number; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "The e164-formatted phone number to be verified") + @E164 + @NotBlank + @JsonProperty + String number, - @Valid - @JsonUnwrapped - private UpdateVerificationSessionRequest updateVerificationSessionRequest; - public CreateVerificationSessionRequest() { - } + @Valid + @JsonUnwrapped + UpdateVerificationSessionRequest updateVerificationSessionRequest) { - @VisibleForTesting - public CreateVerificationSessionRequest(final String number, final UpdateVerificationSessionRequest updateVerificationSessionRequest) { - this.number = number; - this.updateVerificationSessionRequest = updateVerificationSessionRequest; - } - - public String getNumber() { - return number; - } - - public UpdateVerificationSessionRequest getUpdateVerificationSessionRequest() { - return updateVerificationSessionRequest; - } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/RegistrationRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/RegistrationRequest.java index 705b125cd..addeeddc6 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/RegistrationRequest.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/RegistrationRequest.java @@ -5,7 +5,6 @@ package org.whispersystems.textsecuregcm.entities; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -16,7 +15,6 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.NotNull; import java.util.List; -import java.util.Optional; import javax.annotation.Nullable; import org.signal.libsignal.protocol.IdentityKey; import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; @@ -72,31 +70,9 @@ public record RegistrationRequest(@Schema(requiredMode = Schema.RequiredMode.NOT @NotNull @Valid @JsonUnwrapped - @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonProperty DeviceActivationRequest deviceActivationRequest) implements PhoneVerificationRequest { - @JsonCreator - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public RegistrationRequest(@JsonProperty("sessionId") String sessionId, - @JsonProperty("recoveryPassword") byte[] recoveryPassword, - @JsonProperty("accountAttributes") AccountAttributes accountAttributes, - @JsonProperty("skipDeviceTransfer") boolean skipDeviceTransfer, - @JsonProperty("aciIdentityKey") @NotNull @Valid IdentityKey aciIdentityKey, - @JsonProperty("pniIdentityKey") @NotNull @Valid IdentityKey pniIdentityKey, - @JsonProperty("aciSignedPreKey") @NotNull @Valid ECSignedPreKey aciSignedPreKey, - @JsonProperty("pniSignedPreKey") @NotNull @Valid ECSignedPreKey pniSignedPreKey, - @JsonProperty("aciPqLastResortPreKey") @NotNull @Valid KEMSignedPreKey aciPqLastResortPreKey, - @JsonProperty("pniPqLastResortPreKey") @NotNull @Valid KEMSignedPreKey pniPqLastResortPreKey, - @JsonProperty("apnToken") Optional<@Valid ApnRegistrationId> apnToken, - @JsonProperty("gcmToken") Optional<@Valid GcmRegistrationId> gcmToken) { - - // This may seem a little verbose, but at the time of writing, Jackson struggles with `@JsonUnwrapped` members in - // records, and this is a workaround. Please see - // https://github.com/FasterXML/jackson-databind/issues/3726#issuecomment-1525396869 for additional context. - this(sessionId, recoveryPassword, accountAttributes, skipDeviceTransfer, aciIdentityKey, pniIdentityKey, - new DeviceActivationRequest(aciSignedPreKey, pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey, apnToken, gcmToken)); - } - public boolean isEverySignedKeyValid(@Nullable final String userAgent) { if (deviceActivationRequest().aciSignedPreKey() == null || deviceActivationRequest().pniSignedPreKey() == null || diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/VersionedProfileResponse.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/VersionedProfileResponse.java index 0aec22d94..82fd68f2a 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/VersionedProfileResponse.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/VersionedProfileResponse.java @@ -11,86 +11,37 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.whispersystems.textsecuregcm.util.ByteArrayBase64WithPaddingAdapter; -// Note, this class cannot be converted into a record because @JsonUnwrapped does not work with records. -// https://github.com/FasterXML/jackson-databind/issues/1467 -public class VersionedProfileResponse { +public record VersionedProfileResponse( - @JsonUnwrapped - private BaseProfileResponse baseProfileResponse; + @JsonUnwrapped + BaseProfileResponse baseProfileResponse, - @JsonProperty - @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) - @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) - private byte[] name; + @JsonProperty + @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) + @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) + byte[] name, - @JsonProperty - @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) - @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) - private byte[] about; + @JsonProperty + @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) + @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) + byte[] about, - @JsonProperty - @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) - @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) - private byte[] aboutEmoji; + @JsonProperty + @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) + @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) + byte[] aboutEmoji, - @JsonProperty - private String avatar; + @JsonProperty + String avatar, - @JsonProperty - @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) - @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) - private byte[] paymentAddress; + @JsonProperty + @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) + @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) + byte[] paymentAddress, - @JsonProperty - @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) - @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) - private byte[] phoneNumberSharing; + @JsonProperty + @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) + @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) + byte[] phoneNumberSharing) { - public VersionedProfileResponse() { - } - - public VersionedProfileResponse(final BaseProfileResponse baseProfileResponse, - final byte[] name, - final byte[] about, - final byte[] aboutEmoji, - final String avatar, - final byte[] paymentAddress, - final byte[] phoneNumberSharing) { - - this.baseProfileResponse = baseProfileResponse; - this.name = name; - this.about = about; - this.aboutEmoji = aboutEmoji; - this.avatar = avatar; - this.paymentAddress = paymentAddress; - this.phoneNumberSharing = phoneNumberSharing; - } - - public BaseProfileResponse getBaseProfileResponse() { - return baseProfileResponse; - } - - public byte[] getName() { - return name; - } - - public byte[] getAbout() { - return about; - } - - public byte[] getAboutEmoji() { - return aboutEmoji; - } - - public String getAvatar() { - return avatar; - } - - public byte[] getPaymentAddress() { - return paymentAddress; - } - - public byte[] getPhoneNumberSharing() { - return phoneNumberSharing; - } } 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 3c2ea856b..1e19a908e 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java @@ -930,14 +930,14 @@ class ProfileControllerTest { .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(VersionedProfileResponse.class); - assertThat(profile.getBaseProfileResponse().getIdentityKey()).isEqualTo(ACCOUNT_TWO_IDENTITY_KEY); - assertThat(profile.getName()).containsExactly(name); - assertThat(profile.getAbout()).containsExactly(about); - assertThat(profile.getAboutEmoji()).containsExactly(emoji); - assertThat(profile.getAvatar()).isEqualTo("profiles/validavatar"); - assertThat(profile.getPhoneNumberSharing()).containsExactly(phoneNumberSharing); - assertThat(profile.getBaseProfileResponse().getUuid()).isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO)); - assertThat(profile.getBaseProfileResponse().getBadges()).hasSize(1).element(0).has(new Condition<>( + assertThat(profile.baseProfileResponse().getIdentityKey()).isEqualTo(ACCOUNT_TWO_IDENTITY_KEY); + assertThat(profile.name()).containsExactly(name); + assertThat(profile.about()).containsExactly(about); + assertThat(profile.aboutEmoji()).containsExactly(emoji); + assertThat(profile.avatar()).isEqualTo("profiles/validavatar"); + assertThat(profile.phoneNumberSharing()).containsExactly(phoneNumberSharing); + assertThat(profile.baseProfileResponse().getUuid()).isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO)); + assertThat(profile.baseProfileResponse().getBadges()).hasSize(1).element(0).has(new Condition<>( badge -> "Test Badge".equals(badge.getName()), "has badge with expected name")); verify(rateLimiter, times(1)).validate(AuthHelper.VALID_UUID); @@ -982,7 +982,7 @@ class ProfileControllerTest { .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(VersionedProfileResponse.class); - assertThat(profile.getPaymentAddress()).containsExactly(paymentAddress); + assertThat(profile.paymentAddress()).containsExactly(paymentAddress); } when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of(version)); @@ -994,7 +994,7 @@ class ProfileControllerTest { .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(VersionedProfileResponse.class); - assertThat(profile.getPaymentAddress()).containsExactly(paymentAddress); + assertThat(profile.paymentAddress()).containsExactly(paymentAddress); } when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex("someotherversion"))); @@ -1006,7 +1006,7 @@ class ProfileControllerTest { .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(VersionedProfileResponse.class); - assertThat(profile.getPaymentAddress()).isNull(); + assertThat(profile.paymentAddress()).isNull(); } } @@ -1026,7 +1026,7 @@ class ProfileControllerTest { .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(ExpiringProfileKeyCredentialProfileResponse.class); - assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()) + assertThat(profile.getVersionedProfileResponse().baseProfileResponse().getUuid()) .isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID)); assertThat(profile.getCredential()).isNull(); @@ -1245,7 +1245,7 @@ class ProfileControllerTest { .headers(authHeaders) .get(ExpiringProfileKeyCredentialProfileResponse.class); - assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()) + assertThat(profile.getVersionedProfileResponse().baseProfileResponse().getUuid()) .isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID)); assertThat(profile.getCredential()).isEqualTo(credentialResponse); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/RegistrationControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/RegistrationControllerTest.java index c053c4b1e..4ce6dd15c 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/RegistrationControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/RegistrationControllerTest.java @@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.controllers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; @@ -32,6 +31,7 @@ import java.util.Base64; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -519,7 +519,7 @@ class RegistrationControllerTest { } } - static Stream atomicAccountCreationConflictingChannel() { + static List atomicAccountCreationConflictingChannel() { final IdentityKey aciIdentityKey; final IdentityKey pniIdentityKey; final ECSignedPreKey aciSignedPreKey; @@ -544,48 +544,48 @@ class RegistrationControllerTest { final AccountAttributes pushAccountAttributes = new AccountAttributes(false, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, Set.of()); - return Stream.of( - // "Fetches messages" is true, but an APNs token is provided - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - fetchesMessagesAccountAttributes, - true, - aciIdentityKey, - pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.of(new ApnRegistrationId("apns-token")), - Optional.empty())), + return List.of( + Arguments.argumentSet("\"Fetches messages\" is true, but an APNs token is provided", + new RegistrationRequest("session-id", + new byte[0], + fetchesMessagesAccountAttributes, + true, + aciIdentityKey, + pniIdentityKey, + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.of(new ApnRegistrationId("apns-token")), + Optional.empty()))), - // "Fetches messages" is true, but an FCM (GCM) token is provided - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - fetchesMessagesAccountAttributes, - true, - aciIdentityKey, - pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.empty(), - Optional.of(new GcmRegistrationId("gcm-token")))), + Arguments.argumentSet("\"Fetches messages\" is true, but an FCM (GCM) token is provided", + new RegistrationRequest("session-id", + new byte[0], + fetchesMessagesAccountAttributes, + true, + aciIdentityKey, + pniIdentityKey, + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.empty(), + Optional.of(new GcmRegistrationId("gcm-token"))))), - // "Fetches messages" is false, but multiple types of push tokens are provided - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - pushAccountAttributes, - true, - aciIdentityKey, - pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.of(new ApnRegistrationId("apns-token")), - Optional.of(new GcmRegistrationId("gcm-token")))) + Arguments.argumentSet("\"Fetches messages\" is false, but multiple types of push tokens are provided", + new RegistrationRequest("session-id", + new byte[0], + pushAccountAttributes, + true, + aciIdentityKey, + pniIdentityKey, + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.of(new ApnRegistrationId("apns-token")), + Optional.of(new GcmRegistrationId("gcm-token"))))) ); } @@ -608,7 +608,7 @@ class RegistrationControllerTest { } } - static Stream atomicAccountCreationPartialSignedPreKeys() { + static List atomicAccountCreationPartialSignedPreKeys() { final IdentityKey aciIdentityKey; final IdentityKey pniIdentityKey; final ECSignedPreKey aciSignedPreKey; @@ -630,90 +630,90 @@ class RegistrationControllerTest { final AccountAttributes accountAttributes = new AccountAttributes(true, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, Set.of()); - return Stream.of( - // Signed PNI EC pre-key is missing - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - accountAttributes, - true, - aciIdentityKey, - pniIdentityKey, - aciSignedPreKey, - null, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.empty(), - Optional.empty())), + return List.of( + Arguments.argumentSet("Signed PNI EC pre-key is missing", + new RegistrationRequest("session-id", + new byte[0], + accountAttributes, + true, + aciIdentityKey, + pniIdentityKey, + new DeviceActivationRequest(aciSignedPreKey, + null, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.empty(), + Optional.empty()))), - // Signed ACI EC pre-key is missing - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - accountAttributes, - true, - aciIdentityKey, - pniIdentityKey, - null, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.empty(), - Optional.empty())), + Arguments.argumentSet("Signed ACI EC pre-key is missing", + new RegistrationRequest("session-id", + new byte[0], + accountAttributes, + true, + aciIdentityKey, + pniIdentityKey, + new DeviceActivationRequest(null, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.empty(), + Optional.empty()))), - // Signed PNI KEM pre-key is missing - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - accountAttributes, - true, - aciIdentityKey, - pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - null, - Optional.empty(), - Optional.empty())), + Arguments.argumentSet("Signed PNI KEM pre-key is missing", + new RegistrationRequest("session-id", + new byte[0], + accountAttributes, + true, + aciIdentityKey, + pniIdentityKey, + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + null, + Optional.empty(), + Optional.empty()))), - // Signed ACI KEM pre-key is missing - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - accountAttributes, - true, - aciIdentityKey, - pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - null, - pniPqLastResortPreKey, - Optional.empty(), - Optional.empty())), + Arguments.argumentSet("Signed ACI KEM pre-key is missing", + new RegistrationRequest("session-id", + new byte[0], + accountAttributes, + true, + aciIdentityKey, + pniIdentityKey, + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + null, + pniPqLastResortPreKey, + Optional.empty(), + Optional.empty()))), - // All signed pre-keys are present, but ACI identity key is missing - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - accountAttributes, - true, - null, - pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.empty(), - Optional.empty())), + Arguments.argumentSet("All signed pre-keys are present, but ACI identity key is missing", + new RegistrationRequest("session-id", + new byte[0], + accountAttributes, + true, + null, + pniIdentityKey, + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.empty(), + Optional.empty()))), - // All signed pre-keys are present, but PNI identity key is missing - Arguments.of(new RegistrationRequest("session-id", - new byte[0], - accountAttributes, - true, - aciIdentityKey, - null, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.empty(), - Optional.empty())) + Arguments.argumentSet("All signed pre-keys are present, but PNI identity key is missing", + new RegistrationRequest("session-id", + new byte[0], + accountAttributes, + true, + aciIdentityKey, + null, + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.empty(), + Optional.empty()))) ); } @@ -789,7 +789,7 @@ class RegistrationControllerTest { try (Response response = request.post(Entity.json(requestJson("sessionId")))) { assertEquals(200, response.getStatus()); final AccountCreationResponse creationResponse = response.readEntity(AccountCreationResponse.class); - assertEquals(existingAccount, creationResponse.isReregistration()); + assertEquals(existingAccount, creationResponse.reregistration()); } } @@ -806,7 +806,7 @@ class RegistrationControllerTest { && Objects.equals(a.recoveryPassword(), b.recoveryPassword()); } - private static Stream atomicAccountCreationSuccess() { + private static List atomicAccountCreationSuccess() { final IdentityKey aciIdentityKey; final IdentityKey pniIdentityKey; final ECSignedPreKey aciSignedPreKey; @@ -840,20 +840,20 @@ class RegistrationControllerTest { final String apnsToken = "apns-token"; final String gcmToken = "gcm-token"; - return Stream.of( - // Fetches messages; no push tokens - Arguments.of(new RegistrationRequest("session-id", + return List.of( + Arguments.argumentSet("Fetches messages; no push tokens", + new RegistrationRequest("session-id", new byte[0], fetchesMessagesAccountAttributes, true, aciIdentityKey, pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.empty(), - Optional.empty()), + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.empty(), + Optional.empty())), aciIdentityKey, pniIdentityKey, new DeviceSpec( @@ -871,19 +871,19 @@ class RegistrationControllerTest { aciPqLastResortPreKey, pniPqLastResortPreKey)), - // Has APNs tokens - Arguments.of(new RegistrationRequest("session-id", + Arguments.argumentSet("Has APNs tokens", + new RegistrationRequest("session-id", new byte[0], pushAccountAttributes, true, aciIdentityKey, pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.of(new ApnRegistrationId(apnsToken)), - Optional.empty()), + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.of(new ApnRegistrationId(apnsToken)), + Optional.empty())), aciIdentityKey, pniIdentityKey, new DeviceSpec( @@ -901,19 +901,19 @@ class RegistrationControllerTest { aciPqLastResortPreKey, pniPqLastResortPreKey)), - // Has GCM token - Arguments.of(new RegistrationRequest("session-id", + Arguments.argumentSet("Has GCM token", + new RegistrationRequest("session-id", new byte[0], pushAccountAttributes, true, aciIdentityKey, pniIdentityKey, - aciSignedPreKey, - pniSignedPreKey, - aciPqLastResortPreKey, - pniPqLastResortPreKey, - Optional.empty(), - Optional.of(new GcmRegistrationId(gcmToken))), + new DeviceActivationRequest(aciSignedPreKey, + pniSignedPreKey, + aciPqLastResortPreKey, + pniPqLastResortPreKey, + Optional.empty(), + Optional.of(new GcmRegistrationId(gcmToken)))), aciIdentityKey, pniIdentityKey, new DeviceSpec( @@ -965,7 +965,6 @@ class RegistrationControllerTest { KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair), Optional.empty(), Optional.empty())); - try { return SystemMapper.jsonMapper().writerWithDefaultPrettyPrinter().writeValueAsString(request); } catch (final JsonProcessingException e) {