Skip to content

Commit

Permalink
Add field to RestoreAccountRequest for device transfer initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
ravi-signal committed Feb 7, 2025
1 parent ea17eee commit 760c573
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,28 @@

package org.whispersystems.textsecuregcm.entities;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;

@Schema(description = """
Represents a request from a new device to restore account data by some method.
""")
public record RestoreAccountRequest(
@NotNull
@Schema(description = "The method by which the new device has requested account data restoration")
Method method) {
Method method,

@Schema(description = "Additional data to use to bootstrap a connection between devices, in standard unpadded base64.",
implementation = String.class)
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Size(max = 4096)
@Nullable byte[] deviceTransferBootstrap) {

public enum Method {
@Schema(description = "Restore account data from a remote message history backup")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.jupiter.api.AfterEach;
Expand All @@ -51,6 +52,7 @@
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
Expand Down Expand Up @@ -1267,7 +1269,7 @@ void waitForTransferArchiveRateLimited() {
void recordRestoreAccountRequest() {
final String token = RandomStringUtils.secure().nextAlphanumeric(16);
final RestoreAccountRequest restoreAccountRequest =
new RestoreAccountRequest(RestoreAccountRequest.Method.LOCAL_BACKUP);
new RestoreAccountRequest(RestoreAccountRequest.Method.LOCAL_BACKUP, null);

when(accountsManager.recordRestoreAccountRequest(token, restoreAccountRequest))
.thenReturn(CompletableFuture.completedFuture(null));
Expand All @@ -1285,7 +1287,7 @@ void recordRestoreAccountRequest() {
void recordRestoreAccountRequestBadToken() {
final String token = RandomStringUtils.secure().nextAlphanumeric(128);
final RestoreAccountRequest restoreAccountRequest =
new RestoreAccountRequest(RestoreAccountRequest.Method.LOCAL_BACKUP);
new RestoreAccountRequest(RestoreAccountRequest.Method.LOCAL_BACKUP, null);

try (final Response response = resources.getJerseyTest()
.target("/v1/devices/restore_account/" + token)
Expand All @@ -1299,7 +1301,7 @@ void recordRestoreAccountRequestBadToken() {
@Test
void recordRestoreAccountRequestInvalidRequest() {
final String token = RandomStringUtils.secure().nextAlphanumeric(16);
final RestoreAccountRequest restoreAccountRequest = new RestoreAccountRequest(null);
final RestoreAccountRequest restoreAccountRequest = new RestoreAccountRequest(null, null);

try (final Response response = resources.getJerseyTest()
.target("/v1/devices/restore_account/" + token)
Expand All @@ -1310,11 +1312,34 @@ void recordRestoreAccountRequestInvalidRequest() {
}
}

@ParameterizedTest
@CsvSource({
"0, true",
"4096, true",
"4097, false"
})
void recordRestoreAccountRequestBootstrapLengthLimit(int bootstrapLength, boolean valid) {
final String token = RandomStringUtils.secure().nextAlphanumeric(16);

final byte[] bootstrap = TestRandomUtil.nextBytes(bootstrapLength);
final RestoreAccountRequest restoreAccountRequest = new RestoreAccountRequest(
RestoreAccountRequest.Method.DEVICE_TRANSFER, bootstrap);

try (final Response response = resources.getJerseyTest()
.target("/v1/devices/restore_account/" + token)
.request()
.put(Entity.json(restoreAccountRequest))) {

assertEquals(valid ? 204 : 422, response.getStatus());
}

}

@Test
void waitForDeviceTransferRequest() {
final String token = RandomStringUtils.secure().nextAlphanumeric(16);
final RestoreAccountRequest restoreAccountRequest =
new RestoreAccountRequest(RestoreAccountRequest.Method.LOCAL_BACKUP);
new RestoreAccountRequest(RestoreAccountRequest.Method.LOCAL_BACKUP, null);

when(accountsManager.waitForRestoreAccountRequest(eq(token), any()))
.thenReturn(CompletableFuture.completedFuture(Optional.of(restoreAccountRequest)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.whispersystems.textsecuregcm.redis.RedisServerExtension;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.util.TestRandomUtil;

import java.nio.charset.StandardCharsets;
import java.time.Clock;
Expand All @@ -33,7 +34,9 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -178,8 +181,9 @@ void waitForTransferArchiveTimeout() {
@Test
void waitForRestoreAccountRequest() {
final String token = RandomStringUtils.secure().nextAlphanumeric(16);
final byte[] deviceTransferBootstrap = TestRandomUtil.nextBytes(100);
final RestoreAccountRequest restoreAccountRequest =
new RestoreAccountRequest(RestoreAccountRequest.Method.DEVICE_TRANSFER);
new RestoreAccountRequest(RestoreAccountRequest.Method.DEVICE_TRANSFER, deviceTransferBootstrap);

final CompletableFuture<Optional<RestoreAccountRequest>> displacedFuture =
accountsManager.waitForRestoreAccountRequest(token, Duration.ofSeconds(5));
Expand All @@ -191,14 +195,17 @@ void waitForRestoreAccountRequest() {

accountsManager.recordRestoreAccountRequest(token, restoreAccountRequest).join();

assertEquals(Optional.of(restoreAccountRequest), activeFuture.join());
final Optional<RestoreAccountRequest> result = activeFuture.join();
assertTrue(result.isPresent());
assertEquals(restoreAccountRequest.method(), result.get().method());
assertArrayEquals(restoreAccountRequest.deviceTransferBootstrap(), result.get().deviceTransferBootstrap());
}

@Test
void waitForRestoreAccountRequestAlreadyRequested() {
final String token = RandomStringUtils.secure().nextAlphanumeric(16);
final RestoreAccountRequest restoreAccountRequest =
new RestoreAccountRequest(RestoreAccountRequest.Method.DEVICE_TRANSFER);
new RestoreAccountRequest(RestoreAccountRequest.Method.DEVICE_TRANSFER, null);

accountsManager.recordRestoreAccountRequest(token, restoreAccountRequest).join();

Expand Down

0 comments on commit 760c573

Please sign in to comment.