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 2ae19bbe9..86225ce0e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; @@ -86,6 +87,7 @@ import org.whispersystems.textsecuregcm.storage.AbusiveHostRules; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.ChangeNumberManager; +import org.whispersystems.textsecuregcm.storage.ChangeNumberManager.DeviceUpdate; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager; @@ -408,22 +410,27 @@ public class AccountController { throw new ForbiddenException(); } - if (request.getDeviceSignedPrekeys() != null && !request.getDeviceSignedPrekeys().isEmpty()) { - if (request.getDeviceMessages() == null || request.getDeviceMessages().size() != request.getDeviceSignedPrekeys().size() - 1) { - // device_messages should exist and be one shorter than device_signed_prekeys, since it doesn't have the primary's key. - throw new WebApplicationException(Response.status(400).build()); - } + Map devices = Collections.emptyMap(); + if (request.getDeviceUpdates() != null && !request.getDeviceUpdates().isEmpty()) { + devices = request.getDeviceUpdates().entrySet().stream() + .collect(Collectors.toMap( + e -> e.getKey(), + e -> new DeviceUpdate(e.getValue().getSignedPhoneNumberIdentityPreKey(), e.getValue().getMessage(), e.getValue().getRegistrationID()))); try { // Checks that all except master ID are in device messages + List deviceMessages = devices.entrySet().stream() + .map(e -> e.getValue().message()) + .filter(e -> e != null) + .collect(Collectors.toList()); MessageValidation.validateCompleteDeviceList( - authenticatedAccount.getAccount(), request.getDeviceMessages(), + authenticatedAccount.getAccount(), deviceMessages, IncomingMessage::getDestinationDeviceId, true, Optional.of(Device.MASTER_ID)); MessageValidation.validateRegistrationIds( - authenticatedAccount.getAccount(), request.getDeviceMessages(), + authenticatedAccount.getAccount(), deviceMessages, IncomingMessage::getDestinationDeviceId, IncomingMessage::getDestinationRegistrationId); // Checks that all including master ID are in signed prekeys MessageValidation.validateCompleteDeviceList( - authenticatedAccount.getAccount(), request.getDeviceSignedPrekeys().entrySet(), + authenticatedAccount.getAccount(), devices.entrySet(), e -> e.getKey(), false, Optional.empty()); } catch (MismatchedDevicesException e) { throw new WebApplicationException(Response.status(409) @@ -437,9 +444,6 @@ public class AccountController { .entity(new StaleDevices(e.getStaleDevices())) .build()); } - } else if (request.getDeviceMessages() != null && !request.getDeviceMessages().isEmpty()) { - // device_messages shouldn't exist without device_signed_prekeys. - throw new WebApplicationException(Response.status(400).build()); } final String number = request.getNumber(); @@ -470,8 +474,7 @@ public class AccountController { final Account updatedAccount = changeNumberManager.changeNumber( authenticatedAccount.getAccount(), request.getNumber(), - Optional.ofNullable(request.getDeviceSignedPrekeys()).orElse(Collections.emptyMap()), - Optional.ofNullable(request.getDeviceMessages()).orElse(Collections.emptyList())); + devices); return new AccountIdentityResponse( updatedAccount.getUuid(), diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ChangePhoneNumberRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ChangePhoneNumberRequest.java index 5b97e59ec..b884a942f 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ChangePhoneNumberRequest.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ChangePhoneNumberRequest.java @@ -9,7 +9,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import javax.annotation.Nullable; import javax.validation.constraints.NotBlank; -import java.util.List; import java.util.Map; public class ChangePhoneNumberRequest { @@ -26,26 +25,48 @@ public class ChangePhoneNumberRequest { @Nullable final String registrationLock; - @JsonProperty("device_messages") + @JsonProperty("deviceUpdates") @Nullable - final List deviceMessages; + final Map deviceUpdates; - @JsonProperty("device_signed_prekeys") - @Nullable - final Map deviceSignedPrekeys; + public static class DeviceUpdate { + + private final IncomingMessage message; + private final SignedPreKey signedPhoneNumberIdentityPreKey; + private final Integer registrationID; + + @JsonCreator + public DeviceUpdate( + @JsonProperty("message") final IncomingMessage message, + @JsonProperty("signedPhoneNumberIdentityPrekey") final SignedPreKey signedPhoneNumberIdentityPreKey, + @JsonProperty("registratonId") final Integer registrationID) { + this.message = message; + this.signedPhoneNumberIdentityPreKey = signedPhoneNumberIdentityPreKey; + this.registrationID = registrationID; + } + + public IncomingMessage getMessage() { + return message; + } + + public SignedPreKey getSignedPhoneNumberIdentityPreKey() { + return signedPhoneNumberIdentityPreKey; + } + + public Integer getRegistrationID() { + return registrationID; + } + } @JsonCreator public ChangePhoneNumberRequest(@JsonProperty("number") final String number, @JsonProperty("code") final String code, @JsonProperty("reglock") @Nullable final String registrationLock, - @JsonProperty("device_messages") @Nullable final List deviceMessages, - @JsonProperty("device_signed_prekeys") @Nullable final Map deviceSignedPrekeys) { - + @JsonProperty("deviceUpdates") @Nullable final Map deviceUpdates) { this.number = number; this.code = code; this.registrationLock = registrationLock; - this.deviceMessages = deviceMessages; - this.deviceSignedPrekeys = deviceSignedPrekeys; + this.deviceUpdates = deviceUpdates; } public String getNumber() { @@ -62,12 +83,5 @@ public class ChangePhoneNumberRequest { } @Nullable - public List getDeviceMessages() { - return deviceMessages; - } - - @Nullable - public Map getDeviceSignedPrekeys() { - return deviceSignedPrekeys; - } + public Map getDeviceUpdates() { return deviceUpdates; } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManager.java index 984ce8f5b..86a97291c 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManager.java @@ -16,7 +16,6 @@ import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.push.MessageSender; import org.whispersystems.textsecuregcm.push.NotPushRegisteredException; import javax.validation.constraints.NotNull; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -32,11 +31,13 @@ public class ChangeNumberManager { this.accountsManager = accountsManager; } + public record DeviceUpdate(SignedPreKey signedPhoneNumberIdentityPreKey, IncomingMessage message, Integer registrationID) { + } + public Account changeNumber( @NotNull Account account, @NotNull final String number, - @NotNull final Map deviceSignedPrekeys, - @NotNull final List deviceMessages) throws InterruptedException { + @NotNull final Map deviceUpdates) throws InterruptedException { final Account updatedAccount; if (number.equals(account.getNumber())) { @@ -51,14 +52,17 @@ public class ChangeNumberManager { // This makes it so the client can resend a request they didn't get a response for (timeout, etc) // to make sure their messages sent and prekeys were updated, even if the first time around the // server crashed at/above this point. - if (deviceSignedPrekeys != null && !deviceSignedPrekeys.isEmpty()) { - for (Map.Entry entry : deviceSignedPrekeys.entrySet()) { + if (deviceUpdates != null && !deviceUpdates.isEmpty()) { + for (Map.Entry entry : deviceUpdates.entrySet()) { + DeviceUpdate deviceUpdate = entry.getValue(); accountsManager.updateDevice(updatedAccount, entry.getKey(), - d -> d.setPhoneNumberIdentitySignedPreKey(entry.getValue())); - } - - for (IncomingMessage message : deviceMessages) { - sendMessageToSelf(updatedAccount, updatedAccount.getDevice(message.getDestinationDeviceId()), message); + device -> { + if (deviceUpdate.signedPhoneNumberIdentityPreKey() != null) device.setPhoneNumberIdentitySignedPreKey(deviceUpdate.signedPhoneNumberIdentityPreKey()); + if (deviceUpdate.registrationID() != null) device.setRegistrationId(deviceUpdate.registrationID()); + }); + if (deviceUpdate.message() != null) { + sendMessageToSelf(updatedAccount, updatedAccount.getDevice(entry.getKey()), deviceUpdate.message()); + } } } return updatedAccount; diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java index 7c8a25c34..2d626e242 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java @@ -12,9 +12,9 @@ import org.mockito.stubbing.Answer; import org.whispersystems.textsecuregcm.entities.IncomingMessage; import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.push.MessageSender; +import org.whispersystems.textsecuregcm.storage.ChangeNumberManager.DeviceUpdate; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -60,7 +60,7 @@ public class ChangeNumberManagerTest { void changeNumberNoMessages() throws Exception { Account account = mock(Account.class); when(account.getNumber()).thenReturn("+18005551234"); - changeNumberManager.changeNumber(account, "+18025551234", Collections.EMPTY_MAP, Collections.EMPTY_LIST); + changeNumberManager.changeNumber(account, "+18025551234", Collections.EMPTY_MAP); verify(accountsManager).changeNumber(account, "+18025551234"); verify(accountsManager, never()).updateDevice(any(), eq(1L), any()); verify(messageSender, never()).sendMessage(eq(account), any(), any(), eq(false)); @@ -70,8 +70,8 @@ public class ChangeNumberManagerTest { void changeNumberSetPrimaryDevicePrekey() throws Exception { Account account = mock(Account.class); when(account.getNumber()).thenReturn("+18005551234"); - var prekeys = Map.of(1L, new SignedPreKey()); - changeNumberManager.changeNumber(account, "+18025551234", prekeys, Collections.EMPTY_LIST); + var devices = Map.of(1L, new DeviceUpdate(new SignedPreKey(), null, null)); + changeNumberManager.changeNumber(account, "+18025551234", devices); verify(accountsManager).changeNumber(account, "+18025551234"); verify(accountsManager).updateDevice(any(), eq(1L), any()); verify(messageSender, never()).sendMessage(eq(account), any(), any(), eq(false)); @@ -84,11 +84,13 @@ public class ChangeNumberManagerTest { when(account.getUuid()).thenReturn(UUID.randomUUID()); Device d2 = mock(Device.class); when(account.getDevice(2L)).thenReturn(Optional.of(d2)); - var prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey()); IncomingMessage msg = mock(IncomingMessage.class); when(msg.getDestinationDeviceId()).thenReturn(2L); when(msg.getContent()).thenReturn(Base64.encodeBase64String(new byte[]{1})); - changeNumberManager.changeNumber(account, "+18025551234", prekeys, List.of(msg)); + var devices = Map.of( + 1L, new DeviceUpdate(new SignedPreKey(), null, null), + 2L, new DeviceUpdate(new SignedPreKey(), msg, null)); + changeNumberManager.changeNumber(account, "+18025551234", devices); verify(accountsManager).changeNumber(account, "+18025551234"); verify(accountsManager).updateDevice(any(), eq(1L), any()); verify(accountsManager).updateDevice(any(), eq(2L), any()); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java index 600cfdc58..dfabe738e 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java @@ -30,7 +30,7 @@ class DeviceTest { private static Stream testIsEnabled() { return Stream.of( - // master fetchesMessages apnId gcmId signedPreKey lastSeen expectEnabled + // master fetchesMessages apnId gcmId signedPhoneNumberIdentityPreKey lastSeen expectEnabled Arguments.of(true, false, null, null, null, Duration.ofDays(60), false), Arguments.of(true, false, null, null, null, Duration.ofDays(1), false), Arguments.of(true, false, null, null, mock(SignedPreKey.class), Duration.ofDays(60), false), diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java index 2f93b6c31..9a41b594c 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java @@ -69,6 +69,7 @@ import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse; import org.whispersystems.textsecuregcm.entities.ApnRegistrationId; import org.whispersystems.textsecuregcm.entities.ChangePhoneNumberRequest; +import org.whispersystems.textsecuregcm.entities.ChangePhoneNumberRequest.DeviceUpdate; import org.whispersystems.textsecuregcm.entities.GcmRegistrationId; import org.whispersystems.textsecuregcm.entities.IncomingMessage; import org.whispersystems.textsecuregcm.entities.RegistrationLock; @@ -250,7 +251,7 @@ class AccountControllerTest { when(accountsManager.setUsername(AuthHelper.VALID_ACCOUNT, "takenusername")) .thenThrow(new UsernameNotAvailableException()); - when(changeNumberManager.changeNumber(any(), any(), any(), any())).thenAnswer((Answer) invocation -> { + when(changeNumberManager.changeNumber(any(), any(), any())).thenAnswer((Answer) invocation -> { final Account account = invocation.getArgument(0, Account.class); final String number = invocation.getArgument(1, String.class); @@ -1248,10 +1249,10 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null), MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class); - verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), eq(number), any(), any()); + verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), eq(number), any()); assertThat(accountIdentityResponse.getUuid()).isEqualTo(AuthHelper.VALID_UUID); assertThat(accountIdentityResponse.getNumber()).isEqualTo(number); @@ -1268,12 +1269,12 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(400); assertThat(response.readEntity(String.class)).isBlank(); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1286,7 +1287,7 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(400); @@ -1295,7 +1296,7 @@ class AccountControllerTest { assertThat(responseEntity.getOriginalNumber()).isEqualTo(number); assertThat(responseEntity.getNormalizedNumber()).isEqualTo("+447700900111"); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1305,10 +1306,10 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(AuthHelper.VALID_NUMBER, "567890", null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(AuthHelper.VALID_NUMBER, "567890", null, null), MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class); - verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any(), any()); + verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any()); } @Test @@ -1323,11 +1324,11 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(403); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1343,11 +1344,11 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code + "-incorrect", null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code + "-incorrect", null, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(403); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1373,11 +1374,11 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(200); - verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any(), any()); + verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any()); } @Test @@ -1403,11 +1404,11 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(423); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1435,11 +1436,11 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, reglock, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, reglock, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(423); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1467,11 +1468,11 @@ class AccountControllerTest { .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new ChangePhoneNumberRequest(number, code, reglock, null, null), + .put(Entity.entity(new ChangePhoneNumberRequest(number, code, reglock, null), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(200); - verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any(), any()); + verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any()); } @Test @@ -1488,11 +1489,14 @@ class AccountControllerTest { .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, - List.of(new IncomingMessage(1, null, 1, 1, "foo")), null), + Map.of(1L, new DeviceUpdate( + new IncomingMessage(1, null, 1, 1, "foo"), + null, + null))), MediaType.APPLICATION_JSON_TYPE)); - assertThat(response.getStatus()).isEqualTo(400); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + assertThat(response.getStatus()).isEqualTo(409); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1517,14 +1521,19 @@ class AccountControllerTest { .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .put(Entity.entity(new ChangePhoneNumberRequest( number, code, null, - List.of( - new IncomingMessage(1, null, 2, 1, "foo"), - new IncomingMessage(1, null, 4, 1, "foo")), - Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey(), 3L, new SignedPreKey())), + Map.of( + 2L, new DeviceUpdate( + new IncomingMessage(1, null, 2, 1, "foo"), + null, + null), + 4L, new DeviceUpdate( + new IncomingMessage(1, null, 4, 1, "foo"), + null, + null))), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(409); - verify(changeNumberManager, never()).changeNumber(any(), any(), any(), any()); + verify(changeNumberManager, never()).changeNumber(any(), any(), any()); } @Test @@ -1546,10 +1555,19 @@ class AccountControllerTest { when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of( new StoredVerificationCode(code, System.currentTimeMillis(), "push", null))); - var deviceMessages = List.of( + Map perDevice = Map.of( + 1L, new DeviceUpdate( + null, + new SignedPreKey(), + null), + 2L, new DeviceUpdate( new IncomingMessage(1, null, 2, 2, "content2"), - new IncomingMessage(1, null, 3, 3, "content3")); - var deviceKeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey(), 3L, new SignedPreKey()); + new SignedPreKey(), + null), + 3L, new DeviceUpdate( + new IncomingMessage(1, null, 3, 3, "content3"), + new SignedPreKey(), + null)); final AccountIdentityResponse accountIdentityResponse = resources.getJerseyTest() @@ -1557,12 +1575,10 @@ class AccountControllerTest { .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .put(Entity.entity(new ChangePhoneNumberRequest( - number, code, null, - deviceMessages, - deviceKeys), + number, code, null, perDevice), MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class); - verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), eq(number), any(), any()); + verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), eq(number), any()); assertThat(accountIdentityResponse.getUuid()).isEqualTo(AuthHelper.VALID_UUID); assertThat(accountIdentityResponse.getNumber()).isEqualTo(number); @@ -1588,17 +1604,27 @@ class AccountControllerTest { when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of( new StoredVerificationCode(code, System.currentTimeMillis(), "push", null))); + Map perDevice = Map.of( + 1L, new DeviceUpdate( + null, + new SignedPreKey(), + null), + 2L, new DeviceUpdate( + new IncomingMessage(1, null, 2, 1, "foo"), + new SignedPreKey(), + null), + 3L, new DeviceUpdate( + new IncomingMessage(1, null, 3, 1, "foo"), + new SignedPreKey(), + null)); + final Response response = resources.getJerseyTest() .target("/v1/accounts/number") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .put(Entity.entity(new ChangePhoneNumberRequest( - number, code, null, - List.of( - new IncomingMessage(1, null, 2, 1, "foo"), - new IncomingMessage(1, null, 3, 1, "foo")), - Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey(), 3L, new SignedPreKey())), + number, code, null, perDevice), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(410);