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() + "/";