diff --git a/integration-tests/src/main/java/org/signal/integration/TestUser.java b/integration-tests/src/main/java/org/signal/integration/TestUser.java index 8a7dbeff5..88bab99e3 100644 --- a/integration-tests/src/main/java/org/signal/integration/TestUser.java +++ b/integration-tests/src/main/java/org/signal/integration/TestUser.java @@ -126,7 +126,7 @@ public class TestUser { } public AccountAttributes accountAttributes() { - return new AccountAttributes(true, registrationId, pniRegistrationId, "".getBytes(StandardCharsets.UTF_8), "", true, new Device.DeviceCapabilities(false, false, false, false)) + return new AccountAttributes(true, registrationId, pniRegistrationId, "".getBytes(StandardCharsets.UTF_8), "", true, new Device.DeviceCapabilities(false, false, false)) .withUnidentifiedAccessKey(unidentifiedAccessKey) .withRecoveryPassword(registrationPassword); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java index 5de816357..1b2ea56c8 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java @@ -220,8 +220,6 @@ public class DeviceController { if (capabilities == null) { throw new WebApplicationException(Response.status(422, "Missing device capabilities").build()); - } else if (isCapabilityDowngrade(account, capabilities)) { - throw new WebApplicationException(Response.status(409).build()); } final String signalAgent; @@ -355,10 +353,6 @@ public class DeviceController { return Optional.of(aci); } - static boolean isCapabilityDowngrade(Account account, DeviceCapabilities capabilities) { - return account.isPniSupported() && !capabilities.pni(); - } - private static String getUsedTokenKey(final String token) { return "usedToken::" + token; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java index 2f46108cc..f947fc614 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java @@ -7,7 +7,9 @@ package org.whispersystems.textsecuregcm.entities; import org.whispersystems.textsecuregcm.storage.Account; -public record UserCapabilities(boolean paymentActivation, boolean pni) { +public record UserCapabilities(boolean paymentActivation, + // TODO Remove the PNI capability entirely on or after 2024-05-14 + boolean pni) { public static UserCapabilities createForAccount(final Account account) { return new UserCapabilities(account.isPaymentActivationSupported(), true); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcService.java b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcService.java index fa67e1ff1..0c6cd0227 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcService.java @@ -201,7 +201,6 @@ public class DevicesGrpcService extends ReactorDevicesGrpc.DevicesImplBase { d -> d.setCapabilities(new Device.DeviceCapabilities( request.getStorage(), request.getTransfer(), - request.getPni(), request.getPaymentActivation()))))) .thenReturn(SetCapabilitiesResponse.newBuilder().build()); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java index a65861053..39e6a72f8 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java @@ -262,10 +262,6 @@ public class Account { .orElse(false); } - public boolean isPniSupported() { - return allEnabledDevicesHaveCapability(DeviceCapabilities::pni); - } - public boolean isPaymentActivationSupported() { return allEnabledDevicesHaveCapability(DeviceCapabilities::paymentActivation); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java index e4efd1c19..a7f1c759f 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java @@ -251,6 +251,6 @@ public class Device { return this.userAgent; } - public record DeviceCapabilities(boolean storage, boolean transfer, boolean pni, boolean paymentActivation) { + public record DeviceCapabilities(boolean storage, boolean transfer, boolean paymentActivation) { } } diff --git a/service/src/main/proto/org/signal/chat/device.proto b/service/src/main/proto/org/signal/chat/device.proto index eeb19fd6c..9fb4a8146 100644 --- a/service/src/main/proto/org/signal/chat/device.proto +++ b/service/src/main/proto/org/signal/chat/device.proto @@ -147,8 +147,7 @@ message ClearPushTokenResponse {} message SetCapabilitiesRequest { bool storage = 1; bool transfer = 2; - bool pni = 3; - bool paymentActivation = 4; + bool paymentActivation = 3; } message SetCapabilitiesResponse {} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java index 9352dfb10..df696f85d 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java @@ -567,8 +567,6 @@ class AccountControllerV2Test { @Test void pniKeyDistributionSuccess() throws Exception { - when(AuthHelper.VALID_ACCOUNT.isPniSupported()).thenReturn(true); - final AccountIdentityResponse accountIdentityResponse = resources.getJerseyTest() .target("/v2/accounts/phone_number_identity_key_distribution") diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/DeviceControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/DeviceControllerTest.java index 99fd106e0..537ee7a63 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/DeviceControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/DeviceControllerTest.java @@ -141,7 +141,6 @@ class DeviceControllerTest { when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID); when(account.getPhoneNumberIdentifier()).thenReturn(AuthHelper.VALID_PNI); when(account.isEnabled()).thenReturn(false); - when(account.isPniSupported()).thenReturn(true); when(account.isPaymentActivationSupported()).thenReturn(false); when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account)); @@ -213,7 +212,7 @@ class DeviceControllerTest { when(asyncCommands.set(any(), any(), any())).thenReturn(MockRedisFuture.completedFuture(null)); - final AccountAttributes accountAttributes = new AccountAttributes(fetchesMessages, 1234, 5678, null, null, true, new DeviceCapabilities(true, true, true, true)); + final AccountAttributes accountAttributes = new AccountAttributes(fetchesMessages, 1234, 5678, null, null, true, new DeviceCapabilities(true, true, true)); final LinkDeviceRequest request = new LinkDeviceRequest(deviceCode.verificationCode(), accountAttributes, @@ -628,7 +627,7 @@ class DeviceControllerTest { when(asyncCommands.set(any(), any(), any())).thenReturn(MockRedisFuture.completedFuture(null)); final LinkDeviceRequest request = new LinkDeviceRequest(deviceCode.verificationCode(), - new AccountAttributes(false, registrationId, pniRegistrationId, null, null, true, new DeviceCapabilities(true, true, true, true)), + new AccountAttributes(false, registrationId, pniRegistrationId, null, null, true, new DeviceCapabilities(true, true, true)), new DeviceActivationRequest(aciSignedPreKey, pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey, Optional.of(new ApnRegistrationId("apn", null)), Optional.empty())); try (final Response response = resources.getJerseyTest() @@ -685,61 +684,9 @@ class DeviceControllerTest { verify(accountsManager, never()).addDevice(any(), any()); } - @ParameterizedTest - @MethodSource - void deviceDowngradePniTest(final boolean accountSupportsPni, final boolean deviceSupportsPni, final int expectedStatus) { - when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account)); - when(accountsManager.addDevice(any(), any())) - .thenReturn(CompletableFuture.completedFuture(new Pair<>(mock(Account.class), mock(Device.class)))); - - final Device primaryDevice = mock(Device.class); - when(primaryDevice.getId()).thenReturn(Device.PRIMARY_ID); - when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(List.of(primaryDevice)); - - final ECSignedPreKey aciSignedPreKey; - final ECSignedPreKey pniSignedPreKey; - final KEMSignedPreKey aciPqLastResortPreKey; - final KEMSignedPreKey pniPqLastResortPreKey; - - final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair(); - final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair(); - - aciSignedPreKey = KeysHelper.signedECPreKey(1, aciIdentityKeyPair); - pniSignedPreKey = KeysHelper.signedECPreKey(2, pniIdentityKeyPair); - aciPqLastResortPreKey = KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair); - pniPqLastResortPreKey = KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair); - - when(account.getIdentityKey(IdentityType.ACI)).thenReturn(new IdentityKey(aciIdentityKeyPair.getPublicKey())); - when(account.getIdentityKey(IdentityType.PNI)).thenReturn(new IdentityKey(pniIdentityKeyPair.getPublicKey())); - when(account.isPniSupported()).thenReturn(accountSupportsPni); - - when(asyncCommands.set(any(), any(), any())).thenReturn(MockRedisFuture.completedFuture(null)); - - final LinkDeviceRequest request = new LinkDeviceRequest(deviceController.generateVerificationToken(AuthHelper.VALID_UUID), - new AccountAttributes(false, 1234, 5678, null, null, true, new DeviceCapabilities(true, true, deviceSupportsPni, true)), - new DeviceActivationRequest(aciSignedPreKey, pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey, Optional.empty(), Optional.of(new GcmRegistrationId("gcm-id")))); - - try (final Response response = resources.getJerseyTest() - .target("/v1/devices/link") - .request() - .header("Authorization", AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, "password1")) - .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) { - - assertEquals(expectedStatus, response.getStatus()); - } - } - - private static List deviceDowngradePniTest() { - return List.of( - Arguments.of(true, true, 200), - Arguments.of(true, false, 409), - Arguments.of(false, true, 200), - Arguments.of(false, false, 200)); - } - @Test void putCapabilitiesSuccessTest() { - final DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true); + final DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true); final Response response = resources .getJerseyTest() .target("/v1/devices/capabilities") 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 48c7533cf..507806228 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java @@ -200,14 +200,12 @@ class ProfileControllerTest { when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.empty()); when(profileAccount.getUsernameHash()).thenReturn(Optional.of(USERNAME_HASH)); when(profileAccount.getUnidentifiedAccessKey()).thenReturn(Optional.of(UNIDENTIFIED_ACCESS_KEY)); - when(profileAccount.isPniSupported()).thenReturn(true); Account capabilitiesAccount = mock(Account.class); when(capabilitiesAccount.getUuid()).thenReturn(AuthHelper.VALID_UUID); when(capabilitiesAccount.getIdentityKey(IdentityType.ACI)).thenReturn(ACCOUNT_IDENTITY_KEY); when(capabilitiesAccount.getIdentityKey(IdentityType.PNI)).thenReturn(ACCOUNT_PHONE_NUMBER_IDENTITY_KEY); - when(capabilitiesAccount.isPniSupported()).thenReturn(true); when(capabilitiesAccount.isPaymentActivationSupported()).thenReturn(false); when(capabilitiesAccount.isEnabled()).thenReturn(true); 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 85bea1a31..113971d09 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/RegistrationControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/RegistrationControllerTest.java @@ -477,10 +477,10 @@ class RegistrationControllerTest { } final AccountAttributes fetchesMessagesAccountAttributes = - new AccountAttributes(true, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false, false)); + new AccountAttributes(true, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false)); final AccountAttributes pushAccountAttributes = - new AccountAttributes(false, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false, false)); + new AccountAttributes(false, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false)); return Stream.of( // "Fetches messages" is true, but an APNs token is provided @@ -566,7 +566,7 @@ class RegistrationControllerTest { } final AccountAttributes accountAttributes = - new AccountAttributes(true, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false, false)); + new AccountAttributes(true, 1, 1, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false)); return Stream.of( // Signed PNI EC pre-key is missing @@ -736,13 +736,13 @@ class RegistrationControllerTest { final int registrationId = 1; final int pniRegistrationId = 2; - final Device.DeviceCapabilities deviceCapabilities = new Device.DeviceCapabilities(false, false, false, false); + final Device.DeviceCapabilities deviceCapabilities = new Device.DeviceCapabilities(false, false, false); final AccountAttributes fetchesMessagesAccountAttributes = - new AccountAttributes(true, registrationId, pniRegistrationId, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false, false)); + new AccountAttributes(true, registrationId, pniRegistrationId, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false)); final AccountAttributes pushAccountAttributes = - new AccountAttributes(false, registrationId, pniRegistrationId, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false, false)); + new AccountAttributes(false, registrationId, pniRegistrationId, "test".getBytes(StandardCharsets.UTF_8), null, true, new Device.DeviceCapabilities(false, false, false)); final String apnsToken = "apns-token"; final String apnsVoipToken = "apns-voip-token"; @@ -857,7 +857,7 @@ class RegistrationControllerTest { final IdentityKey pniIdentityKey = new IdentityKey(pniIdentityKeyPair.getPublicKey()); final AccountAttributes accountAttributes = new AccountAttributes(true, registrationId, pniRegistrationId, "name".getBytes(StandardCharsets.UTF_8), "reglock", - true, new Device.DeviceCapabilities(true, true, true, true)); + true, new Device.DeviceCapabilities(true, true, true)); final RegistrationRequest request = new RegistrationRequest( Base64.getEncoder().encodeToString(sessionId.getBytes(StandardCharsets.UTF_8)), diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcServiceTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcServiceTest.java index ca1fada62..ca0532958 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcServiceTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/DevicesGrpcServiceTest.java @@ -393,7 +393,6 @@ class DevicesGrpcServiceTest extends SimpleBaseGrpcTest when(updatedAccount.isEnabled()).thenAnswer(stubbing); case "isDiscoverableByPhoneNumber" -> when(updatedAccount.isDiscoverableByPhoneNumber()).thenAnswer(stubbing); case "getNextDeviceId" -> when(updatedAccount.getNextDeviceId()).thenAnswer(stubbing); - case "isPniSupported" -> when(updatedAccount.isPniSupported()).thenAnswer(stubbing); case "isPaymentActivationSupported" -> when(updatedAccount.isPaymentActivationSupported()).thenAnswer(stubbing); case "hasEnabledLinkedDevice" -> when(updatedAccount.hasEnabledLinkedDevice()).thenAnswer(stubbing); case "getRegistrationLock" -> when(updatedAccount.getRegistrationLock()).thenAnswer(stubbing);