Add `DeviceCapabilities.pni`

This commit is contained in:
Chris Eager 2022-02-07 13:10:25 -08:00 committed by Chris Eager
parent 6e2ae42dab
commit ed398aa7b9
11 changed files with 176 additions and 89 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
package org.whispersystems.textsecuregcm.controllers; package org.whispersystems.textsecuregcm.controllers;
@ -63,17 +63,16 @@ public class DeviceController {
private final Map<String, Integer> maxDeviceConfiguration; private final Map<String, Integer> maxDeviceConfiguration;
public DeviceController(StoredVerificationCodeManager pendingDevices, public DeviceController(StoredVerificationCodeManager pendingDevices,
AccountsManager accounts, AccountsManager accounts,
MessagesManager messages, MessagesManager messages,
Keys keys, Keys keys,
RateLimiters rateLimiters, RateLimiters rateLimiters,
Map<String, Integer> maxDeviceConfiguration) Map<String, Integer> maxDeviceConfiguration) {
{ this.pendingDevices = pendingDevices;
this.pendingDevices = pendingDevices; this.accounts = accounts;
this.accounts = accounts; this.messages = messages;
this.messages = messages; this.keys = keys;
this.keys = keys; this.rateLimiters = rateLimiters;
this.rateLimiters = rateLimiters;
this.maxDeviceConfiguration = maxDeviceConfiguration; this.maxDeviceConfiguration = maxDeviceConfiguration;
} }
@ -151,12 +150,11 @@ public class DeviceController {
@Path("/{verification_code}") @Path("/{verification_code}")
@ChangesDeviceEnabledState @ChangesDeviceEnabledState
public DeviceResponse verifyDeviceToken(@PathParam("verification_code") String verificationCode, public DeviceResponse verifyDeviceToken(@PathParam("verification_code") String verificationCode,
@HeaderParam("Authorization") BasicAuthorizationHeader authorizationHeader, @HeaderParam("Authorization") BasicAuthorizationHeader authorizationHeader,
@HeaderParam("User-Agent") String userAgent, @HeaderParam("User-Agent") String userAgent,
@NotNull @Valid AccountAttributes accountAttributes, @NotNull @Valid AccountAttributes accountAttributes,
@Context ContainerRequest containerRequest) @Context ContainerRequest containerRequest)
throws RateLimitExceededException, DeviceLimitExceededException throws RateLimitExceededException, DeviceLimitExceededException {
{
String number = authorizationHeader.getUsername(); String number = authorizationHeader.getUsername();
String password = authorizationHeader.getPassword(); String password = authorizationHeader.getPassword();
@ -241,6 +239,7 @@ public class DeviceController {
private boolean isCapabilityDowngrade(Account account, DeviceCapabilities capabilities, String userAgent) { private boolean isCapabilityDowngrade(Account account, DeviceCapabilities capabilities, String userAgent) {
boolean isDowngrade = false; boolean isDowngrade = false;
isDowngrade |= account.isPniSupported() && !capabilities.isPni();
isDowngrade |= account.isChangeNumberSupported() && !capabilities.isChangeNumber(); isDowngrade |= account.isChangeNumberSupported() && !capabilities.isChangeNumber();
isDowngrade |= account.isAnnouncementGroupSupported() && !capabilities.isAnnouncementGroup(); isDowngrade |= account.isAnnouncementGroupSupported() && !capabilities.isAnnouncementGroup();
isDowngrade |= account.isSenderKeySupported() && !capabilities.isSenderKey(); isDowngrade |= account.isSenderKeySupported() && !capabilities.isSenderKey();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
@ -17,13 +17,13 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials; import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock; import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
import javax.annotation.Nullable;
public class Account { public class Account {
@ -234,6 +234,14 @@ public class Account {
.allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isChangeNumber()); .allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isChangeNumber());
} }
public boolean isPniSupported() {
requireNotStale();
return devices.stream()
.filter(Device::isEnabled)
.allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isPni());
}
public boolean isEnabled() { public boolean isEnabled() {
requireNotStale(); requireNotStale();

View File

@ -1,18 +1,17 @@
/* /*
* Copyright 2013-2020 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials; import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.entities.SignedPreKey;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
import javax.annotation.Nullable;
import java.util.concurrent.TimeUnit;
public class Device { public class Device {
public static final long MASTER_ID = 1; public static final long MASTER_ID = 1;
@ -71,27 +70,26 @@ public class Device {
public Device() {} public Device() {}
public Device(long id, String name, String authToken, String salt, public Device(long id, String name, String authToken, String salt,
String gcmId, String apnId, String gcmId, String apnId,
String voipApnId, boolean fetchesMessages, String voipApnId, boolean fetchesMessages,
int registrationId, SignedPreKey signedPreKey, int registrationId, SignedPreKey signedPreKey,
long lastSeen, long created, String userAgent, long lastSeen, long created, String userAgent,
long uninstalledFeedback, DeviceCapabilities capabilities) long uninstalledFeedback, DeviceCapabilities capabilities) {
{ this.id = id;
this.id = id; this.name = name;
this.name = name; this.authToken = authToken;
this.authToken = authToken; this.salt = salt;
this.salt = salt; this.gcmId = gcmId;
this.gcmId = gcmId; this.apnId = apnId;
this.apnId = apnId; this.voipApnId = voipApnId;
this.voipApnId = voipApnId; this.fetchesMessages = fetchesMessages;
this.fetchesMessages = fetchesMessages; this.registrationId = registrationId;
this.registrationId = registrationId; this.signedPreKey = signedPreKey;
this.signedPreKey = signedPreKey; this.lastSeen = lastSeen;
this.lastSeen = lastSeen; this.created = created;
this.created = created; this.userAgent = userAgent;
this.userAgent = userAgent; this.uninstalledFeedback = uninstalledFeedback;
this.uninstalledFeedback = uninstalledFeedback; this.capabilities = capabilities;
this.capabilities = capabilities;
} }
public String getApnId() { public String getApnId() {
@ -175,7 +173,8 @@ public class Device {
return new AuthenticationCredentials(authToken, salt); return new AuthenticationCredentials(authToken, salt);
} }
public @Nullable DeviceCapabilities getCapabilities() { @Nullable
public DeviceCapabilities getCapabilities() {
return capabilities; return capabilities;
} }
@ -293,10 +292,15 @@ public class Device {
@JsonProperty @JsonProperty
private boolean changeNumber; private boolean changeNumber;
public DeviceCapabilities() {} @JsonProperty
private boolean pni;
public DeviceCapabilities() {
}
public DeviceCapabilities(boolean gv2, final boolean gv2_2, final boolean gv2_3, boolean storage, boolean transfer, public DeviceCapabilities(boolean gv2, final boolean gv2_2, final boolean gv2_3, boolean storage, boolean transfer,
boolean gv1Migration, final boolean senderKey, final boolean announcementGroup, final boolean changeNumber) { boolean gv1Migration, final boolean senderKey, final boolean announcementGroup, final boolean changeNumber,
final boolean pni) {
this.gv2 = gv2; this.gv2 = gv2;
this.gv2_2 = gv2_2; this.gv2_2 = gv2_2;
this.gv2_3 = gv2_3; this.gv2_3 = gv2_3;
@ -306,6 +310,7 @@ public class Device {
this.senderKey = senderKey; this.senderKey = senderKey;
this.announcementGroup = announcementGroup; this.announcementGroup = announcementGroup;
this.changeNumber = changeNumber; this.changeNumber = changeNumber;
this.pni = pni;
} }
public boolean isGv2() { public boolean isGv2() {
@ -343,5 +348,9 @@ public class Device {
public boolean isChangeNumber() { public boolean isChangeNumber() {
return changeNumber; return changeNumber;
} }
public boolean isPni() {
return pni;
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -132,22 +132,22 @@ class MessageControllerTest {
add(new Device(1, null, "foo", "bar", add(new Device(1, null, "foo", "bar",
"isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, true, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, true, false,
false, false, false))); false, false, false, false)));
}}; }};
Set<Device> multiDeviceList = new HashSet<>() {{ Set<Device> multiDeviceList = new HashSet<>() {{
add(new Device(1, null, "foo", "bar", add(new Device(1, null, "foo", "bar",
"isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false,
false, false, false))); false, false, false, false)));
add(new Device(2, null, "foo", "bar", add(new Device(2, null, "foo", "bar",
"isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false,
false, false, false))); false, false, false, false)));
add(new Device(3, null, "foo", "bar", add(new Device(3, null, "foo", "bar",
"isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false, false, false, false, false, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false, false, false, false, false, false,
false, false, false))); false, false, false, false)));
}}; }};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, SINGLE_DEVICE_PNI, singleDeviceList, "1234".getBytes()); Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, SINGLE_DEVICE_PNI, singleDeviceList, "1234".getBytes());

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -182,7 +182,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(),
"testUserAgent-" + random.nextInt(), 0, "testUserAgent-" + random.nextInt(), 0,
new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean(), random.nextBoolean()))); random.nextBoolean(), random.nextBoolean(), random.nextBoolean())));
}); });

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -579,7 +579,7 @@ class AccountsManagerTest {
@ValueSource(booleans = {true, false}) @ValueSource(booleans = {true, false})
void testCreateWithStorageCapability(final boolean hasStorage) throws InterruptedException { void testCreateWithStorageCapability(final boolean hasStorage) throws InterruptedException {
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true,
new DeviceCapabilities(false, false, false, hasStorage, false, false, false, false, false)); new DeviceCapabilities(false, false, false, hasStorage, false, false, false, false, false, false));
final Account account = accountsManager.create("+18005550123", "password", null, attributes, new ArrayList<>()); final Account account = accountsManager.create("+18005550123", "password", null, attributes, new ArrayList<>());

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -830,11 +830,17 @@ class AccountsTest {
} }
private Device generateDevice(long id) { private Device generateDevice(long id) {
Random random = new Random(System.currentTimeMillis()); Random random = new Random(System.currentTimeMillis());
SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), "testSignature-" + random.nextInt()); SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(),
return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(), "testSignature-" + random.nextInt());
"testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), "testUserAgent-" + random.nextInt() , 0, new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(),
random.nextBoolean(), random.nextBoolean(), random.nextBoolean())); "testSalt-" + random.nextInt(),
"testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(),
random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(),
"testUserAgent-" + random.nextInt(), 0,
new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean()));
} }
private Account generateAccount(String number, UUID uuid, final UUID pni) { private Account generateAccount(String number, UUID uuid, final UUID pni) {

View File

@ -72,7 +72,7 @@ class DeviceTest {
final boolean gv2_2Capability, final boolean gv2_3Capability, final boolean expectGv2Supported) { final boolean gv2_2Capability, final boolean gv2_3Capability, final boolean expectGv2Supported) {
final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability, final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability,
gv2_3Capability, false, false, false, gv2_3Capability, false, false, false,
false, false, false); false, false, false, false);
final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt", final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt",
null, apnId, null, false, 1, null, 0, 0, "user-agent", 0, capabilities); null, apnId, null, false, 1, null, 0, 0, "user-agent", 0, capabilities);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
package org.whispersystems.textsecuregcm.tests.controllers; package org.whispersystems.textsecuregcm.tests.controllers;
@ -128,6 +128,7 @@ class DeviceControllerTest {
when(account.isSenderKeySupported()).thenReturn(true); when(account.isSenderKeySupported()).thenReturn(true);
when(account.isAnnouncementGroupSupported()).thenReturn(true); when(account.isAnnouncementGroupSupported()).thenReturn(true);
when(account.isChangeNumberSupported()).thenReturn(true); when(account.isChangeNumberSupported()).thenReturn(true);
when(account.isPniSupported()).thenReturn(true);
when(pendingDevicesManager.getCodeForNumber(AuthHelper.VALID_NUMBER)).thenReturn( when(pendingDevicesManager.getCodeForNumber(AuthHelper.VALID_NUMBER)).thenReturn(
Optional.of(new StoredVerificationCode("5678901", System.currentTimeMillis(), null, null))); Optional.of(new StoredVerificationCode("5678901", System.currentTimeMillis(), null, null)));
@ -304,7 +305,7 @@ class DeviceControllerTest {
void deviceDowngradeCapabilitiesTest(final String userAgent, final boolean gv2, final boolean gv2_2, void deviceDowngradeCapabilitiesTest(final String userAgent, final boolean gv2, final boolean gv2_2,
final boolean gv2_3, final int expectedStatus) { final boolean gv2_3, final int expectedStatus) {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(gv2, gv2_2, gv2_3, true, false, true, true, true, DeviceCapabilities deviceCapabilities = new DeviceCapabilities(gv2, gv2_2, gv2_3, true, false, true, true, true,
true); true, true);
AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources.getJerseyTest() Response response = resources.getJerseyTest()
.target("/v1/devices/5678901") .target("/v1/devices/5678901")
@ -344,7 +345,8 @@ class DeviceControllerTest {
@Test @Test
void deviceDowngradeGv1MigrationTest() { void deviceDowngradeGv1MigrationTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, false, true, true, true); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, false, true, true,
true, true);
AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources.getJerseyTest() Response response = resources.getJerseyTest()
.target("/v1/devices/5678901") .target("/v1/devices/5678901")
@ -355,7 +357,7 @@ class DeviceControllerTest {
assertThat(response.getStatus()).isEqualTo(409); assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, true, true, true, true); deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources.getJerseyTest() response = resources.getJerseyTest()
.target("/v1/devices/5678901") .target("/v1/devices/5678901")
@ -369,7 +371,8 @@ class DeviceControllerTest {
@Test @Test
void deviceDowngradeSenderKeyTest() { void deviceDowngradeSenderKeyTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, false, true, true); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, false, true,
true, true);
AccountAttributes accountAttributes = AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources Response response = resources
@ -381,7 +384,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE)); .put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409); assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true); deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources response = resources
.getJerseyTest() .getJerseyTest()
@ -395,7 +398,8 @@ class DeviceControllerTest {
@Test @Test
void deviceDowngradeAnnouncementGroupTest() { void deviceDowngradeAnnouncementGroupTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, false, true); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, false,
true, true);
AccountAttributes accountAttributes = AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources Response response = resources
@ -407,7 +411,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE)); .put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409); assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true); deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources response = resources
.getJerseyTest() .getJerseyTest()
@ -421,7 +425,36 @@ class DeviceControllerTest {
@Test @Test
void deviceDowngradeChangeNumberTest() { void deviceDowngradeChangeNumberTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, false); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, false, true);
AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources
.getJerseyTest()
.target("/v1/devices/5678901")
.request()
.header("Authorization",
AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.header("User-Agent", "Signal-Android/5.42.8675309 Android/30")
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()
.target("/v1/devices/5678901")
.request()
.header("Authorization",
AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.header("User-Agent", "Signal-Android/5.42.8675309 Android/30")
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
void deviceDowngradePniTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true,
false);
AccountAttributes accountAttributes = AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources Response response = resources
@ -433,7 +466,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE)); .put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409); assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true); deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources response = resources
.getJerseyTest() .getJerseyTest()

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2020 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -58,6 +58,10 @@ class AccountTest {
private final Device changeNumberIncapableDevice = mock(Device.class); private final Device changeNumberIncapableDevice = mock(Device.class);
private final Device changeNumberIncapableExpiredDevice = mock(Device.class); private final Device changeNumberIncapableExpiredDevice = mock(Device.class);
private final Device pniCapableDevice = mock(Device.class);
private final Device pniIncapableDevice = mock(Device.class);
private final Device pniIncapableExpiredDevice = mock(Device.class);
@BeforeEach @BeforeEach
void setup() { void setup() {
when(oldMasterDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(366)); when(oldMasterDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(366));
@ -93,52 +97,64 @@ class AccountTest {
when(gv2IncapableExpiredDevice.isEnabled()).thenReturn(false); when(gv2IncapableExpiredDevice.isEnabled()).thenReturn(false);
when(gv1MigrationCapableDevice.getCapabilities()).thenReturn( when(gv1MigrationCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false, false)); new DeviceCapabilities(true, true, true, true, true, true, false, false, false, false));
when(gv1MigrationCapableDevice.isEnabled()).thenReturn(true); when(gv1MigrationCapableDevice.isEnabled()).thenReturn(true);
when(gv1MigrationIncapableDevice.getCapabilities()).thenReturn( when(gv1MigrationIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, false, false, false, false)); new DeviceCapabilities(true, true, true, true, true, false, false, false, false, false));
when(gv1MigrationIncapableDevice.isEnabled()).thenReturn(true); when(gv1MigrationIncapableDevice.isEnabled()).thenReturn(true);
when(gv1MigrationIncapableExpiredDevice.getCapabilities()).thenReturn( when(gv1MigrationIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, false, false, false, false)); new DeviceCapabilities(true, true, true, true, true, false, false, false, false, false));
when(gv1MigrationIncapableExpiredDevice.isEnabled()).thenReturn(false); when(gv1MigrationIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(senderKeyCapableDevice.getCapabilities()).thenReturn( when(senderKeyCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false));
when(senderKeyCapableDevice.isEnabled()).thenReturn(true); when(senderKeyCapableDevice.isEnabled()).thenReturn(true);
when(senderKeyIncapableDevice.getCapabilities()).thenReturn( when(senderKeyIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false, false)); new DeviceCapabilities(true, true, true, true, true, true, false, false, false, false));
when(senderKeyIncapableDevice.isEnabled()).thenReturn(true); when(senderKeyIncapableDevice.isEnabled()).thenReturn(true);
when(senderKeyIncapableExpiredDevice.getCapabilities()).thenReturn( when(senderKeyIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false, false)); new DeviceCapabilities(true, true, true, true, true, true, false, false, false, false));
when(senderKeyIncapableExpiredDevice.isEnabled()).thenReturn(false); when(senderKeyIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(announcementGroupCapableDevice.getCapabilities()).thenReturn( when(announcementGroupCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, true, false)); new DeviceCapabilities(true, true, true, true, true, true, true, true, false, false));
when(announcementGroupCapableDevice.isEnabled()).thenReturn(true); when(announcementGroupCapableDevice.isEnabled()).thenReturn(true);
when(announcementGroupIncapableDevice.getCapabilities()).thenReturn( when(announcementGroupIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false));
when(announcementGroupIncapableDevice.isEnabled()).thenReturn(true); when(announcementGroupIncapableDevice.isEnabled()).thenReturn(true);
when(announcementGroupIncapableExpiredDevice.getCapabilities()).thenReturn( when(announcementGroupIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false));
when(announcementGroupIncapableExpiredDevice.isEnabled()).thenReturn(false); when(announcementGroupIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(changeNumberCapableDevice.getCapabilities()).thenReturn( when(changeNumberCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, true)); new DeviceCapabilities(true, true, true, true, true, true, true, false, true, false));
when(changeNumberCapableDevice.isEnabled()).thenReturn(true); when(changeNumberCapableDevice.isEnabled()).thenReturn(true);
when(changeNumberIncapableDevice.getCapabilities()).thenReturn( when(changeNumberIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false));
when(changeNumberIncapableDevice.isEnabled()).thenReturn(true); when(changeNumberIncapableDevice.isEnabled()).thenReturn(true);
when(changeNumberIncapableExpiredDevice.getCapabilities()).thenReturn( when(changeNumberIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false));
when(changeNumberIncapableExpiredDevice.isEnabled()).thenReturn(false); when(changeNumberIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(pniCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, true));
when(pniCapableDevice.isEnabled()).thenReturn(true);
when(pniIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false));
when(pniIncapableDevice.isEnabled()).thenReturn(true);
when(pniIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false));
when(pniIncapableExpiredDevice.isEnabled()).thenReturn(false);
} }
@Test @Test
@ -317,6 +333,19 @@ class AccountTest {
"1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isTrue(); "1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isTrue();
} }
@Test
void isPniSupported() {
assertThat(new Account("+18005551234", UUID.randomUUID(),
UUID.randomUUID(), Set.of(pniCapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isPniSupported()).isTrue();
assertThat(new Account("+18005551234", UUID.randomUUID(),
UUID.randomUUID(), Set.of(pniCapableDevice, pniIncapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isPniSupported()).isFalse();
assertThat(new Account("+18005551234", UUID.randomUUID(),
UUID.randomUUID(), Set.of(pniCapableDevice, pniIncapableExpiredDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isPniSupported()).isTrue();
}
@Test @Test
void stale() { void stale() {
final Account account = new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), Collections.emptySet(), new byte[0]); final Account account = new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), Collections.emptySet(), new byte[0]);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -153,6 +153,10 @@ public class AccountsHelper {
when(updatedAccount.isChangeNumberSupported()).thenAnswer(stubbing); when(updatedAccount.isChangeNumberSupported()).thenAnswer(stubbing);
break; break;
} }
case "isPniSupported": {
when(updatedAccount.isPniSupported()).thenAnswer(stubbing);
break;
}
case "getEnabledDeviceCount": { case "getEnabledDeviceCount": {
when(updatedAccount.getEnabledDeviceCount()).thenAnswer(stubbing); when(updatedAccount.getEnabledDeviceCount()).thenAnswer(stubbing);
break; break;
@ -188,7 +192,6 @@ public class AccountsHelper {
account.markStale(); account.markStale();
} }
return updatedAccount; return updatedAccount;
} }