mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-20 22:48:03 +01:00
Convert backup services to use new error model
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
package org.whispersystems.textsecuregcm.backup;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatException;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -21,8 +20,6 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -107,7 +104,7 @@ public class BackupAuthManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void commitBackupId() throws RateLimitExceededException {
|
||||
void commitBackupId() throws RateLimitExceededException, BackupInvalidArgumentException, BackupPermissionException {
|
||||
final BackupAuthManager authManager = create();
|
||||
|
||||
final Account account = mock(Account.class);
|
||||
@@ -158,15 +155,13 @@ public class BackupAuthManagerTest {
|
||||
linkedDevice(),
|
||||
Optional.of(backupAuthTestUtil.getRequest(messagesBackupKey, aci)),
|
||||
Optional.of(backupAuthTestUtil.getRequest(mediaBackupKey, aci)));
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(commit)
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.PERMISSION_DENIED);
|
||||
assertThatExceptionOfType(BackupPermissionException.class)
|
||||
.isThrownBy(commit);
|
||||
}
|
||||
|
||||
@CartesianTest
|
||||
void paidTierCredentialViaConfiguration(@CartesianTest.Enum final BackupCredentialType credentialType)
|
||||
throws VerificationFailedException {
|
||||
throws VerificationFailedException, BackupNotFoundException {
|
||||
final BackupAuthManager authManager = create(BackupLevel.PAID, rateLimiter(aci, false, false));
|
||||
|
||||
final byte[] backupKey = switch (credentialType) {
|
||||
@@ -196,7 +191,7 @@ public class BackupAuthManagerTest {
|
||||
|
||||
@CartesianTest
|
||||
void getBackupAuthCredentials(@CartesianTest.Enum final BackupLevel backupLevel,
|
||||
@CartesianTest.Enum final BackupCredentialType credentialType) {
|
||||
@CartesianTest.Enum final BackupCredentialType credentialType) throws BackupNotFoundException {
|
||||
|
||||
final BackupAuthManager authManager = create();
|
||||
|
||||
@@ -217,16 +212,14 @@ public class BackupAuthManagerTest {
|
||||
|
||||
final Account account = new MockAccountBuilder().build();
|
||||
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() ->
|
||||
authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1))))
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.NOT_FOUND);
|
||||
assertThatExceptionOfType(BackupNotFoundException.class)
|
||||
.isThrownBy(() -> authManager.getBackupAuthCredentials(account, credentialType, range(Duration.ofDays(1))));
|
||||
}
|
||||
|
||||
@CartesianTest
|
||||
void getReceiptCredentials(@CartesianTest.Enum final BackupLevel backupLevel,
|
||||
@CartesianTest.Enum final BackupCredentialType credentialType) throws VerificationFailedException {
|
||||
@CartesianTest.Enum final BackupCredentialType credentialType)
|
||||
throws VerificationFailedException, BackupNotFoundException {
|
||||
final BackupAuthManager authManager = create();
|
||||
|
||||
final byte[] backupKey = switch (credentialType) {
|
||||
@@ -259,7 +252,7 @@ public class BackupAuthManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void expiringBackupPayment() throws VerificationFailedException {
|
||||
void expiringBackupPayment() throws VerificationFailedException, BackupNotFoundException {
|
||||
clock.pin(Instant.ofEpochSecond(1));
|
||||
final Instant day4 = Instant.EPOCH.plus(Duration.ofDays(4));
|
||||
|
||||
@@ -292,7 +285,7 @@ public class BackupAuthManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void expiredBackupPayment() {
|
||||
void expiredBackupPayment() throws BackupNotFoundException {
|
||||
final Instant day1 = Instant.EPOCH.plus(Duration.ofDays(1));
|
||||
final Instant day2 = Instant.EPOCH.plus(Duration.ofDays(2));
|
||||
final Instant day3 = Instant.EPOCH.plus(Duration.ofDays(3));
|
||||
@@ -335,7 +328,8 @@ public class BackupAuthManagerTest {
|
||||
|
||||
|
||||
@Test
|
||||
void redeemReceipt() throws InvalidInputException, VerificationFailedException {
|
||||
void redeemReceipt()
|
||||
throws InvalidInputException, VerificationFailedException, BackupInvalidArgumentException, BackupMissingIdCommitmentException, BackupBadReceiptException {
|
||||
final Instant expirationTime = Instant.EPOCH.plus(Duration.ofDays(1));
|
||||
final BackupAuthManager authManager = create();
|
||||
final Account account = new MockAccountBuilder()
|
||||
@@ -358,15 +352,13 @@ public class BackupAuthManagerTest {
|
||||
clock.pin(Instant.EPOCH.plus(Duration.ofDays(1)));
|
||||
when(redeemedReceiptsManager.put(any(), eq(expirationTime.getEpochSecond()), eq(201L), eq(aci)))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() ->
|
||||
authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)))
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.ABORTED);
|
||||
assertThatExceptionOfType(BackupMissingIdCommitmentException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mergeRedemptions() throws InvalidInputException, VerificationFailedException {
|
||||
void mergeRedemptions()
|
||||
throws InvalidInputException, VerificationFailedException, BackupInvalidArgumentException, BackupMissingIdCommitmentException, BackupBadReceiptException {
|
||||
final Instant newExpirationTime = Instant.EPOCH.plus(Duration.ofDays(1));
|
||||
final Instant existingExpirationTime = Instant.EPOCH.plus(Duration.ofDays(1)).plus(Duration.ofSeconds(1));
|
||||
|
||||
@@ -396,10 +388,8 @@ public class BackupAuthManagerTest {
|
||||
final Instant expirationTime = Instant.EPOCH.plus(Duration.ofDays(1));
|
||||
clock.pin(expirationTime.plus(Duration.ofSeconds(1)));
|
||||
final BackupAuthManager authManager = create();
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), receiptPresentation(3, expirationTime)))
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.INVALID_ARGUMENT);
|
||||
assertThatExceptionOfType(BackupBadReceiptException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), receiptPresentation(3, expirationTime)));
|
||||
verifyNoInteractions(accountsManager);
|
||||
verifyNoInteractions(redeemedReceiptsManager);
|
||||
}
|
||||
@@ -410,11 +400,8 @@ public class BackupAuthManagerTest {
|
||||
final Instant expirationTime = Instant.EPOCH.plus(Duration.ofDays(1));
|
||||
clock.pin(expirationTime.plus(Duration.ofSeconds(1)));
|
||||
final BackupAuthManager authManager = create();
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() ->
|
||||
authManager.redeemReceipt(mock(Account.class), receiptPresentation(level, expirationTime)))
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.INVALID_ARGUMENT);
|
||||
assertThatExceptionOfType(BackupBadReceiptException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), receiptPresentation(level, expirationTime)));
|
||||
verifyNoInteractions(accountsManager);
|
||||
verifyNoInteractions(redeemedReceiptsManager);
|
||||
}
|
||||
@@ -423,10 +410,8 @@ public class BackupAuthManagerTest {
|
||||
void redeemInvalidPresentation() throws InvalidInputException, VerificationFailedException {
|
||||
final BackupAuthManager authManager = create();
|
||||
final ReceiptCredentialPresentation invalid = receiptPresentation(ServerSecretParams.generate(), 3L, Instant.EPOCH);
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), invalid))
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.INVALID_ARGUMENT);
|
||||
assertThatExceptionOfType(BackupBadReceiptException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), invalid));
|
||||
verifyNoInteractions(accountsManager);
|
||||
verifyNoInteractions(redeemedReceiptsManager);
|
||||
}
|
||||
@@ -444,10 +429,8 @@ public class BackupAuthManagerTest {
|
||||
when(redeemedReceiptsManager.put(any(), eq(expirationTime.getEpochSecond()), eq(201L), eq(aci)))
|
||||
.thenReturn(CompletableFuture.completedFuture(false));
|
||||
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)))
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.INVALID_ARGUMENT);
|
||||
assertThatExceptionOfType(BackupBadReceiptException.class)
|
||||
.isThrownBy(() -> authManager.redeemReceipt(account, receiptPresentation(201, expirationTime)));
|
||||
verifyNoInteractions(accountsManager);
|
||||
}
|
||||
|
||||
@@ -544,10 +527,8 @@ public class BackupAuthManagerTest {
|
||||
authManager.commitBackupId(account, primaryDevice(), newMessagesCredential, newMediaCredential);
|
||||
|
||||
if (messageChange == CredentialChangeType.NO_UPDATE && mediaChange == CredentialChangeType.NO_UPDATE) {
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(commit)
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.INVALID_ARGUMENT);
|
||||
assertThatExceptionOfType(BackupInvalidArgumentException.class)
|
||||
.isThrownBy(commit);
|
||||
} else if (expectRateLimit) {
|
||||
assertThatExceptionOfType(RateLimitExceededException.class).isThrownBy(commit);
|
||||
} else {
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.signal.libsignal.zkgroup.GenericServerSecretParams;
|
||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||
import org.signal.libsignal.zkgroup.backups.BackupAuthCredentialPresentation;
|
||||
@@ -76,6 +77,10 @@ public class BackupAuthTestUtil {
|
||||
});
|
||||
final RedemptionRange redemptionRange;
|
||||
redemptionRange = RedemptionRange.inclusive(clock, redemptionStart, redemptionEnd);
|
||||
return issuer.getBackupAuthCredentials(account, credentialType, redemptionRange);
|
||||
try {
|
||||
return issuer.getBackupAuthCredentials(account, credentialType, redemptionRange);
|
||||
} catch (BackupNotFoundException e) {
|
||||
return Assertions.fail("Backup credential request not found even though we set one");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import static org.mockito.Mockito.when;
|
||||
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||
|
||||
import io.dropwizard.util.DataSize;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -184,11 +182,7 @@ public class BackupManagerTest {
|
||||
() -> BackupManager.checkBackupLevel(backupUser, requiredLevel);
|
||||
|
||||
if (expectException) {
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(checkBackupLevel)
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.Code.PERMISSION_DENIED);
|
||||
assertThatExceptionOfType(BackupPermissionException.class).isThrownBy(checkBackupLevel);
|
||||
} else {
|
||||
assertThatNoException().isThrownBy(checkBackupLevel);
|
||||
}
|
||||
@@ -212,11 +206,7 @@ public class BackupManagerTest {
|
||||
() -> BackupManager.checkBackupCredentialType(backupUser, requiredCredentialType);
|
||||
|
||||
if (expectException) {
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(checkCredentialType)
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.Code.UNAUTHENTICATED);
|
||||
assertThatExceptionOfType(BackupWrongCredentialTypeException.class).isThrownBy(checkCredentialType);
|
||||
} else {
|
||||
assertThatNoException().isThrownBy(checkCredentialType);
|
||||
}
|
||||
@@ -224,7 +214,7 @@ public class BackupManagerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource
|
||||
public void createBackup(final BackupLevel backupLevel) {
|
||||
public void createBackup(final BackupLevel backupLevel) throws BackupException {
|
||||
|
||||
final Instant now = Instant.ofEpochSecond(Duration.ofDays(1).getSeconds());
|
||||
testClock.pin(now);
|
||||
@@ -253,9 +243,8 @@ public class BackupManagerTest {
|
||||
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, backupLevel);
|
||||
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.createMessageBackupUploadDescriptor(backupUser))
|
||||
.matches(exception -> exception.getStatus().getCode() == Status.UNAUTHENTICATED.getCode());
|
||||
assertThatExceptionOfType(BackupWrongCredentialTypeException.class)
|
||||
.isThrownBy(() -> backupManager.createMessageBackupUploadDescriptor(backupUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -270,26 +259,20 @@ public class BackupManagerTest {
|
||||
@Test
|
||||
public void createTemporaryMediaAttachmentWrongTier() {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.FREE);
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.createTemporaryAttachmentUploadDescriptor(backupUser))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.Code.PERMISSION_DENIED);
|
||||
assertThatExceptionOfType(BackupPermissionException.class)
|
||||
.isThrownBy(() -> backupManager.createTemporaryAttachmentUploadDescriptor(backupUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryMediaAttachmentWrongCredentialType() {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.createTemporaryAttachmentUploadDescriptor(backupUser))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.Code.UNAUTHENTICATED);
|
||||
assertThatExceptionOfType(BackupWrongCredentialTypeException.class)
|
||||
.isThrownBy(() -> backupManager.createTemporaryAttachmentUploadDescriptor(backupUser));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource
|
||||
public void ttlRefresh(final BackupLevel backupLevel) {
|
||||
public void ttlRefresh(final BackupLevel backupLevel) throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, backupLevel);
|
||||
|
||||
final Instant tstart = Instant.ofEpochSecond(1).plus(Duration.ofDays(1));
|
||||
@@ -311,7 +294,7 @@ public class BackupManagerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource
|
||||
public void createBackupRefreshesTtl(final BackupLevel backupLevel) {
|
||||
public void createBackupRefreshesTtl(final BackupLevel backupLevel) throws BackupException {
|
||||
final Instant tstart = Instant.ofEpochSecond(1).plus(Duration.ofDays(1));
|
||||
final Instant tnext = tstart.plus(Duration.ofDays(1));
|
||||
|
||||
@@ -340,19 +323,16 @@ public class BackupManagerTest {
|
||||
final ECKeyPair keyPair = ECKeyPair.generate();
|
||||
|
||||
// haven't set a public key yet, but should fail before hitting the database anyway
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
assertThatExceptionOfType(BackupFailedZkAuthenticationException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(
|
||||
invalidPresentation,
|
||||
keyPair.getPrivateKey().calculateSignature(invalidPresentation.serialize()),
|
||||
null))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.UNAUTHENTICATED.getCode());
|
||||
null));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void invalidPresentationCorrectSignature() throws VerificationFailedException {
|
||||
public void invalidPresentationCorrectSignature() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.FREE, backupKey, aci);
|
||||
final BackupAuthCredentialPresentation invalidPresentation = backupAuthTestUtil.getPresentation(
|
||||
@@ -365,14 +345,11 @@ public class BackupManagerTest {
|
||||
keyPair.getPrivateKey().calculateSignature(presentation.serialize()),
|
||||
keyPair.getPublicKey());
|
||||
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
assertThatExceptionOfType(BackupFailedZkAuthenticationException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(
|
||||
invalidPresentation,
|
||||
keyPair.getPrivateKey().calculateSignature(invalidPresentation.serialize()),
|
||||
null))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.UNAUTHENTICATED.getCode());
|
||||
null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -384,15 +361,12 @@ public class BackupManagerTest {
|
||||
final byte[] signature = keyPair.getPrivateKey().calculateSignature(presentation.serialize());
|
||||
|
||||
// haven't set a public key yet
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(presentation, signature, null))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.UNAUTHENTICATED.getCode());
|
||||
assertThatExceptionOfType(BackupFailedZkAuthenticationException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(presentation, signature, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mismatchedPublicKey() throws VerificationFailedException {
|
||||
public void mismatchedPublicKey() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.FREE, backupKey, aci);
|
||||
|
||||
@@ -404,18 +378,15 @@ public class BackupManagerTest {
|
||||
backupManager.setPublicKey(presentation, signature1, keyPair1.getPublicKey());
|
||||
|
||||
// shouldn't be able to set a different public key
|
||||
assertThatExceptionOfType(StatusRuntimeException.class).isThrownBy(() ->
|
||||
backupManager.setPublicKey(presentation, signature2, keyPair2.getPublicKey()))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.UNAUTHENTICATED.getCode());
|
||||
assertThatExceptionOfType(BackupFailedZkAuthenticationException.class)
|
||||
.isThrownBy(() -> backupManager.setPublicKey(presentation, signature2, keyPair2.getPublicKey()));
|
||||
|
||||
// should be able to set the same public key again (noop)
|
||||
backupManager.setPublicKey(presentation, signature1, keyPair1.getPublicKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signatureValidation() throws VerificationFailedException {
|
||||
public void signatureValidation() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.FREE, backupKey, aci);
|
||||
|
||||
@@ -427,19 +398,14 @@ public class BackupManagerTest {
|
||||
wrongSignature[1] += 1;
|
||||
|
||||
// shouldn't be able to set a public key with an invalid signature
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.setPublicKey(presentation, wrongSignature, keyPair.getPublicKey()))
|
||||
.extracting(ex -> ex.getStatus().getCode())
|
||||
.isEqualTo(Status.UNAUTHENTICATED.getCode());
|
||||
assertThatExceptionOfType(BackupFailedZkAuthenticationException.class)
|
||||
.isThrownBy(() -> backupManager.setPublicKey(presentation, wrongSignature, keyPair.getPublicKey()));
|
||||
|
||||
backupManager.setPublicKey(presentation, signature, keyPair.getPublicKey());
|
||||
|
||||
// shouldn't be able to authenticate with an invalid signature
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(presentation, wrongSignature, null))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.UNAUTHENTICATED.getCode());
|
||||
assertThatExceptionOfType(BackupFailedZkAuthenticationException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(presentation, wrongSignature, null));
|
||||
|
||||
// correct signature
|
||||
final AuthenticatedBackupUser user = backupManager.authenticateBackupUser(presentation, signature, null);
|
||||
@@ -448,7 +414,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void credentialExpiration() throws VerificationFailedException {
|
||||
public void credentialExpiration() throws VerificationFailedException, BackupException {
|
||||
|
||||
// credential for 1 day after epoch
|
||||
testClock.pin(Instant.ofEpochSecond(1).plus(Duration.ofDays(1)));
|
||||
@@ -468,15 +434,12 @@ public class BackupManagerTest {
|
||||
|
||||
// should be rejected the day after that
|
||||
testClock.pin(Instant.ofEpochSecond(1).plus(Duration.ofDays(3)));
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(oldCredential, signature, null))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.UNAUTHENTICATED.getCode());
|
||||
assertThatExceptionOfType(BackupFailedZkAuthenticationException.class)
|
||||
.isThrownBy(() -> backupManager.authenticateBackupUser(oldCredential, signature, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copySuccess() {
|
||||
public void copySuccess() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
final CopyResult copied = copy(backupUser);
|
||||
|
||||
@@ -493,7 +456,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyUsageCheckpoints() throws InterruptedException {
|
||||
public void copyUsageCheckpoints() throws InterruptedException, BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
backupsDb.setMediaUsage(backupUser, new UsageInfo(0, 0)).join();
|
||||
|
||||
@@ -512,7 +475,7 @@ public class BackupManagerTest {
|
||||
.thenReturn(slow);
|
||||
final ArrayBlockingQueue<CopyResult> copyResults = new ArrayBlockingQueue<>(100);
|
||||
final CompletableFuture<Void> future = backupManager
|
||||
.copyToBackup(backupUser, toCopy)
|
||||
.copyToBackup(backupManager.getCopyQuota(backupUser, toCopy))
|
||||
.doOnNext(copyResults::add).then().toFuture();
|
||||
|
||||
for (int i = 0; i < slowIndex; i++) {
|
||||
@@ -540,7 +503,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyFailure() {
|
||||
public void copyFailure() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
assertThat(copyError(backupUser, new SourceObjectNotFoundException()).outcome())
|
||||
.isEqualTo(CopyResult.Outcome.SOURCE_NOT_FOUND);
|
||||
@@ -552,7 +515,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyPartialSuccess() {
|
||||
public void copyPartialSuccess() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
final List<CopyParameters> toCopy = List.of(
|
||||
new CopyParameters(3, "success", 100, COPY_ENCRYPTION_PARAM, TestRandomUtil.nextBytes(15)),
|
||||
@@ -568,7 +531,7 @@ public class BackupManagerTest {
|
||||
when(remoteStorageManager.copy(eq(3), eq("badlength"), eq(300), any(), any()))
|
||||
.thenReturn(CompletableFuture.failedFuture(new InvalidLengthException("")));
|
||||
|
||||
final List<CopyResult> results = backupManager.copyToBackup(backupUser, toCopy)
|
||||
final List<CopyResult> results = backupManager.copyToBackup(backupManager.getCopyQuota(backupUser, toCopy))
|
||||
.collectList().block();
|
||||
|
||||
assertThat(results).hasSize(3);
|
||||
@@ -587,15 +550,11 @@ public class BackupManagerTest {
|
||||
public void copyWrongCredentialType() {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
|
||||
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> copy(backupUser))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.Code.UNAUTHENTICATED);
|
||||
assertThatExceptionOfType(BackupWrongCredentialTypeException.class).isThrownBy(() -> copy(backupUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void quotaEnforcementNoRecalculation() {
|
||||
public void quotaEnforcementNoRecalculation() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
verifyNoInteractions(remoteStorageManager);
|
||||
|
||||
@@ -612,7 +571,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void quotaEnforcementRecalculation() {
|
||||
public void quotaEnforcementRecalculation() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
final String backupMediaPrefix = "%s/%s/".formatted(backupUser.backupDir(), backupUser.mediaDir());
|
||||
|
||||
@@ -642,7 +601,7 @@ public class BackupManagerTest {
|
||||
public void quotaEnforcement(
|
||||
@CartesianTest.Values(booleans = {true, false}) boolean hasSpaceBeforeRecalc,
|
||||
@CartesianTest.Values(booleans = {true, false}) boolean hasSpaceAfterRecalc,
|
||||
@CartesianTest.Values(booleans = {true, false}) boolean doesReaclc) {
|
||||
@CartesianTest.Values(booleans = {true, false}) boolean doesReaclc) throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
final String backupMediaPrefix = "%s/%s/".formatted(backupUser.backupDir(), backupUser.mediaDir());
|
||||
|
||||
@@ -701,7 +660,7 @@ public class BackupManagerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"", "cursor"})
|
||||
public void list(final String cursorVal) {
|
||||
public void list(final String cursorVal) throws BackupException {
|
||||
final Optional<String> cursor = Optional.of(cursorVal).filter(StringUtils::isNotBlank);
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
|
||||
final String backupMediaPrefix = "%s/%s/".formatted(backupUser.backupDir(), backupUser.mediaDir());
|
||||
@@ -724,7 +683,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteEntireBackup() {
|
||||
public void deleteEntireBackup() throws BackupException {
|
||||
final AuthenticatedBackupUser original = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
|
||||
|
||||
testClock.pin(Instant.ofEpochSecond(10));
|
||||
@@ -762,7 +721,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete() {
|
||||
public void delete() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
final byte[] mediaId = TestRandomUtil.nextBytes(16);
|
||||
final String backupMediaKey = "%s/%s/%s".formatted(
|
||||
@@ -786,10 +745,8 @@ public class BackupManagerTest {
|
||||
public void deleteWrongCredentialType() {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
|
||||
final byte[] mediaId = TestRandomUtil.nextBytes(16);
|
||||
assertThatThrownBy(() ->
|
||||
backupManager.deleteMedia(backupUser, List.of(new BackupManager.StorageDescriptor(5, mediaId))).then().block())
|
||||
.isInstanceOf(StatusRuntimeException.class)
|
||||
.matches(e -> ((StatusRuntimeException) e).getStatus().getCode() == Status.UNAUTHENTICATED.getCode());
|
||||
assertThatExceptionOfType(BackupWrongCredentialTypeException.class)
|
||||
.isThrownBy(() -> backupManager.deleteMedia(backupUser, List.of(new BackupManager.StorageDescriptor(5, mediaId))).then().block());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -797,14 +754,12 @@ public class BackupManagerTest {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
final BackupManager.StorageDescriptor sd = new BackupManager.StorageDescriptor(4, TestRandomUtil.nextBytes(15));
|
||||
when(remoteStorageManager.cdnNumber()).thenReturn(5);
|
||||
assertThatThrownBy(() ->
|
||||
backupManager.deleteMedia(backupUser, List.of(sd)).then().block())
|
||||
.isInstanceOf(StatusRuntimeException.class)
|
||||
.matches(e -> ((StatusRuntimeException) e).getStatus().getCode() == Status.INVALID_ARGUMENT.getCode());
|
||||
assertThatThrownBy(() -> backupManager.deleteMedia(backupUser, List.of(sd)).then().toFuture().join())
|
||||
.hasCauseInstanceOf(BackupInvalidArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteUsageCheckpoints() throws InterruptedException {
|
||||
public void deleteUsageCheckpoints() throws InterruptedException, BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA,
|
||||
BackupLevel.PAID);
|
||||
|
||||
@@ -849,7 +804,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletePartialFailure() {
|
||||
public void deletePartialFailure() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
|
||||
final List<BackupManager.StorageDescriptor> descriptors = new ArrayList<>();
|
||||
@@ -887,7 +842,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alreadyDeleted() {
|
||||
public void alreadyDeleted() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, BackupLevel.PAID);
|
||||
final byte[] mediaId = TestRandomUtil.nextBytes(16);
|
||||
final String backupMediaKey = "%s/%s/%s".formatted(
|
||||
@@ -907,7 +862,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listExpiredBackups() {
|
||||
public void listExpiredBackups() throws BackupException {
|
||||
final List<AuthenticatedBackupUser> backupUsers = IntStream.range(0, 10)
|
||||
.mapToObj(_ -> backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID))
|
||||
.toList();
|
||||
@@ -943,7 +898,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listExpiredBackupsByTier() {
|
||||
public void listExpiredBackupsByTier() throws BackupException {
|
||||
final byte[] backupId = TestRandomUtil.nextBytes(16);
|
||||
|
||||
// refreshed media timestamp at t=5
|
||||
@@ -971,7 +926,7 @@ public class BackupManagerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(mode = EnumSource.Mode.INCLUDE, names = {"MEDIA", "ALL"})
|
||||
public void expireBackup(ExpiredBackup.ExpirationType expirationType) {
|
||||
public void expireBackup(ExpiredBackup.ExpirationType expirationType) throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
|
||||
backupManager.createMessageBackupUploadDescriptor(backupUser);
|
||||
|
||||
@@ -1005,11 +960,9 @@ public class BackupManagerTest {
|
||||
|
||||
if (expirationType == ExpiredBackup.ExpirationType.ALL) {
|
||||
// should have deleted the db row for the backup
|
||||
assertThat(CompletableFutureTestUtil.assertFailsWithCause(
|
||||
StatusRuntimeException.class,
|
||||
backupsDb.describeBackup(backupUser))
|
||||
.getStatus().getCode())
|
||||
.isEqualTo(Status.NOT_FOUND.getCode());
|
||||
CompletableFutureTestUtil.assertFailsWithCause(
|
||||
BackupNotFoundException.class,
|
||||
backupsDb.describeBackup(backupUser));
|
||||
} else {
|
||||
// should have deleted all the media, but left the backup descriptor in place
|
||||
assertThatNoException().isThrownBy(() -> backupsDb.describeBackup(backupUser).join());
|
||||
@@ -1017,7 +970,7 @@ public class BackupManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteBackupPaginated() {
|
||||
public void deleteBackupPaginated() throws BackupException {
|
||||
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, BackupLevel.PAID);
|
||||
backupManager.createMessageBackupUploadDescriptor(backupUser);
|
||||
|
||||
@@ -1055,7 +1008,7 @@ public class BackupManagerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(BackupLevel.class)
|
||||
void svrbAuthValid(BackupLevel backupLevel) {
|
||||
void svrbAuthValid(BackupLevel backupLevel) throws BackupException {
|
||||
testClock.pin(Instant.ofEpochSecond(123));
|
||||
final AuthenticatedBackupUser backupUser =
|
||||
backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MESSAGES, backupLevel);
|
||||
@@ -1072,29 +1025,26 @@ public class BackupManagerTest {
|
||||
// Can't use MEDIA for svrb auth
|
||||
final AuthenticatedBackupUser backupUser =
|
||||
backupUser(TestRandomUtil.nextBytes(16), BackupCredentialType.MEDIA, backupLevel);
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> backupManager.generateSvrbAuth(backupUser))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.Code.UNAUTHENTICATED);
|
||||
assertThatExceptionOfType(BackupWrongCredentialTypeException.class)
|
||||
.isThrownBy(() -> backupManager.generateSvrbAuth(backupUser));
|
||||
}
|
||||
|
||||
private CopyResult copyError(final AuthenticatedBackupUser backupUser, Throwable copyException) {
|
||||
private CopyResult copyError(final AuthenticatedBackupUser backupUser, Throwable copyException) throws BackupException {
|
||||
when(tusCredentialGenerator.generateUpload(any()))
|
||||
.thenReturn(new BackupUploadDescriptor(3, "def", Collections.emptyMap(), ""));
|
||||
when(remoteStorageManager.copy(eq(3), eq(COPY_PARAM.sourceKey()), eq(COPY_PARAM.sourceLength()), any(), any()))
|
||||
.thenReturn(CompletableFuture.failedFuture(copyException));
|
||||
return backupManager.copyToBackup(backupUser, List.of(COPY_PARAM)).single().block();
|
||||
return backupManager.copyToBackup(backupManager.getCopyQuota(backupUser, List.of(COPY_PARAM))).single().block();
|
||||
}
|
||||
|
||||
private CopyResult copy(final AuthenticatedBackupUser backupUser) {
|
||||
private CopyResult copy(final AuthenticatedBackupUser backupUser) throws BackupException {
|
||||
when(tusCredentialGenerator.generateUpload(any()))
|
||||
.thenReturn(new BackupUploadDescriptor(3, "def", Collections.emptyMap(), ""));
|
||||
when(tusCredentialGenerator.generateUpload(any()))
|
||||
.thenReturn(new BackupUploadDescriptor(3, "def", Collections.emptyMap(), ""));
|
||||
when(remoteStorageManager.copy(eq(3), eq(COPY_PARAM.sourceKey()), eq(COPY_PARAM.sourceLength()), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(null));
|
||||
return backupManager.copyToBackup(backupUser, List.of(COPY_PARAM)).single().block();
|
||||
return backupManager.copyToBackup(backupManager.getCopyQuota(backupUser, List.of(COPY_PARAM))).single().block();
|
||||
}
|
||||
|
||||
private static ExpiredBackup expiredBackup(final ExpiredBackup.ExpirationType expirationType,
|
||||
|
||||
@@ -8,8 +8,6 @@ package org.whispersystems.textsecuregcm.backup;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -212,10 +210,9 @@ public class BackupsDbTest {
|
||||
backupsDb.finishExpiration(opt.get()).join();
|
||||
|
||||
// The backup entry should be gone
|
||||
assertThat(CompletableFutureTestUtil.assertFailsWithCause(StatusRuntimeException.class,
|
||||
backupsDb.describeBackup(backupUser(backupId, BackupCredentialType.MEDIA, BackupLevel.PAID)))
|
||||
.getStatus().getCode())
|
||||
.isEqualTo(Status.Code.NOT_FOUND);
|
||||
CompletableFutureTestUtil.assertFailsWithCause(
|
||||
BackupNotFoundException.class,
|
||||
backupsDb.describeBackup(backupUser(backupId, BackupCredentialType.MEDIA, BackupLevel.PAID)));
|
||||
assertThat(expiredBackups.apply(Instant.ofEpochSecond(10))).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import static org.mockito.Mockito.when;
|
||||
import io.dropwizard.auth.AuthValueFactoryProvider;
|
||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||
import io.grpc.Status;
|
||||
import jakarta.ws.rs.client.Entity;
|
||||
import jakarta.ws.rs.client.Invocation;
|
||||
import jakarta.ws.rs.client.WebTarget;
|
||||
@@ -34,7 +33,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import org.glassfish.jersey.server.ServerProperties;
|
||||
@@ -68,10 +66,16 @@ import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.RedemptionRange;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupAuthManager;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupAuthTestUtil;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupFailedZkAuthenticationException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupInvalidArgumentException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupManager;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupNotFoundException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupPermissionException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupUploadDescriptor;
|
||||
import org.whispersystems.textsecuregcm.backup.CopyResult;
|
||||
import org.whispersystems.textsecuregcm.entities.RemoteAttachment;
|
||||
import org.whispersystems.textsecuregcm.mappers.BackupExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.CompletionExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.GrpcStatusRuntimeExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
|
||||
@@ -97,6 +101,7 @@ public class ArchiveControllerTest {
|
||||
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class))
|
||||
.addProvider(new CompletionExceptionMapper())
|
||||
.addResource(new GrpcStatusRuntimeExceptionMapper())
|
||||
.addResource(new BackupExceptionMapper())
|
||||
.addProvider(new RateLimitExceededExceptionMapper())
|
||||
.setMapper(SystemMapper.jsonMapper())
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
@@ -165,7 +170,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setBackupId() throws RateLimitExceededException {
|
||||
public void setBackupId() throws RateLimitExceededException, BackupInvalidArgumentException, BackupPermissionException {
|
||||
final Response response = resources.getJerseyTest()
|
||||
.target("v1/archives/backupid")
|
||||
.request()
|
||||
@@ -183,7 +188,8 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setBackupIdPartial() throws RateLimitExceededException {
|
||||
public void setBackupIdPartial()
|
||||
throws RateLimitExceededException, BackupInvalidArgumentException, BackupPermissionException {
|
||||
final Response response = resources.getJerseyTest()
|
||||
.target("v1/archives/backupid")
|
||||
.request()
|
||||
@@ -302,13 +308,15 @@ public class ArchiveControllerTest {
|
||||
public static Stream<Arguments> setBackupIdException() {
|
||||
return Stream.of(
|
||||
Arguments.of(new RateLimitExceededException(null), 429),
|
||||
Arguments.of(Status.INVALID_ARGUMENT.withDescription("test").asRuntimeException(), 400)
|
||||
Arguments.of(new BackupInvalidArgumentException("test"), 400),
|
||||
Arguments.of(new BackupPermissionException("test"), 403)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void setBackupIdException(final Exception ex, final int expectedStatus) throws RateLimitExceededException {
|
||||
public void setBackupIdException(final Exception ex, final int expectedStatus)
|
||||
throws RateLimitExceededException, BackupInvalidArgumentException, BackupPermissionException {
|
||||
doThrow(ex).when(backupAuthManager).commitBackupId(any(), any(), any(), any());
|
||||
final Response response = resources.getJerseyTest()
|
||||
.target("v1/archives/backupid")
|
||||
@@ -322,7 +330,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCredentials() {
|
||||
public void getCredentials() throws BackupNotFoundException {
|
||||
final Instant start = Instant.now().truncatedTo(ChronoUnit.DAYS);
|
||||
final Instant end = start.plus(Duration.ofDays(1));
|
||||
final RedemptionRange expectedRange = RedemptionRange.inclusive(Clock.systemUTC(), start, end);
|
||||
@@ -331,9 +339,12 @@ public class ArchiveControllerTest {
|
||||
EnumMapUtil.toEnumMap(BackupCredentialType.class, credentialType -> backupAuthTestUtil.getCredentials(
|
||||
BackupLevel.PAID, backupAuthTestUtil.getRequest(messagesBackupKey, aci), credentialType, start, end));
|
||||
|
||||
expectedCredentialsByType.forEach((credentialType, expectedCredentials) ->
|
||||
for (Map.Entry<BackupCredentialType, List<BackupAuthManager.Credential>> entry : expectedCredentialsByType.entrySet()) {
|
||||
final BackupCredentialType credentialType = entry.getKey();
|
||||
final List<BackupAuthManager.Credential> expectedCredentials = entry.getValue();
|
||||
when(backupAuthManager.getBackupAuthCredentials(any(), eq(credentialType), eq(expectedRange)))
|
||||
.thenReturn(expectedCredentials));
|
||||
.thenReturn(expectedCredentials);
|
||||
}
|
||||
|
||||
final ArchiveController.BackupAuthCredentialsResponse credentialResponse = resources.getJerseyTest()
|
||||
.target("v1/archives/auth")
|
||||
@@ -385,7 +396,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBackupInfo() throws VerificationFailedException {
|
||||
public void getBackupInfo() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -405,13 +416,13 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putMediaBatchSuccess() throws VerificationFailedException {
|
||||
public void putMediaBatchSuccess() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
|
||||
final byte[][] mediaIds = new byte[][]{TestRandomUtil.nextBytes(15), TestRandomUtil.nextBytes(15)};
|
||||
when(backupManager.copyToBackup(any(), any()))
|
||||
when(backupManager.copyToBackup(any()))
|
||||
.thenReturn(Flux.just(
|
||||
new CopyResult(CopyResult.Outcome.SUCCESS, mediaIds[0], 1),
|
||||
new CopyResult(CopyResult.Outcome.SUCCESS, mediaIds[1], 1)));
|
||||
@@ -449,7 +460,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putMediaBatchPartialFailure() throws VerificationFailedException {
|
||||
public void putMediaBatchPartialFailure() throws VerificationFailedException, BackupException {
|
||||
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.PAID, messagesBackupKey, aci);
|
||||
@@ -457,7 +468,7 @@ public class ArchiveControllerTest {
|
||||
.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()))
|
||||
when(backupManager.copyToBackup(any()))
|
||||
.thenReturn(Flux.just(
|
||||
new CopyResult(CopyResult.Outcome.SUCCESS, mediaIds[0], 1),
|
||||
new CopyResult(CopyResult.Outcome.SOURCE_NOT_FOUND, mediaIds[1], null),
|
||||
@@ -508,7 +519,7 @@ public class ArchiveControllerTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void copyMediaWithNegativeLength() throws VerificationFailedException {
|
||||
public void copyMediaWithNegativeLength() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -541,7 +552,7 @@ public class ArchiveControllerTest {
|
||||
public void list(
|
||||
@CartesianTest.Values(booleans = {true, false}) final boolean cursorProvided,
|
||||
@CartesianTest.Values(booleans = {true, false}) final boolean cursorReturned)
|
||||
throws VerificationFailedException {
|
||||
throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -576,7 +587,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete() throws VerificationFailedException {
|
||||
public void delete() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(BackupLevel.PAID,
|
||||
messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -612,7 +623,7 @@ public class ArchiveControllerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void messagesUploadForm(Optional<Long> uploadLength, boolean expectSuccess) throws VerificationFailedException {
|
||||
public void messagesUploadForm(Optional<Long> uploadLength, boolean expectSuccess) throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation =
|
||||
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -641,7 +652,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaUploadForm() throws VerificationFailedException, RateLimitExceededException {
|
||||
public void mediaUploadForm() throws VerificationFailedException, BackupException, RateLimitExceededException {
|
||||
final BackupAuthCredentialPresentation presentation =
|
||||
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -671,7 +682,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readAuth() throws VerificationFailedException {
|
||||
public void readAuth() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation =
|
||||
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -689,7 +700,7 @@ public class ArchiveControllerTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void svrbAuth() throws VerificationFailedException {
|
||||
public void svrbAuth() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation =
|
||||
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -706,7 +717,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readAuthInvalidParam() throws VerificationFailedException {
|
||||
public void readAuthInvalidParam() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation =
|
||||
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
|
||||
Response response = resources.getJerseyTest()
|
||||
@@ -728,7 +739,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteEntireBackup() throws VerificationFailedException {
|
||||
public void deleteEntireBackup() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation =
|
||||
backupAuthTestUtil.getPresentation(BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
@@ -743,7 +754,7 @@ public class ArchiveControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidSourceAttachmentKey() throws VerificationFailedException {
|
||||
public void invalidSourceAttachmentKey() throws VerificationFailedException, BackupException {
|
||||
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
|
||||
BackupLevel.PAID, messagesBackupKey, aci);
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
|
||||
@@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.grpc;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -19,19 +18,16 @@ import io.grpc.StatusRuntimeException;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import jakarta.ws.rs.client.WebTarget;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@@ -62,10 +58,11 @@ import org.signal.libsignal.zkgroup.backups.BackupCredentialType;
|
||||
import org.signal.libsignal.zkgroup.backups.BackupLevel;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedBackupUser;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupAuthTestUtil;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupFailedZkAuthenticationException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupManager;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupUploadDescriptor;
|
||||
import org.whispersystems.textsecuregcm.backup.CopyResult;
|
||||
import org.whispersystems.textsecuregcm.controllers.ArchiveController;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.metrics.BackupMetrics;
|
||||
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
|
||||
@@ -90,9 +87,12 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
when(backupManager.authenticateBackupUserAsync(any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(
|
||||
backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID)));
|
||||
try {
|
||||
when(backupManager.authenticateBackupUser(any(), any(), any()))
|
||||
.thenReturn(backupUser(presentation.getBackupId(), BackupCredentialType.MESSAGES, BackupLevel.PAID));
|
||||
} catch (BackupFailedZkAuthenticationException e) {
|
||||
Assertions.fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -129,7 +129,7 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
@Test
|
||||
void putMediaBatchSuccess() {
|
||||
final byte[][] mediaIds = {TestRandomUtil.nextBytes(15), TestRandomUtil.nextBytes(15)};
|
||||
when(backupManager.copyToBackup(any(), any()))
|
||||
when(backupManager.copyToBackup(any()))
|
||||
.thenReturn(Flux.just(
|
||||
new CopyResult(CopyResult.Outcome.SUCCESS, mediaIds[0], 1),
|
||||
new CopyResult(CopyResult.Outcome.SUCCESS, mediaIds[1], 1)));
|
||||
@@ -174,7 +174,7 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
CopyResult.Outcome.SOURCE_WRONG_LENGTH,
|
||||
CopyResult.Outcome.OUT_OF_QUOTA
|
||||
};
|
||||
when(backupManager.copyToBackup(any(), any()))
|
||||
when(backupManager.copyToBackup(any()))
|
||||
.thenReturn(Flux.fromStream(IntStream.range(0, 4)
|
||||
.mapToObj(i -> new CopyResult(
|
||||
outcomes[i],
|
||||
@@ -211,17 +211,17 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBackupInfo() {
|
||||
void getBackupInfo() throws BackupException {
|
||||
when(backupManager.backupInfo(any()))
|
||||
.thenReturn(new BackupManager.BackupInfo(1, "myBackupDir", "myMediaDir", "filename", Optional.empty()));
|
||||
|
||||
final GetBackupInfoResponse response = unauthenticatedServiceStub().getBackupInfo(GetBackupInfoRequest.newBuilder()
|
||||
.setSignedPresentation(signedPresentation(presentation))
|
||||
.build());
|
||||
assertThat(response.getBackupDir()).isEqualTo("myBackupDir");
|
||||
assertThat(response.getBackupName()).isEqualTo("filename");
|
||||
assertThat(response.getCdn()).isEqualTo(1);
|
||||
assertThat(response.getUsedSpace()).isEqualTo(0L);
|
||||
assertThat(response.getBackupInfo().getBackupDir()).isEqualTo("myBackupDir");
|
||||
assertThat(response.getBackupInfo().getBackupName()).isEqualTo("filename");
|
||||
assertThat(response.getBackupInfo().getCdn()).isEqualTo(1);
|
||||
assertThat(response.getBackupInfo().getUsedSpace()).isEqualTo(0L);
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
void list(
|
||||
@CartesianTest.Values(booleans = {true, false}) final boolean cursorProvided,
|
||||
@CartesianTest.Values(booleans = {true, false}) final boolean cursorReturned)
|
||||
throws VerificationFailedException {
|
||||
throws VerificationFailedException, BackupException {
|
||||
|
||||
final byte[] mediaId = TestRandomUtil.nextBytes(15);
|
||||
final Optional<String> expectedCursor = cursorProvided ? Optional.of("myCursor") : Optional.empty();
|
||||
@@ -250,15 +250,16 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
}
|
||||
|
||||
final ListMediaResponse response = unauthenticatedServiceStub().listMedia(request.build());
|
||||
assertThat(response.getPageCount()).isEqualTo(1);
|
||||
assertThat(response.getPage(0).getLength()).isEqualTo(100);
|
||||
assertThat(response.getPage(0).getMediaId().toByteArray()).isEqualTo(mediaId);
|
||||
assertThat(response.hasCursor() ? response.getCursor() : null).isEqualTo(returnedCursor.orElse(null));
|
||||
assertThat(response.getListResult().getPageCount()).isEqualTo(1);
|
||||
assertThat(response.getListResult().getPage(0).getLength()).isEqualTo(100);
|
||||
assertThat(response.getListResult().getPage(0).getMediaId().toByteArray()).isEqualTo(mediaId);
|
||||
assertThat(response.getListResult().hasCursor() ? response.getListResult().getCursor() : null)
|
||||
.isEqualTo(returnedCursor.orElse(null));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void delete() {
|
||||
void delete() throws BackupException {
|
||||
final DeleteMediaRequest request = DeleteMediaRequest.newBuilder()
|
||||
.setSignedPresentation(signedPresentation(presentation))
|
||||
.addAllItems(IntStream.range(0, 100).mapToObj(i ->
|
||||
@@ -278,7 +279,7 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
}
|
||||
|
||||
@Test
|
||||
void mediaUploadForm() throws RateLimitExceededException {
|
||||
void mediaUploadForm() throws RateLimitExceededException, BackupException {
|
||||
when(backupManager.createTemporaryAttachmentUploadDescriptor(any()))
|
||||
.thenReturn(new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org"));
|
||||
final GetUploadFormRequest request = GetUploadFormRequest.newBuilder()
|
||||
@@ -287,10 +288,10 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
.build();
|
||||
|
||||
final GetUploadFormResponse uploadForm = unauthenticatedServiceStub().getUploadForm(request);
|
||||
assertThat(uploadForm.getCdn()).isEqualTo(3);
|
||||
assertThat(uploadForm.getKey()).isEqualTo("abc");
|
||||
assertThat(uploadForm.getHeadersMap()).containsExactlyEntriesOf(Map.of("k", "v"));
|
||||
assertThat(uploadForm.getSignedUploadLocation()).isEqualTo("example.org");
|
||||
assertThat(uploadForm.getUploadForm().getCdn()).isEqualTo(3);
|
||||
assertThat(uploadForm.getUploadForm().getKey()).isEqualTo("abc");
|
||||
assertThat(uploadForm.getUploadForm().getHeadersMap()).containsExactlyEntriesOf(Map.of("k", "v"));
|
||||
assertThat(uploadForm.getUploadForm().getSignedUploadLocation()).isEqualTo("example.org");
|
||||
|
||||
// rate limit
|
||||
Duration duration = Duration.ofSeconds(10);
|
||||
@@ -309,7 +310,7 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void messagesUploadForm(Optional<Long> uploadLength, boolean expectSuccess) {
|
||||
public void messagesUploadForm(Optional<Long> uploadLength, boolean allowedSize) throws BackupException {
|
||||
when(backupManager.createMessageBackupUploadDescriptor(any()))
|
||||
.thenReturn(new BackupUploadDescriptor(3, "abc", Map.of("k", "v"), "example.org"));
|
||||
final GetUploadFormRequest.MessagesUploadType.Builder builder = GetUploadFormRequest.MessagesUploadType.newBuilder();
|
||||
@@ -318,24 +319,20 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
.setMessages(builder.build())
|
||||
.setSignedPresentation(signedPresentation(presentation))
|
||||
.build();
|
||||
if (expectSuccess) {
|
||||
final GetUploadFormResponse uploadForm = unauthenticatedServiceStub().getUploadForm(request);
|
||||
assertThat(uploadForm.getCdn()).isEqualTo(3);
|
||||
assertThat(uploadForm.getKey()).isEqualTo("abc");
|
||||
assertThat(uploadForm.getHeadersMap()).containsExactlyEntriesOf(Map.of("k", "v"));
|
||||
assertThat(uploadForm.getSignedUploadLocation()).isEqualTo("example.org");
|
||||
final GetUploadFormResponse response = unauthenticatedServiceStub().getUploadForm(request);
|
||||
if (allowedSize) {
|
||||
assertThat(response.getUploadForm().getCdn()).isEqualTo(3);
|
||||
assertThat(response.getUploadForm().getKey()).isEqualTo("abc");
|
||||
assertThat(response.getUploadForm().getHeadersMap()).containsExactlyEntriesOf(Map.of("k", "v"));
|
||||
assertThat(response.getUploadForm().getSignedUploadLocation()).isEqualTo("example.org");
|
||||
} else {
|
||||
assertThatExceptionOfType(StatusRuntimeException.class)
|
||||
.isThrownBy(() -> unauthenticatedServiceStub().getUploadForm(request))
|
||||
.extracting(StatusRuntimeException::getStatus)
|
||||
.extracting(Status::getCode)
|
||||
.isEqualTo(Status.FAILED_PRECONDITION.getCode());
|
||||
assertThat(response.hasExceedsMaxUploadLength()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void readAuth() {
|
||||
void readAuth() throws BackupException {
|
||||
when(backupManager.generateReadAuth(any(), eq(3))).thenReturn(Map.of("key", "value"));
|
||||
|
||||
final GetCdnCredentialsResponse response = unauthenticatedServiceStub().getCdnCredentials(
|
||||
@@ -343,7 +340,7 @@ class BackupsAnonymousGrpcServiceTest extends
|
||||
.setCdn(3)
|
||||
.setSignedPresentation(signedPresentation(presentation))
|
||||
.build());
|
||||
assertThat(response.getHeadersMap()).containsExactlyEntriesOf(Map.of("key", "value"));
|
||||
assertThat(response.getCdnCredentials().getHeadersMap()).containsExactlyEntriesOf(Map.of("key", "value"));
|
||||
}
|
||||
|
||||
private static AuthenticatedBackupUser backupUser(final byte[] backupId, final BackupCredentialType credentialType,
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.signal.chat.backup.BackupsGrpc;
|
||||
import org.signal.chat.backup.GetBackupAuthCredentialsRequest;
|
||||
import org.signal.chat.backup.GetBackupAuthCredentialsResponse;
|
||||
import org.signal.chat.backup.RedeemReceiptRequest;
|
||||
import org.signal.chat.backup.RedeemReceiptResponse;
|
||||
import org.signal.chat.backup.SetBackupIdRequest;
|
||||
import org.signal.chat.common.ZkCredential;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
@@ -54,6 +55,12 @@ import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.RedemptionRange;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupAuthManager;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupAuthTestUtil;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupBadReceiptException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupInvalidArgumentException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupMissingIdCommitmentException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupNotFoundException;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupPermissionException;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.metrics.BackupMetrics;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
@@ -61,6 +68,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.EnumMapUtil;
|
||||
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, BackupsGrpc.BackupsBlockingStub> {
|
||||
|
||||
@@ -98,7 +106,7 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
|
||||
|
||||
|
||||
@Test
|
||||
void setBackupId() throws RateLimitExceededException {
|
||||
void setBackupId() throws RateLimitExceededException, BackupInvalidArgumentException, BackupPermissionException {
|
||||
authenticatedServiceStub().setBackupId(
|
||||
SetBackupIdRequest.newBuilder()
|
||||
.setMediaBackupAuthCredentialRequest(ByteString.copyFrom(mediaAuthCredRequest.serialize()))
|
||||
@@ -111,7 +119,8 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void setBackupIdPartial(boolean media) throws RateLimitExceededException {
|
||||
void setBackupIdPartial(boolean media)
|
||||
throws RateLimitExceededException, BackupInvalidArgumentException, BackupPermissionException {
|
||||
final SetBackupIdRequest.Builder builder = SetBackupIdRequest.newBuilder();
|
||||
if (media) {
|
||||
builder.setMediaBackupAuthCredentialRequest(ByteString.copyFrom(mediaAuthCredRequest.serialize()));
|
||||
@@ -141,15 +150,14 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
|
||||
public static Stream<Arguments> setBackupIdException() {
|
||||
return Stream.of(
|
||||
Arguments.of(new RateLimitExceededException(null), Status.RESOURCE_EXHAUSTED),
|
||||
Arguments.of(Status.INVALID_ARGUMENT.withDescription("test").asRuntimeException(),
|
||||
Status.INVALID_ARGUMENT)
|
||||
);
|
||||
Arguments.of(new BackupPermissionException("test"), Status.INVALID_ARGUMENT),
|
||||
Arguments.of(new BackupInvalidArgumentException("test"), Status.INVALID_ARGUMENT));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void setBackupIdException(final Exception ex, final Status expected)
|
||||
throws RateLimitExceededException {
|
||||
throws RateLimitExceededException, BackupInvalidArgumentException, BackupPermissionException {
|
||||
doThrow(ex).when(backupAuthManager).commitBackupId(any(), any(), any(), any());
|
||||
|
||||
GrpcTestUtils.assertStatusException(
|
||||
@@ -160,8 +168,18 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void redeemReceipt() throws InvalidInputException, VerificationFailedException {
|
||||
public static Stream<Arguments> redeemReceipt() {
|
||||
return Stream.of(
|
||||
Arguments.of(null, RedeemReceiptResponse.OutcomeCase.SUCCESS),
|
||||
Arguments.of(new BackupBadReceiptException("test"), RedeemReceiptResponse.OutcomeCase.INVALID_RECEIPT),
|
||||
Arguments.of(new BackupMissingIdCommitmentException(), RedeemReceiptResponse.OutcomeCase.ACCOUNT_MISSING_COMMITMENT));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void redeemReceipt(@Nullable final BackupException exception, final RedeemReceiptResponse.OutcomeCase expectedOutcome)
|
||||
throws InvalidInputException, VerificationFailedException, BackupInvalidArgumentException, BackupMissingIdCommitmentException, BackupBadReceiptException {
|
||||
|
||||
final ServerSecretParams params = ServerSecretParams.generate();
|
||||
final ServerZkReceiptOperations serverOps = new ServerZkReceiptOperations(params);
|
||||
final ClientZkReceiptOperations clientOps = new ClientZkReceiptOperations(params.getPublicParams());
|
||||
@@ -171,16 +189,22 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
|
||||
final ReceiptCredential receiptCredential = clientOps.receiveReceiptCredential(rcrc, rcr);
|
||||
final ReceiptCredentialPresentation presentation = clientOps.createReceiptCredentialPresentation(receiptCredential);
|
||||
|
||||
authenticatedServiceStub().redeemReceipt(RedeemReceiptRequest.newBuilder()
|
||||
.setPresentation(ByteString.copyFrom(presentation.serialize()))
|
||||
.build());
|
||||
if (exception != null) {
|
||||
doThrow(exception).when(backupAuthManager).redeemReceipt(any(), any());
|
||||
}
|
||||
|
||||
final RedeemReceiptResponse redeemReceiptResponse = authenticatedServiceStub().redeemReceipt(
|
||||
RedeemReceiptRequest.newBuilder()
|
||||
.setPresentation(ByteString.copyFrom(presentation.serialize()))
|
||||
.build());
|
||||
assertThat(redeemReceiptResponse.getOutcomeCase()).isEqualTo(expectedOutcome);
|
||||
|
||||
verify(backupAuthManager).redeemReceipt(account, presentation);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void getCredentials() {
|
||||
void getCredentials() throws BackupNotFoundException {
|
||||
final Instant start = Instant.now().truncatedTo(ChronoUnit.DAYS);
|
||||
final Instant end = start.plus(Duration.ofDays(1));
|
||||
final RedemptionRange expectedRange = RedemptionRange.inclusive(Clock.systemUTC(), start, end);
|
||||
@@ -190,9 +214,12 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
|
||||
BackupLevel.PAID, backupAuthTestUtil.getRequest(messagesBackupKey, AUTHENTICATED_ACI), credentialType,
|
||||
start, end));
|
||||
|
||||
expectedCredentialsByType.forEach((credentialType, expectedCredentials) ->
|
||||
when(backupAuthManager.getBackupAuthCredentials(any(), eq(credentialType), eq(expectedRange)))
|
||||
.thenReturn(expectedCredentials));
|
||||
for (Map.Entry<BackupCredentialType, List<BackupAuthManager.Credential>> entry : expectedCredentialsByType.entrySet()) {
|
||||
final BackupCredentialType credentialType = entry.getKey();
|
||||
final List<BackupAuthManager.Credential> expectedCredentials = entry.getValue();
|
||||
when(backupAuthManager.getBackupAuthCredentials(any(), eq(credentialType), eq(expectedRange)))
|
||||
.thenReturn(expectedCredentials);
|
||||
}
|
||||
|
||||
final GetBackupAuthCredentialsResponse credentialResponse = authenticatedServiceStub().getBackupAuthCredentials(
|
||||
GetBackupAuthCredentialsRequest.newBuilder()
|
||||
@@ -202,8 +229,8 @@ class BackupsGrpcServiceTest extends SimpleBaseGrpcTest<BackupsGrpcService, Back
|
||||
expectedCredentialsByType.forEach((credentialType, expectedCredentials) -> {
|
||||
|
||||
final Map<Long, ZkCredential> creds = switch (credentialType) {
|
||||
case MESSAGES -> credentialResponse.getMessageCredentialsMap();
|
||||
case MEDIA -> credentialResponse.getMediaCredentialsMap();
|
||||
case MESSAGES -> credentialResponse.getCredentials().getMessageCredentialsMap();
|
||||
case MEDIA -> credentialResponse.getCredentials().getMediaCredentialsMap();
|
||||
};
|
||||
assertThat(creds).hasSize(expectedCredentials.size()).containsKey(start.getEpochSecond());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user