Skip to content

Commit

Permalink
Refresh accounts before returning device lists
Browse files Browse the repository at this point in the history
  • Loading branch information
jon-signal authored Dec 5, 2024
1 parent 651e444 commit 4988b4e
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
import org.whispersystems.textsecuregcm.entities.RemoteAttachmentError;
import org.whispersystems.textsecuregcm.entities.RestoreAccountRequest;
import org.whispersystems.textsecuregcm.entities.SetPublicKeyRequest;
import org.whispersystems.textsecuregcm.entities.TransferArchiveResult;
import org.whispersystems.textsecuregcm.entities.TransferArchiveUploadedRequest;
import org.whispersystems.textsecuregcm.identity.IdentityType;
import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
Expand Down Expand Up @@ -143,9 +142,13 @@ private static AtomicInteger buildGauge(final String clientPlatformName) {
@GET
@Produces(MediaType.APPLICATION_JSON)
public DeviceInfoList getDevices(@ReadOnly @Auth AuthenticatedDevice auth) {
return new DeviceInfoList(auth.getAccount().getDevices().stream()
.map(DeviceInfo::forDevice)
.toList());
// Devices may change their own names (and primary devices may change the names of linked devices) and so the device
// state associated with the authenticated account may be stale. Fetch a fresh copy to compensate.
return accounts.getByAccountIdentifier(auth.getAccount().getIdentifier(IdentityType.ACI))
.map(account -> new DeviceInfoList(account.getDevices().stream()
.map(DeviceInfo::forDevice)
.toList()))
.orElseThrow(ForbiddenException::new);
}

@DELETE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
import org.whispersystems.textsecuregcm.entities.DeviceActivationRequest;
import org.whispersystems.textsecuregcm.entities.DeviceInfo;
import org.whispersystems.textsecuregcm.entities.DeviceInfoList;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
Expand All @@ -81,7 +82,6 @@
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.push.WebSocketConnectionEventManager;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.ClientPublicKeysManager;
Expand Down Expand Up @@ -113,7 +113,6 @@ class DeviceControllerTest {
private static final Account maxedAccount = mock(Account.class);
private static final Device primaryDevice = mock(Device.class);
private static final DisconnectionRequestManager disconnectionRequestManager = mock(DisconnectionRequestManager.class);
private static final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
private static final Map<String, Integer> deviceConfiguration = new HashMap<>();
private static final TestClock testClock = TestClock.now();

Expand Down Expand Up @@ -180,6 +179,37 @@ void teardown() {
testClock.unpin();
}

@Test
void getDevices() {
final byte deviceId = Device.PRIMARY_ID;
final byte[] deviceName = "refreshed-device-name".getBytes(StandardCharsets.UTF_8);
final long deviceCreated = System.currentTimeMillis();
final long deviceLastSeen = deviceCreated + 1;

final Device refreshedDevice = mock(Device.class);
when(refreshedDevice.getId()).thenReturn(deviceId);
when(refreshedDevice.getName()).thenReturn(deviceName);
when(refreshedDevice.getCreated()).thenReturn(deviceCreated);
when(refreshedDevice.getLastSeen()).thenReturn(deviceLastSeen);

final Account refreshedAccount = mock(Account.class);
when(refreshedAccount.getDevices()).thenReturn(List.of(refreshedDevice));

when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(refreshedAccount));

final DeviceInfoList deviceInfoList = resources.getJerseyTest()
.target("/v1/devices")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(DeviceInfoList.class);

assertEquals(1, deviceInfoList.devices().size());
assertEquals(deviceId, deviceInfoList.devices().getFirst().id());
assertArrayEquals(deviceName, deviceInfoList.devices().getFirst().name());
assertEquals(deviceCreated, deviceInfoList.devices().getFirst().created());
assertEquals(deviceLastSeen, deviceInfoList.devices().getFirst().lastSeen());
}

@ParameterizedTest
@MethodSource
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
Expand Down

0 comments on commit 4988b4e

Please sign in to comment.