Make Backup methods synchronous

This commit is contained in:
ravi-signal
2026-01-21 15:01:58 -05:00
committed by GitHub
parent b4db86a39b
commit 023296feaf
16 changed files with 640 additions and 626 deletions

View File

@@ -12,6 +12,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -64,7 +65,6 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager;
import org.whispersystems.textsecuregcm.tests.util.ExperimentHelper;
import org.whispersystems.textsecuregcm.util.CompletableFutureTestUtil;
import org.whispersystems.textsecuregcm.util.TestClock;
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
@@ -107,19 +107,18 @@ public class BackupAuthManagerTest {
}
@Test
void commitBackupId() {
void commitBackupId() throws RateLimitExceededException {
final BackupAuthManager authManager = create();
final Account account = mock(Account.class);
when(account.getUuid()).thenReturn(aci);
when(accountsManager.updateAsync(any(), any()))
when(accountsManager.update(any(), any()))
.thenAnswer(invocation -> {
final Account a = invocation.getArgument(0);
final Consumer<Account> updater = invocation.getArgument(1);
updater.accept(a);
return CompletableFuture.completedFuture(a);
return a;
});
final BackupAuthCredentialRequest messagesCredentialRequest = backupAuthTestUtil.getRequest(messagesBackupKey, aci);
@@ -127,7 +126,7 @@ public class BackupAuthManagerTest {
authManager.commitBackupId(account, primaryDevice(),
Optional.of(messagesCredentialRequest),
Optional.of(mediaCredentialRequest)).join();
Optional.of(mediaCredentialRequest));
verify(account).setBackupCredentialRequests(messagesCredentialRequest.serialize(),
mediaCredentialRequest.serialize());
@@ -138,13 +137,13 @@ public class BackupAuthManagerTest {
void commitOnAnyBackupLevel(final BackupLevel backupLevel) {
final BackupAuthManager authManager = create();
final Account account = new MockAccountBuilder().backupLevel(backupLevel).build();
when(accountsManager.updateAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(account));
when(accountsManager.update(any(), any())).thenReturn(account);
final ThrowableAssert.ThrowingCallable commit = () ->
authManager.commitBackupId(account,
primaryDevice(),
Optional.of(backupAuthTestUtil.getRequest(messagesBackupKey, aci)),
Optional.of(backupAuthTestUtil.getRequest(mediaBackupKey, aci))).join();
Optional.of(backupAuthTestUtil.getRequest(mediaBackupKey, aci)));
Assertions.assertThatNoException().isThrownBy(commit);
}
@@ -152,13 +151,13 @@ public class BackupAuthManagerTest {
void commitRequiresPrimary() {
final BackupAuthManager authManager = create();
final Account account = new MockAccountBuilder().build();
when(accountsManager.updateAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(account));
when(accountsManager.update(any(), any())).thenReturn(account);
final ThrowableAssert.ThrowingCallable commit = () ->
authManager.commitBackupId(account,
linkedDevice(),
Optional.of(backupAuthTestUtil.getRequest(messagesBackupKey, aci)),
Optional.of(backupAuthTestUtil.getRequest(mediaBackupKey, aci))).join();
Optional.of(backupAuthTestUtil.getRequest(mediaBackupKey, aci)));
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(commit)
.extracting(ex -> ex.getStatus().getCode())
@@ -186,7 +185,7 @@ public class BackupAuthManagerTest {
final RedemptionRange range = range(Duration.ofDays(1));
final List<BackupAuthManager.Credential> creds =
authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1))).join();
authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1)));
assertThat(creds).hasSize(2);
assertThat(requestContext
@@ -207,7 +206,7 @@ public class BackupAuthManagerTest {
.mediaCredential(backupAuthTestUtil.getRequest(mediaBackupKey, aci))
.build();
assertThat(authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1))).join())
assertThat(authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1))))
.hasSize(2);
}
@@ -220,7 +219,7 @@ public class BackupAuthManagerTest {
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() ->
authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1))).join())
authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1))))
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.Code.NOT_FOUND);
}
@@ -245,7 +244,7 @@ public class BackupAuthManagerTest {
.build();
final List<BackupAuthManager.Credential> creds = authManager.getBackupAuthCredentials(account,
credentialType, range(Duration.ofDays(7))).join();
credentialType, range(Duration.ofDays(7)));
assertThat(creds).hasSize(8);
Instant redemptionTime = clock.instant().truncatedTo(ChronoUnit.DAYS);
@@ -275,7 +274,7 @@ public class BackupAuthManagerTest {
final List<BackupAuthManager.Credential> creds = authManager.getBackupAuthCredentials(
account,
BackupCredentialType.MESSAGES,
range(RedemptionRange.MAX_REDEMPTION_DURATION)).join();
range(RedemptionRange.MAX_REDEMPTION_DURATION));
Instant redemptionTime = Instant.EPOCH;
final BackupAuthCredentialRequestContext requestContext = BackupAuthCredentialRequestContext.create(
messagesBackupKey, aci);
@@ -311,15 +310,15 @@ public class BackupAuthManagerTest {
.backupVoucher(null)
.build();
when(accountsManager.updateAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(updated));
when(accountsManager.update(any(), any())).thenReturn(updated);
clock.pin(day2.plus(Duration.ofSeconds(1)));
assertThat(authManager.getBackupAuthCredentials(account, BackupCredentialType.MESSAGES, range(Duration.ofDays(7))).join())
assertThat(authManager.getBackupAuthCredentials(account, BackupCredentialType.MESSAGES, range(Duration.ofDays(7))))
.hasSize(8);
@SuppressWarnings("unchecked") final ArgumentCaptor<Consumer<Account>> accountUpdater = ArgumentCaptor.forClass(
Consumer.class);
verify(accountsManager, times(1)).updateAsync(any(), accountUpdater.capture());
verify(accountsManager, times(1)).update(any(), accountUpdater.capture());
// If the account is not expired when we go to update it, we shouldn't wipe it out
final Account alreadyUpdated = mock(Account.class);
@@ -343,11 +342,11 @@ public class BackupAuthManagerTest {
.mediaCredential(Optional.of(new byte[0]))
.build();
clock.pin(Instant.EPOCH.plus(Duration.ofDays(1)));
when(accountsManager.updateAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(account));
when(accountsManager.update(any(), any())).thenReturn(account);
when(redeemedReceiptsManager.put(any(), eq(expirationTime.getEpochSecond()), eq(201L), eq(aci)))
.thenReturn(CompletableFuture.completedFuture(true));
authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)).join();
verify(accountsManager, times(1)).updateAsync(any(), any());
authManager.redeemReceipt(account, receiptPresentation(201, expirationTime));
verify(accountsManager, times(1)).update(any(), any());
}
@Test
@@ -361,7 +360,7 @@ public class BackupAuthManagerTest {
.thenReturn(CompletableFuture.completedFuture(true));
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() ->
authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)).join())
authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)))
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.Code.ABORTED);
}
@@ -379,13 +378,13 @@ public class BackupAuthManagerTest {
.build();
clock.pin(Instant.EPOCH.plus(Duration.ofDays(1)));
when(accountsManager.updateAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(account));
when(accountsManager.update(any(), any())).thenReturn(account);
when(redeemedReceiptsManager.put(any(), eq(newExpirationTime.getEpochSecond()), eq(201L), eq(aci)))
.thenReturn(CompletableFuture.completedFuture(true));
authManager.redeemReceipt(account, receiptPresentation(201, newExpirationTime)).join();
authManager.redeemReceipt(account, receiptPresentation(201, newExpirationTime));
final ArgumentCaptor<Consumer<Account>> updaterCaptor = ArgumentCaptor.captor();
verify(accountsManager, times(1)).updateAsync(any(), updaterCaptor.capture());
verify(accountsManager, times(1)).update(any(), updaterCaptor.capture());
updaterCaptor.getValue().accept(account);
// Should select the voucher with the later expiration time
@@ -398,7 +397,7 @@ public class BackupAuthManagerTest {
clock.pin(expirationTime.plus(Duration.ofSeconds(1)));
final BackupAuthManager authManager = create();
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), receiptPresentation(3, expirationTime)).join())
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), receiptPresentation(3, expirationTime)))
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.Code.INVALID_ARGUMENT);
verifyNoInteractions(accountsManager);
@@ -413,7 +412,7 @@ public class BackupAuthManagerTest {
final BackupAuthManager authManager = create();
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() ->
authManager.redeemReceipt(mock(Account.class), receiptPresentation(level, expirationTime)).join())
authManager.redeemReceipt(mock(Account.class), receiptPresentation(level, expirationTime)))
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.Code.INVALID_ARGUMENT);
verifyNoInteractions(accountsManager);
@@ -425,7 +424,7 @@ public class BackupAuthManagerTest {
final BackupAuthManager authManager = create();
final ReceiptCredentialPresentation invalid = receiptPresentation(ServerSecretParams.generate(), 3L, Instant.EPOCH);
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), invalid).join())
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), invalid))
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.Code.INVALID_ARGUMENT);
verifyNoInteractions(accountsManager);
@@ -433,7 +432,7 @@ public class BackupAuthManagerTest {
}
@Test
void receiptAlreadyRedeemed() throws InvalidInputException, VerificationFailedException {
void receiptAlreadyRedeemed() {
final Instant expirationTime = Instant.EPOCH.plus(Duration.ofDays(1));
final BackupAuthManager authManager = create();
final Account account = new MockAccountBuilder()
@@ -441,12 +440,12 @@ public class BackupAuthManagerTest {
.build();
clock.pin(Instant.EPOCH.plus(Duration.ofDays(1)));
when(accountsManager.updateAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(account));
when(accountsManager.update(any(), any())).thenReturn(account);
when(redeemedReceiptsManager.put(any(), eq(expirationTime.getEpochSecond()), eq(201L), eq(aci)))
.thenReturn(CompletableFuture.completedFuture(false));
final CompletableFuture<Void> result = authManager.redeemReceipt(account, receiptPresentation(201, expirationTime));
assertThat(CompletableFutureTestUtil.assertFailsWithCause(StatusRuntimeException.class, result))
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() -> authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)))
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.Code.INVALID_ARGUMENT);
verifyNoInteractions(accountsManager);
@@ -484,8 +483,7 @@ public class BackupAuthManagerTest {
? new Account.BackupVoucher(1, Instant.EPOCH.plus(Duration.ofSeconds(1)))
: null)
.build();
final BackupAuthManager.BackupIdRotationLimit limit = authManager.checkBackupIdRotationLimit(account)
.toCompletableFuture().join();
final BackupAuthManager.BackupIdRotationLimit limit = authManager.checkBackupIdRotationLimit(account);
final boolean expectHasPermits = !messageLimited && (!mediaLimited || !hasVoucher);
final Duration expectedDuration = expectHasPermits ? Duration.ZERO : Duration.ofDays(1);
assertThat(limit.hasPermitsRemaining()).isEqualTo(expectHasPermits);
@@ -524,7 +522,7 @@ public class BackupAuthManagerTest {
.backupVoucher(backupVoucher)
.build();
when(accountsManager.updateAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(account));
when(accountsManager.update(any(), any())).thenReturn(account);
final Optional<BackupAuthCredentialRequest> newMessagesCredential = switch (messageChange) {
case MATCH -> Optional.of(storedMessagesCredential);
@@ -543,7 +541,7 @@ public class BackupAuthManagerTest {
final boolean expectRateLimit = ((mediaChange == CredentialChangeType.MISMATCH) && rateLimitMediaBackupId && paid)
|| ((messageChange == CredentialChangeType.MISMATCH) && rateLimitMessagesBackupId);
final ThrowableAssert.ThrowingCallable commit = () ->
authManager.commitBackupId(account, primaryDevice(), newMessagesCredential, newMediaCredential).join();
authManager.commitBackupId(account, primaryDevice(), newMessagesCredential, newMediaCredential);
if (messageChange == CredentialChangeType.NO_UPDATE && mediaChange == CredentialChangeType.NO_UPDATE) {
assertThatExceptionOfType(StatusRuntimeException.class)
@@ -551,7 +549,7 @@ public class BackupAuthManagerTest {
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.Code.INVALID_ARGUMENT);
} else if (expectRateLimit) {
assertThatException().isThrownBy(commit).withRootCauseInstanceOf(RateLimitExceededException.class);
assertThatExceptionOfType(RateLimitExceededException.class).isThrownBy(commit);
} else {
assertThatNoException().isThrownBy(commit);
}
@@ -611,26 +609,29 @@ public class BackupAuthManagerTest {
}
private static RateLimiters rateLimiter(final UUID aci, boolean rateLimitBackupId,
boolean rateLimitPaidMediaBackupId) {
final RateLimiters limiters = mock(RateLimiters.class);
private static RateLimiters rateLimiter(final UUID aci, boolean rateLimitBackupId, boolean rateLimitPaidMediaBackupId) {
try {
final RateLimiters limiters = mock(RateLimiters.class);
final RateLimiter allowLimiter = mock(RateLimiter.class);
when(allowLimiter.hasAvailablePermitsAsync(eq(aci), anyLong())).thenReturn(CompletableFuture.completedFuture(true));
when(allowLimiter.validateAsync(aci)).thenReturn(CompletableFuture.completedFuture(null));
when(allowLimiter.config()).thenReturn(new RateLimiterConfig(1, Duration.ofDays(1), false));
final RateLimiter allowLimiter = mock(RateLimiter.class);
when(allowLimiter.hasAvailablePermitsAsync(eq(aci), anyLong())).thenReturn(
CompletableFuture.completedFuture(true));
when(allowLimiter.config()).thenReturn(new RateLimiterConfig(1, Duration.ofDays(1), false));
final RateLimiter denyLimiter = mock(RateLimiter.class);
when(denyLimiter.hasAvailablePermitsAsync(eq(aci), anyLong())).thenReturn(CompletableFuture.completedFuture(false));
when(denyLimiter.validateAsync(aci))
.thenReturn(CompletableFuture.failedFuture(new RateLimitExceededException(null)));
when(denyLimiter.config()).thenReturn(new RateLimiterConfig(1, Duration.ofDays(1), false));
final RateLimiter denyLimiter = mock(RateLimiter.class);
when(denyLimiter.hasAvailablePermitsAsync(eq(aci), anyLong())).thenReturn(
CompletableFuture.completedFuture(false));
doThrow(new RateLimitExceededException(null)).when(denyLimiter).validate(aci);
when(denyLimiter.config()).thenReturn(new RateLimiterConfig(1, Duration.ofDays(1), false));
when(limiters.forDescriptor(RateLimiters.For.SET_BACKUP_ID))
.thenReturn(rateLimitBackupId ? denyLimiter : allowLimiter);
when(limiters.forDescriptor(RateLimiters.For.SET_PAID_MEDIA_BACKUP_ID))
.thenReturn(rateLimitPaidMediaBackupId ? denyLimiter : allowLimiter);
return limiters;
when(limiters.forDescriptor(RateLimiters.For.SET_BACKUP_ID))
.thenReturn(rateLimitBackupId ? denyLimiter : allowLimiter);
when(limiters.forDescriptor(RateLimiters.For.SET_PAID_MEDIA_BACKUP_ID))
.thenReturn(rateLimitPaidMediaBackupId ? denyLimiter : allowLimiter);
return limiters;
} catch (RateLimitExceededException e) {
throw new RuntimeException(e);
}
}
private RedemptionRange range(Duration length) {

View File

@@ -76,6 +76,6 @@ public class BackupAuthTestUtil {
});
final RedemptionRange redemptionRange;
redemptionRange = RedemptionRange.inclusive(clock, redemptionStart, redemptionEnd);
return issuer.getBackupAuthCredentials(account, credentialType, redemptionRange).join();
return issuer.getBackupAuthCredentials(account, credentialType, redemptionRange);
}
}

View File

@@ -13,6 +13,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -230,11 +231,11 @@ public class BackupManagerTest {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, backupLevel);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
backupManager.createMessageBackupUploadDescriptor(backupUser);
verify(tusCredentialGenerator, times(1))
.generateUpload("%s/%s".formatted(backupUser.backupDir(), BackupManager.MESSAGE_BACKUP_NAME));
final BackupManager.BackupInfo info = backupManager.backupInfo(backupUser).join();
final BackupManager.BackupInfo info = backupManager.backupInfo(backupUser);
assertThat(info.backupSubdir()).isEqualTo(backupUser.backupDir()).isNotBlank();
assertThat(info.messageBackupKey()).isEqualTo(BackupManager.MESSAGE_BACKUP_NAME);
assertThat(info.mediaUsedSpace()).isEqualTo(Optional.empty());
@@ -253,18 +254,17 @@ public class BackupManagerTest {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, backupLevel);
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() -> backupManager.createMessageBackupUploadDescriptor(backupUser).join())
.isThrownBy(() -> backupManager.createMessageBackupUploadDescriptor(backupUser))
.matches(exception -> exception.getStatus().getCode() == Status.UNAUTHENTICATED.getCode());
}
@Test
public void createTemporaryMediaAttachmentRateLimited() {
public void createTemporaryMediaAttachmentRateLimited() throws RateLimitExceededException {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
when(mediaUploadLimiter.validateAsync(eq(BackupManager.rateLimitKey(backupUser))))
.thenReturn(CompletableFuture.failedFuture(new RateLimitExceededException(null)));
CompletableFutureTestUtil.assertFailsWithCause(
RateLimitExceededException.class,
backupManager.createTemporaryAttachmentUploadDescriptor(backupUser).toCompletableFuture());
doThrow(new RateLimitExceededException(null))
.when(mediaUploadLimiter).validate(eq(BackupManager.rateLimitKey(backupUser)));
assertThatExceptionOfType(RateLimitExceededException.class)
.isThrownBy(() -> backupManager.createTemporaryAttachmentUploadDescriptor(backupUser));
}
@Test
@@ -297,11 +297,11 @@ public class BackupManagerTest {
// create backup at t=tstart
testClock.pin(tstart);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
backupManager.createMessageBackupUploadDescriptor(backupUser);
// refresh at t=tnext
testClock.pin(tnext);
backupManager.ttlRefresh(backupUser).join();
backupManager.ttlRefresh(backupUser);
checkExpectedExpirations(
tnext.truncatedTo(ChronoUnit.DAYS),
@@ -319,11 +319,11 @@ public class BackupManagerTest {
// create backup at t=tstart
testClock.pin(tstart);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
backupManager.createMessageBackupUploadDescriptor(backupUser);
// create again at t=tnext
testClock.pin(tnext);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
backupManager.createMessageBackupUploadDescriptor(backupUser);
checkExpectedExpirations(
tnext.truncatedTo(ChronoUnit.DAYS),
@@ -363,7 +363,7 @@ public class BackupManagerTest {
backupManager.setPublicKey(
presentation,
keyPair.getPrivateKey().calculateSignature(presentation.serialize()),
keyPair.getPublicKey()).join();
keyPair.getPublicKey());
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() -> backupManager.authenticateBackupUser(
@@ -384,10 +384,10 @@ public class BackupManagerTest {
final byte[] signature = keyPair.getPrivateKey().calculateSignature(presentation.serialize());
// haven't set a public key yet
assertThat(CompletableFutureTestUtil.assertFailsWithCause(
StatusRuntimeException.class,
backupManager.authenticateBackupUser(presentation, signature, null))
.getStatus().getCode())
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() -> backupManager.authenticateBackupUser(presentation, signature, null))
.extracting(StatusRuntimeException::getStatus)
.extracting(Status::getCode)
.isEqualTo(Status.UNAUTHENTICATED.getCode());
}
@@ -401,17 +401,17 @@ public class BackupManagerTest {
final byte[] signature1 = keyPair1.getPrivateKey().calculateSignature(presentation.serialize());
final byte[] signature2 = keyPair2.getPrivateKey().calculateSignature(presentation.serialize());
backupManager.setPublicKey(presentation, signature1, keyPair1.getPublicKey()).join();
backupManager.setPublicKey(presentation, signature1, keyPair1.getPublicKey());
// shouldn't be able to set a different public key
assertThat(CompletableFutureTestUtil.assertFailsWithCause(
StatusRuntimeException.class,
assertThatExceptionOfType(StatusRuntimeException.class).isThrownBy(() ->
backupManager.setPublicKey(presentation, signature2, keyPair2.getPublicKey()))
.getStatus().getCode())
.extracting(StatusRuntimeException::getStatus)
.extracting(Status::getCode)
.isEqualTo(Status.UNAUTHENTICATED.getCode());
// should be able to set the same public key again (noop)
backupManager.setPublicKey(presentation, signature1, keyPair1.getPublicKey()).join();
backupManager.setPublicKey(presentation, signature1, keyPair1.getPublicKey());
}
@Test
@@ -432,17 +432,17 @@ public class BackupManagerTest {
.extracting(ex -> ex.getStatus().getCode())
.isEqualTo(Status.UNAUTHENTICATED.getCode());
backupManager.setPublicKey(presentation, signature, keyPair.getPublicKey()).join();
backupManager.setPublicKey(presentation, signature, keyPair.getPublicKey());
// shouldn't be able to authenticate with an invalid signature
assertThat(CompletableFutureTestUtil.assertFailsWithCause(
StatusRuntimeException.class,
backupManager.authenticateBackupUser(presentation, wrongSignature, null))
.getStatus().getCode())
assertThatExceptionOfType(StatusRuntimeException.class)
.isThrownBy(() -> backupManager.authenticateBackupUser(presentation, wrongSignature, null))
.extracting(StatusRuntimeException::getStatus)
.extracting(Status::getCode)
.isEqualTo(Status.UNAUTHENTICATED.getCode());
// correct signature
final AuthenticatedBackupUser user = backupManager.authenticateBackupUser(presentation, signature, null).join();
final AuthenticatedBackupUser user = backupManager.authenticateBackupUser(presentation, signature, null);
assertThat(user.backupId()).isEqualTo(presentation.getBackupId());
assertThat(user.backupLevel()).isEqualTo(BackupLevel.FREE);
}
@@ -456,15 +456,15 @@ public class BackupManagerTest {
backupKey, aci);
final ECKeyPair keyPair = ECKeyPair.generate();
final byte[] signature = keyPair.getPrivateKey().calculateSignature(oldCredential.serialize());
backupManager.setPublicKey(oldCredential, signature, keyPair.getPublicKey()).join();
backupManager.setPublicKey(oldCredential, signature, keyPair.getPublicKey());
// should be accepted the day before to forgive clock skew
testClock.pin(Instant.ofEpochSecond(1));
assertThatNoException().isThrownBy(() -> backupManager.authenticateBackupUser(oldCredential, signature, null).join());
assertThatNoException().isThrownBy(() -> backupManager.authenticateBackupUser(oldCredential, signature, null));
// should be accepted the day after to forgive clock skew
testClock.pin(Instant.ofEpochSecond(1).plus(Duration.ofDays(2)));
assertThatNoException().isThrownBy(() -> backupManager.authenticateBackupUser(oldCredential, signature, null).join());
assertThatNoException().isThrownBy(() -> backupManager.authenticateBackupUser(oldCredential, signature, null));
// should be rejected the day after that
testClock.pin(Instant.ofEpochSecond(1).plus(Duration.ofDays(3)));
@@ -713,8 +713,7 @@ public class BackupManagerTest {
Optional.of("newCursor")
)));
final BackupManager.ListMediaResult result = backupManager.list(backupUser, cursor, 17)
.toCompletableFuture().join();
final BackupManager.ListMediaResult result = backupManager.list(backupUser, cursor, 17);
assertThat(result.media()).hasSize(1);
assertThat(result.media().getFirst().cdn()).isEqualTo(13);
assertThat(result.media().getFirst().key()).isEqualTo(
@@ -733,7 +732,7 @@ public class BackupManagerTest {
when(svrbClient.removeData(anyString())).thenReturn(CompletableFuture.completedFuture(null));
// Deleting should swap the backupDir for the user
backupManager.deleteEntireBackup(original).join();
backupManager.deleteEntireBackup(original);
verifyNoInteractions(remoteStorageManager);
verify(svrbClient).removeData(HexFormat.of().formatHex(BackupsDb.hashedBackupId(original.backupId())));
@@ -747,7 +746,7 @@ public class BackupManagerTest {
Collections.emptyList(),
Optional.empty()
)));
backupManager.deleteEntireBackup(after).join();
backupManager.deleteEntireBackup(after);
verify(remoteStorageManager, times(1))
.list(eq(after.backupDir() + "/"), eq(Optional.empty()), anyLong());
@@ -914,7 +913,7 @@ public class BackupManagerTest {
.toList();
for (int i = 0; i < backupUsers.size(); i++) {
testClock.pin(days(i));
backupManager.createMessageBackupUploadDescriptor(backupUsers.get(i)).join();
backupManager.createMessageBackupUploadDescriptor(backupUsers.get(i));
}
// set of backup-id hashes that should be expired (initially t=0)
@@ -949,11 +948,11 @@ public class BackupManagerTest {
// refreshed media timestamp at t=5
testClock.pin(days(5));
backupManager.createMessageBackupUploadDescriptor(backupUser(backupId, BackupCredentialType.MESSAGES, BackupLevel.PAID)).join();
backupManager.createMessageBackupUploadDescriptor(backupUser(backupId, BackupCredentialType.MESSAGES, BackupLevel.PAID));
// refreshed messages timestamp at t=6
testClock.pin(days(6));
backupManager.createMessageBackupUploadDescriptor(backupUser(backupId, BackupCredentialType.MESSAGES, BackupLevel.FREE)).join();
backupManager.createMessageBackupUploadDescriptor(backupUser(backupId, BackupCredentialType.MESSAGES, BackupLevel.FREE));
Function<Instant, List<ExpiredBackup>> getExpired = time -> backupManager
.getExpiredBackups(1, Schedulers.immediate(), time)
@@ -974,7 +973,7 @@ public class BackupManagerTest {
@EnumSource(mode = EnumSource.Mode.INCLUDE, names = {"MEDIA", "ALL"})
public void expireBackup(ExpiredBackup.ExpirationType expirationType) {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
backupManager.createMessageBackupUploadDescriptor(backupUser);
final String expectedPrefixToDelete = switch (expirationType) {
case ALL -> backupUser.backupDir();
@@ -1020,7 +1019,7 @@ public class BackupManagerTest {
@Test
public void deleteBackupPaginated() {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
backupManager.createMessageBackupUploadDescriptor(backupUser);
final ExpiredBackup expiredBackup = expiredBackup(ExpiredBackup.ExpirationType.MEDIA, backupUser);
final String mediaPrefix = expiredBackup.prefixToDelete() + "/";

View File

@@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.controllers;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -111,8 +112,8 @@ public class ArchiveControllerTest {
reset(backupAuthManager);
reset(backupManager);
when(accountsManager.getByAccountIdentifierAsync(AuthHelper.VALID_UUID))
.thenReturn(CompletableFuture.completedFuture(Optional.of(AuthHelper.VALID_ACCOUNT)));
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID))
.thenReturn(Optional.of(AuthHelper.VALID_ACCOUNT));
}
@ParameterizedTest
@@ -164,9 +165,7 @@ public class ArchiveControllerTest {
}
@Test
public void setBackupId() {
when(backupAuthManager.commitBackupId(any(), any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
public void setBackupId() throws RateLimitExceededException {
final Response response = resources.getJerseyTest()
.target("v1/archives/backupid")
.request()
@@ -184,9 +183,7 @@ public class ArchiveControllerTest {
}
@Test
public void setBackupIdPartial() {
when(backupAuthManager.commitBackupId(any(), any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
public void setBackupIdPartial() throws RateLimitExceededException {
final Response response = resources.getJerseyTest()
.target("v1/archives/backupid")
.request()
@@ -210,8 +207,7 @@ public class ArchiveControllerTest {
})
public void backupIdLimits(boolean hasPermits, long waitSeconds) {
when(backupAuthManager.checkBackupIdRotationLimit(any()))
.thenReturn(CompletableFuture.completedFuture(
new BackupAuthManager.BackupIdRotationLimit(hasPermits, Duration.ofSeconds(waitSeconds))));
.thenReturn(new BackupAuthManager.BackupIdRotationLimit(hasPermits, Duration.ofSeconds(waitSeconds)));
final ArchiveController.BackupIdLimitResponse response = resources.getJerseyTest()
.target("v1/archives/backupid/limits")
@@ -233,7 +229,6 @@ public class ArchiveControllerTest {
final ReceiptCredentialResponse rcr = serverOps.issueReceiptCredential(rcrc.getRequest(), 0L, 3L);
final ReceiptCredential receiptCredential = clientOps.receiveReceiptCredential(rcrc, rcr);
final ReceiptCredentialPresentation presentation = clientOps.createReceiptCredentialPresentation(receiptCredential);
when(backupAuthManager.redeemReceipt(any(), any())).thenReturn(CompletableFuture.completedFuture(null));
final Response response = resources.getJerseyTest()
.target("v1/archives/redeem-receipt")
@@ -248,8 +243,6 @@ public class ArchiveControllerTest {
@Test
public void setBadPublicKey() throws VerificationFailedException {
when(backupManager.setPublicKey(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
final Response response = resources.getJerseyTest()
@@ -265,8 +258,6 @@ public class ArchiveControllerTest {
@Test
public void setMissingPublicKey() throws VerificationFailedException {
when(backupManager.setPublicKey(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
final Response response = resources.getJerseyTest()
@@ -280,8 +271,6 @@ public class ArchiveControllerTest {
@Test
public void setPublicKey() throws VerificationFailedException {
when(backupManager.setPublicKey(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
final Response response = resources.getJerseyTest()
@@ -312,20 +301,15 @@ public class ArchiveControllerTest {
public static Stream<Arguments> setBackupIdException() {
return Stream.of(
Arguments.of(new RateLimitExceededException(null), false, 429),
Arguments.of(Status.INVALID_ARGUMENT.withDescription("async").asRuntimeException(), false, 400),
Arguments.of(Status.INVALID_ARGUMENT.withDescription("sync").asRuntimeException(), true, 400)
Arguments.of(new RateLimitExceededException(null), 429),
Arguments.of(Status.INVALID_ARGUMENT.withDescription("test").asRuntimeException(), 400)
);
}
@ParameterizedTest
@MethodSource
public void setBackupIdException(final Exception ex, final boolean sync, final int expectedStatus) {
if (sync) {
when(backupAuthManager.commitBackupId(any(), any(), any(), any())).thenThrow(ex);
} else {
when(backupAuthManager.commitBackupId(any(), any(), any(), any())).thenReturn(CompletableFuture.failedFuture(ex));
}
public void setBackupIdException(final Exception ex, final int expectedStatus) throws RateLimitExceededException {
doThrow(ex).when(backupAuthManager).commitBackupId(any(), any(), any(), any());
final Response response = resources.getJerseyTest()
.target("v1/archives/backupid")
.request()
@@ -349,7 +333,7 @@ public class ArchiveControllerTest {
expectedCredentialsByType.forEach((credentialType, expectedCredentials) ->
when(backupAuthManager.getBackupAuthCredentials(any(), eq(credentialType), eq(expectedRange)))
.thenReturn(CompletableFuture.completedFuture(expectedCredentials)));
.thenReturn(expectedCredentials));
final ArchiveController.BackupAuthCredentialsResponse credentialResponse = resources.getJerseyTest()
.target("v1/archives/auth")
@@ -405,9 +389,9 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
when(backupManager.backupInfo(any())).thenReturn(CompletableFuture.completedFuture(new BackupManager.BackupInfo(
1, "myBackupDir", "myMediaDir", "filename", Optional.empty())));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
when(backupManager.backupInfo(any()))
.thenReturn(new BackupManager.BackupInfo(1, "myBackupDir", "myMediaDir", "filename", Optional.empty()));
final ArchiveController.BackupInfoResponse response = resources.getJerseyTest()
.target("v1/archives")
.request()
@@ -425,7 +409,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
final byte[][] mediaIds = new byte[][]{TestRandomUtil.nextBytes(15), TestRandomUtil.nextBytes(15)};
when(backupManager.copyToBackup(any(), any()))
.thenReturn(Flux.just(
@@ -470,7 +454,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
final byte[][] mediaIds = IntStream.range(0, 4).mapToObj(i -> TestRandomUtil.nextBytes(15)).toArray(byte[][]::new);
when(backupManager.copyToBackup(any(), any()))
@@ -528,7 +512,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
final byte[][] mediaIds = new byte[][]{TestRandomUtil.nextBytes(15), TestRandomUtil.nextBytes(15)};
final Response r = resources.getJerseyTest()
.target("v1/archives/media/batch")
@@ -561,17 +545,17 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
final byte[] mediaId = TestRandomUtil.nextBytes(15);
final Optional<String> expectedCursor = cursorProvided ? Optional.of("myCursor") : Optional.empty();
final Optional<String> returnedCursor = cursorReturned ? Optional.of("newCursor") : Optional.empty();
when(backupManager.list(any(), eq(expectedCursor), eq(17)))
.thenReturn(CompletableFuture.completedFuture(new BackupManager.ListMediaResult(
.thenReturn(new BackupManager.ListMediaResult(
List.of(new BackupManager.StorageDescriptorWithLength(1, mediaId, 100)),
returnedCursor
)));
));
WebTarget target = resources.getJerseyTest()
.target("v1/archives/media/")
@@ -596,7 +580,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(BackupLevel.PAID,
messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
final ArchiveController.DeleteMedia deleteRequest = new ArchiveController.DeleteMedia(
IntStream
@@ -632,10 +616,9 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation =
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
when(backupManager.createMessageBackupUploadDescriptor(any()))
.thenReturn(CompletableFuture.completedFuture(
new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org")));
.thenReturn(new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org"));
final WebTarget builder = resources.getJerseyTest().target("v1/archives/upload/form");
final Response response = uploadLength
@@ -658,14 +641,13 @@ public class ArchiveControllerTest {
}
@Test
public void mediaUploadForm() throws VerificationFailedException {
public void mediaUploadForm() throws VerificationFailedException, RateLimitExceededException {
final BackupAuthCredentialPresentation presentation =
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
when(backupManager.createTemporaryAttachmentUploadDescriptor(any()))
.thenReturn(CompletableFuture.completedFuture(
new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org")));
.thenReturn(new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org"));
final ArchiveController.UploadDescriptorResponse desc = resources.getJerseyTest()
.target("v1/archives/media/upload/form")
.request()
@@ -678,8 +660,7 @@ public class ArchiveControllerTest {
assertThat(desc.signedUploadLocation()).isEqualTo("example.org");
// rate limit
when(backupManager.createTemporaryAttachmentUploadDescriptor(any()))
.thenReturn(CompletableFuture.failedFuture(new RateLimitExceededException(null)));
when(backupManager.createTemporaryAttachmentUploadDescriptor(any())).thenThrow(new RateLimitExceededException(null));
final Response response = resources.getJerseyTest()
.target("v1/archives/media/upload/form")
.request()
@@ -694,7 +675,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation =
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
when(backupManager.generateReadAuth(any(), eq(3))).thenReturn(Map.of("key", "value"));
final ArchiveController.ReadAuthResponse response = resources.getJerseyTest()
.target("v1/archives/auth/read")
@@ -712,7 +693,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation =
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
final ExternalServiceCredentials credentials = new ExternalServiceCredentials("username", "password");
when(backupManager.generateSvrbAuth(any())).thenReturn(credentials);
final ExternalServiceCredentials response = resources.getJerseyTest()
@@ -751,8 +732,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation =
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
when(backupManager.deleteEntireBackup(any())).thenReturn(CompletableFuture.completedFuture(null));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
Response response = resources.getJerseyTest()
.target("v1/archives/")
.request()
@@ -767,7 +747,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupLevel.PAID, messagesBackupKey, aci);
when(backupManager.authenticateBackupUser(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
final Response r = resources.getJerseyTest()
.target("v1/archives/media")
.request()

View File

@@ -29,7 +29,6 @@ import java.util.Base64;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.jupiter.api.BeforeEach;
@@ -45,7 +44,6 @@ import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.mappers.CompletionExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.GrpcStatusRuntimeExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.devicecheck.AppleDeviceCheckManager;
import org.whispersystems.textsecuregcm.storage.devicecheck.ChallengeNotFoundException;
@@ -206,11 +204,6 @@ class DeviceCheckControllerTest {
{"action": "backup", "challenge": "embeddedChallenge"}
""";
when(backupAuthManager.extendBackupVoucher(any(), eq(new Account.BackupVoucher(
REDEMPTION_LEVEL,
clock.instant().plus(REDEMPTION_DURATION)))))
.thenReturn(CompletableFuture.completedFuture(null));
final Response response = resources.getJerseyTest()
.target("v1/devicecheck/assert")
.queryParam("keyId", Base64.getUrlEncoder().encodeToString(keyId))

View File

@@ -90,14 +90,13 @@ class BackupsAnonymousGrpcServiceTest extends
@BeforeEach
void setup() {
when(backupManager.authenticateBackupUser(any(), any(), any()))
when(backupManager.authenticateBackupUserAsync(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(
backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
}
@Test
void setPublicKey() {
when(backupManager.setPublicKey(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
assertThatNoException().isThrownBy(() -> unauthenticatedServiceStub().setPublicKey(SetPublicKeyRequest.newBuilder()
.setPublicKey(ByteString.copyFrom(ECKeyPair.generate().getPublicKey().serialize()))
.setSignedPresentation(signedPresentation(presentation))
@@ -106,7 +105,6 @@ class BackupsAnonymousGrpcServiceTest extends
@Test
void setBadPublicKey() {
when(backupManager.setPublicKey(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
assertThatExceptionOfType(StatusRuntimeException.class).isThrownBy(() ->
unauthenticatedServiceStub().setPublicKey(SetPublicKeyRequest.newBuilder()
.setPublicKey(ByteString.copyFromUtf8("aaaaa")) // Invalid public key
@@ -214,8 +212,8 @@ class BackupsAnonymousGrpcServiceTest extends
@Test
void getBackupInfo() {
when(backupManager.backupInfo(any())).thenReturn(CompletableFuture.completedFuture(new BackupManager.BackupInfo(
1, "myBackupDir", "myMediaDir", "filename", Optional.empty())));
when(backupManager.backupInfo(any()))
.thenReturn(new BackupManager.BackupInfo(1, "myBackupDir", "myMediaDir", "filename", Optional.empty()));
final GetBackupInfoResponse response = unauthenticatedServiceStub().getBackupInfo(GetBackupInfoRequest.newBuilder()
.setSignedPresentation(signedPresentation(presentation))
@@ -240,9 +238,9 @@ class BackupsAnonymousGrpcServiceTest extends
final int limit = 17;
when(backupManager.list(any(), eq(expectedCursor), eq(limit)))
.thenReturn(CompletableFuture.completedFuture(new BackupManager.ListMediaResult(
.thenReturn(new BackupManager.ListMediaResult(
List.of(new BackupManager.StorageDescriptorWithLength(1, mediaId, 100)),
returnedCursor)));
returnedCursor));
final ListMediaRequest.Builder request = ListMediaRequest.newBuilder()
.setSignedPresentation(signedPresentation(presentation))
@@ -280,10 +278,9 @@ class BackupsAnonymousGrpcServiceTest extends
}
@Test
void mediaUploadForm() {
void mediaUploadForm() throws RateLimitExceededException {
when(backupManager.createTemporaryAttachmentUploadDescriptor(any()))
.thenReturn(CompletableFuture.completedFuture(
new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org")));
.thenReturn(new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org"));
final GetUploadFormRequest request = GetUploadFormRequest.newBuilder()
.setMedia(GetUploadFormRequest.MediaUploadType.getDefaultInstance())
.setSignedPresentation(signedPresentation(presentation))
@@ -298,7 +295,7 @@ class BackupsAnonymousGrpcServiceTest extends
// rate limit
Duration duration = Duration.ofSeconds(10);
when(backupManager.createTemporaryAttachmentUploadDescriptor(any()))
.thenReturn(CompletableFuture.failedFuture(new RateLimitExceededException(duration)));
.thenThrow(new RateLimitExceededException(duration));
GrpcTestUtils.assertRateLimitExceeded(duration, () -> unauthenticatedServiceStub().getUploadForm(request));
}
@@ -314,8 +311,7 @@ class BackupsAnonymousGrpcServiceTest extends
@MethodSource
public void messagesUploadForm(Optional<Long> uploadLength, boolean expectSuccess) {
when(backupManager.createMessageBackupUploadDescriptor(any()))
.thenReturn(CompletableFuture.completedFuture(
new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org")));
.thenReturn(new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org"));
final GetUploadFormRequest.MessagesUploadType.Builder builder = GetUploadFormRequest.MessagesUploadType.newBuilder();
uploadLength.ifPresent(builder::setUploadLength);
final GetUploadFormRequest request = GetUploadFormRequest.newBuilder()

View File

@@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.grpc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -90,15 +91,14 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
when(device.isPrimary()).thenReturn(true);
when(accountsManager.getByAccountIdentifierAsync(AUTHENTICATED_ACI))
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
when(accountsManager.getByAccountIdentifier(AUTHENTICATED_ACI))
.thenReturn(Optional.of(account));
when(account.getDevice(AUTHENTICATED_DEVICE_ID)).thenReturn(Optional.of(device));
}
@Test
void setBackupId() {
when(backupAuthManager.commitBackupId(any(), any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(null));
void setBackupId() throws RateLimitExceededException {
authenticatedServiceStub().setBackupId(
SetBackupIdRequest.newBuilder()
.setMediaBackupAuthCredentialRequest(ByteString.copyFrom(mediaAuthCredRequest.serialize()))
@@ -111,10 +111,7 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
@ParameterizedTest
@ValueSource(booleans = {false, true})
void setBackupIdPartial(boolean media) {
when(backupAuthManager.commitBackupId(any(), any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(null));
void setBackupIdPartial(boolean media) throws RateLimitExceededException {
final SetBackupIdRequest.Builder builder = SetBackupIdRequest.newBuilder();
if (media) {
builder.setMediaBackupAuthCredentialRequest(ByteString.copyFrom(mediaAuthCredRequest.serialize()));
@@ -143,23 +140,17 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
public static Stream<Arguments> setBackupIdException() {
return Stream.of(
Arguments.of(new RateLimitExceededException(null), false, Status.RESOURCE_EXHAUSTED),
Arguments.of(Status.INVALID_ARGUMENT.withDescription("async").asRuntimeException(), false,
Status.INVALID_ARGUMENT),
Arguments.of(Status.INVALID_ARGUMENT.withDescription("sync").asRuntimeException(), true,
Arguments.of(new RateLimitExceededException(null), Status.RESOURCE_EXHAUSTED),
Arguments.of(Status.INVALID_ARGUMENT.withDescription("test").asRuntimeException(),
Status.INVALID_ARGUMENT)
);
}
@ParameterizedTest
@MethodSource
void setBackupIdException(final Exception ex, final boolean sync, final Status expected) {
if (sync) {
when(backupAuthManager.commitBackupId(any(), any(), any(), any())).thenThrow(ex);
} else {
when(backupAuthManager.commitBackupId(any(), any(), any(), any()))
.thenReturn(CompletableFuture.failedFuture(ex));
}
void setBackupIdException(final Exception ex, final Status expected)
throws RateLimitExceededException {
doThrow(ex).when(backupAuthManager).commitBackupId(any(), any(), any(), any());
GrpcTestUtils.assertStatusException(
expected, () -> authenticatedServiceStub().setBackupId(SetBackupIdRequest.newBuilder()
@@ -180,8 +171,6 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
final ReceiptCredential receiptCredential = clientOps.receiveReceiptCredential(rcrc, rcr);
final ReceiptCredentialPresentation presentation = clientOps.createReceiptCredentialPresentation(receiptCredential);
when(backupAuthManager.redeemReceipt(any(), any())).thenReturn(CompletableFuture.completedFuture(null));
authenticatedServiceStub().redeemReceipt(RedeemReceiptRequest.newBuilder()
.setPresentation(ByteString.copyFrom(presentation.serialize()))
.build());
@@ -203,7 +192,7 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
expectedCredentialsByType.forEach((credentialType, expectedCredentials) ->
when(backupAuthManager.getBackupAuthCredentials(any(), eq(credentialType), eq(expectedRange)))
.thenReturn(CompletableFuture.completedFuture(expectedCredentials)));
.thenReturn(expectedCredentials));
final GetBackupAuthCredentialsResponse credentialResponse = authenticatedServiceStub().getBackupAuthCredentials(
GetBackupAuthCredentialsRequest.newBuilder()

View File

@@ -15,6 +15,7 @@ import io.grpc.inprocess.InProcessServerBuilder;
import io.grpc.protobuf.StatusProto;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -120,6 +121,20 @@ class ErrorMappingInterceptorTest {
client.echo(EchoRequest.getDefaultInstance()));
}
@Test
public void mapWrappedIOExceptionsSimple() throws Exception {
server = InProcessServerBuilder.forName("ErrorMappingInterceptorTest")
.directExecutor()
.addService(new SimpleEchoServiceErrorImpl(new CompletionException(new UncheckedIOException(new IOException("test")))))
.intercept(new ErrorMappingInterceptor())
.build()
.start();
final EchoServiceGrpc.EchoServiceBlockingStub client = EchoServiceGrpc.newBlockingStub(channel);
GrpcTestUtils.assertStatusException(Status.UNAVAILABLE, "UNAVAILABLE", () ->
client.echo(EchoRequest.getDefaultInstance()));
}
static class ReactorEchoServiceErrorImpl extends ReactorEchoServiceGrpc.EchoServiceImplBase {