diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 4e7336f5c..23ad4f756 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -205,7 +205,7 @@ import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient; import org.whispersystems.textsecuregcm.s3.PolicySigner; import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.spam.ChallengeConstraintChecker; import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker; import org.whispersystems.textsecuregcm.spam.RegistrationRecoveryChecker; @@ -508,8 +508,8 @@ public class WhisperServerService extends Application dynamicConfigurationManager.getConfiguration().getSvr2StatusCodesToIgnoreForAccountDeletion()); + SecureValueRecoveryClient secureValueRecoveryBClient = new SecureValueRecoveryClient( + svrbCredentialsGenerator, + secureValueRecoveryServiceExecutor, + secureValueRecoveryServiceRetryExecutor, + config.getSvrbConfiguration(), + () -> dynamicConfigurationManager.getConfiguration().getSvrbStatusCodesToIgnoreForAccountDeletion()); SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, storageServiceExecutor, storageServiceRetryExecutor, config.getSecureStorageServiceConfiguration()); DisconnectionRequestManager disconnectionRequestManager = new DisconnectionRequestManager(pubsubClient, disconnectionRequestListenerExecutor); @@ -646,7 +656,7 @@ public class WhisperServerService extends Application svrStatusCodesToIgnoreForAccountDeletion = Collections.emptyList(); + List svr2StatusCodesToIgnoreForAccountDeletion = Collections.emptyList(); + + @JsonProperty + @Valid + List svrbStatusCodesToIgnoreForAccountDeletion = Collections.emptyList(); @JsonProperty @Valid @@ -108,8 +110,12 @@ public class DynamicConfiguration { return metricsConfiguration; } - public List getSvrStatusCodesToIgnoreForAccountDeletion() { - return svrStatusCodesToIgnoreForAccountDeletion; + public List getSvr2StatusCodesToIgnoreForAccountDeletion() { + return svr2StatusCodesToIgnoreForAccountDeletion; + } + + public List getSvrbStatusCodesToIgnoreForAccountDeletion() { + return svrbStatusCodesToIgnoreForAccountDeletion; } public DynamicRestDeprecationConfiguration restDeprecation() { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecovery2Client.java b/service/src/main/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecoveryClient.java similarity index 72% rename from service/src/main/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecovery2Client.java rename to service/src/main/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecoveryClient.java index 26b9eb77d..c33cefed3 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecovery2Client.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecoveryClient.java @@ -15,10 +15,14 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.security.cert.CertificateException; import java.time.Duration; +import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration; @@ -28,21 +32,26 @@ import org.whispersystems.textsecuregcm.util.HttpUtils; /** * A client for sending requests to Signal's secure value recovery v2 service on behalf of authenticated users. */ -public class SecureValueRecovery2Client { +public class SecureValueRecoveryClient { + private static final Logger logger = LoggerFactory.getLogger(SecureValueRecoveryClient.class); private final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator; private final URI deleteUri; + private final Supplier> allowedDeletionErrorStatusCodes; private final FaultTolerantHttpClient httpClient; @VisibleForTesting static final String DELETE_PATH = "/v1/delete"; - public SecureValueRecovery2Client(final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator, + public SecureValueRecoveryClient( + final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator, final Executor executor, final ScheduledExecutorService retryExecutor, - final SecureValueRecoveryConfiguration configuration) + final SecureValueRecoveryConfiguration configuration, + Supplier> allowedDeletionErrorStatusCodes) throws CertificateException { this.secureValueRecoveryCredentialsGenerator = secureValueRecoveryCredentialsGenerator; this.deleteUri = URI.create(configuration.uri()).resolve(DELETE_PATH); + this.allowedDeletionErrorStatusCodes = allowedDeletionErrorStatusCodes; this.httpClient = FaultTolerantHttpClient.newBuilder() .withCircuitBreaker(configuration.circuitBreaker()) .withRetry(configuration.retry()) @@ -57,7 +66,7 @@ public class SecureValueRecovery2Client { .build(); } - public CompletableFuture deleteBackups(final UUID accountUuid) { + public CompletableFuture removeData(final UUID accountUuid) { final ExternalServiceCredentials credentials = secureValueRecoveryCredentialsGenerator.generateForUuid(accountUuid); @@ -72,6 +81,13 @@ public class SecureValueRecovery2Client { return null; } + final List allowedErrors = allowedDeletionErrorStatusCodes.get(); + if (allowedErrors.contains(response.statusCode())) { + logger.warn("Ignoring failure to delete svr entry for account {} with status {}", + accountUuid, response.statusCode()); + return null; + } + logger.warn("Failed to delete svr entry for account {} with status {}", accountUuid, response.statusCode()); throw new SecureValueRecoveryException("Failed to delete backup", String.valueOf(response.statusCode())); }); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java index 85f8bc06d..0cdc35e4e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java @@ -45,7 +45,6 @@ import java.util.Queue; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -84,8 +83,7 @@ import org.whispersystems.textsecuregcm.redis.FaultTolerantPubSubConnection; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryException; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.util.ExceptionUtils; import org.whispersystems.textsecuregcm.util.Pair; import org.whispersystems.textsecuregcm.util.SystemMapper; @@ -126,8 +124,8 @@ public class AccountsManager extends RedisPubSubAdapter implemen private final MessagesManager messagesManager; private final ProfilesManager profilesManager; private final SecureStorageClient secureStorageClient; - private final SecureValueRecovery2Client secureValueRecovery2Client; - + private final SecureValueRecoveryClient secureValueRecovery2Client; + private final SecureValueRecoveryClient secureValueRecoveryBClient; private final DisconnectionRequestManager disconnectionRequestManager; private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager; private final ClientPublicKeysManager clientPublicKeysManager; @@ -209,7 +207,8 @@ public class AccountsManager extends RedisPubSubAdapter implemen final MessagesManager messagesManager, final ProfilesManager profilesManager, final SecureStorageClient secureStorageClient, - final SecureValueRecovery2Client secureValueRecovery2Client, + final SecureValueRecoveryClient secureValueRecovery2Client, + final SecureValueRecoveryClient secureValueRecoveryBClient, final DisconnectionRequestManager disconnectionRequestManager, final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager, final ClientPublicKeysManager clientPublicKeysManager, @@ -228,6 +227,7 @@ public class AccountsManager extends RedisPubSubAdapter implemen this.profilesManager = profilesManager; this.secureStorageClient = secureStorageClient; this.secureValueRecovery2Client = secureValueRecovery2Client; + this.secureValueRecoveryBClient = secureValueRecoveryBClient; this.disconnectionRequestManager = disconnectionRequestManager; this.registrationRecoveryPasswordsManager = requireNonNull(registrationRecoveryPasswordsManager); this.clientPublicKeysManager = clientPublicKeysManager; @@ -1292,20 +1292,10 @@ public class AccountsManager extends RedisPubSubAdapter implemen account.getIdentifier(IdentityType.ACI), device.getId()))) .toList(); - final CompletableFuture svr2DeleteBackupFuture = secureValueRecovery2Client.deleteBackups(account.getUuid()) - .exceptionally(ExceptionUtils.exceptionallyHandler(SecureValueRecoveryException.class, exception -> { - final List svrStatusCodesToIgnore = dynamicConfigurationManager.getConfiguration().getSvrStatusCodesToIgnoreForAccountDeletion(); - if (svrStatusCodesToIgnore.contains(exception.getStatusCode())) { - logger.warn("Ignoring failure to delete svr2 backup for account: " + account.getUuid(), exception); - return null; - } - logger.warn("Failed to delete svr2 backup for account: " + account.getUuid(), exception); - throw new CompletionException(exception); - })); - return CompletableFuture.allOf( secureStorageClient.deleteStoredData(account.getUuid()), - svr2DeleteBackupFuture, + secureValueRecovery2Client.removeData(account.getUuid()), + secureValueRecoveryBClient.removeData(account.getUuid()), keysManager.deleteSingleUsePreKeys(account.getUuid()), keysManager.deleteSingleUsePreKeys(account.getPhoneNumberIdentifier()), messagesManager.clear(account.getUuid()), diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java index 8a6055ff3..d6858cdff 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java @@ -34,6 +34,7 @@ import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfigurati import org.whispersystems.textsecuregcm.controllers.SecureStorageController; import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller; import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager; +import org.whispersystems.textsecuregcm.controllers.SecureValueRecoveryBController; import org.whispersystems.textsecuregcm.experiment.PushNotificationExperimentSamples; import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.metrics.MicrometerAwsSdkMetricPublisher; @@ -45,7 +46,7 @@ import org.whispersystems.textsecuregcm.push.WebSocketConnectionEventManager; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.storage.AccountLockManager; import org.whispersystems.textsecuregcm.storage.Accounts; import org.whispersystems.textsecuregcm.storage.AccountsManager; @@ -173,6 +174,8 @@ record CommandDependencies( configuration.getSecureStorageServiceConfiguration()); ExternalServiceCredentialsGenerator secureValueRecovery2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator( configuration.getSvr2Configuration()); + ExternalServiceCredentialsGenerator secureValueRecoveryBCredentialsGenerator = SecureValueRecoveryBController.credentialsGenerator( + configuration.getSvrbConfiguration()); final ExecutorService awsSdkMetricsExecutor = environment.lifecycle() .virtualExecutorService(MetricRegistry.name(WhisperServerService.class, "awsSdkMetrics-%d")); @@ -238,10 +241,18 @@ record CommandDependencies( .getRedisClusterConfiguration().build("messages", redisClientResourcesBuilder); FaultTolerantRedisClusterClient rateLimitersCluster = configuration.getRateLimitersCluster().build("rate_limiters", redisClientResourcesBuilder); - SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client( - secureValueRecovery2CredentialsGenerator, secureValueRecoveryServiceExecutor, + SecureValueRecoveryClient secureValueRecovery2Client = new SecureValueRecoveryClient( + secureValueRecovery2CredentialsGenerator, + secureValueRecoveryServiceExecutor, secureValueRecoveryServiceRetryExecutor, - configuration.getSvr2Configuration()); + configuration.getSvr2Configuration(), + () -> dynamicConfigurationManager.getConfiguration().getSvr2StatusCodesToIgnoreForAccountDeletion()); + SecureValueRecoveryClient secureValueRecoveryBClient = new SecureValueRecoveryClient( + secureValueRecoveryBCredentialsGenerator, + secureValueRecoveryServiceExecutor, + secureValueRecoveryServiceRetryExecutor, + configuration.getSvrbConfiguration(), + () -> dynamicConfigurationManager.getConfiguration().getSvrbStatusCodesToIgnoreForAccountDeletion()); SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, storageServiceExecutor, storageServiceRetryExecutor, configuration.getSecureStorageServiceConfiguration()); DisconnectionRequestManager disconnectionRequestManager = new DisconnectionRequestManager(pubsubClient, disconnectionRequestListenerExecutor); @@ -264,7 +275,7 @@ record CommandDependencies( new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords); AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster, pubsubClient, accountLockManager, keys, messagesManager, profilesManager, - secureStorageClient, secureValueRecovery2Client, disconnectionRequestManager, + secureStorageClient, secureValueRecovery2Client, secureValueRecoveryBClient, disconnectionRequestManager, registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor, messagePollExecutor, clock, configuration.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager); RateLimiters rateLimiters = RateLimiters.create(dynamicConfigurationManager, rateLimitersCluster); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecovery2ClientTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecoveryClientTest.java similarity index 81% rename from service/src/test/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecovery2ClientTest.java rename to service/src/test/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecoveryClientTest.java index f839a22fe..01a803892 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecovery2ClientTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecoveryClientTest.java @@ -18,9 +18,10 @@ import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import java.security.cert.CertificateException; +import java.util.Arrays; import java.util.List; import java.util.UUID; -import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -28,23 +29,26 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration; +import org.whispersystems.textsecuregcm.util.CompletableFutureTestUtil; -class SecureValueRecovery2ClientTest { +class SecureValueRecoveryClientTest { + private static final List ALLOWED_ERRORS = Arrays.asList(567, 568); private UUID accountUuid; private ExternalServiceCredentialsGenerator credentialsGenerator; private ExecutorService httpExecutor; private ScheduledExecutorService retryExecutor; - private SecureValueRecovery2Client secureValueRecovery2Client; + private SecureValueRecoveryClient secureValueRecoveryClient; @RegisterExtension - private final WireMockExtension wireMock = WireMockExtension.newInstance() + private static final WireMockExtension wireMock = WireMockExtension.newInstance() .options(wireMockConfig().dynamicPort().dynamicHttpsPort()) .build(); @@ -107,8 +111,7 @@ class SecureValueRecovery2ClientTest { """), null, null); - secureValueRecovery2Client = new SecureValueRecovery2Client(credentialsGenerator, httpExecutor, retryExecutor, - config); + secureValueRecoveryClient = new SecureValueRecoveryClient(credentialsGenerator, httpExecutor, retryExecutor, config, () -> ALLOWED_ERRORS); } @AfterEach @@ -119,35 +122,31 @@ class SecureValueRecovery2ClientTest { retryExecutor.awaitTermination(1, TimeUnit.SECONDS); } - @Test - void deleteStoredData() { + @ParameterizedTest + @CsvSource({ + "400, false", + "429, false", + "200, true", + "201, true", + "567, true", + "568, true", + }) + void deleteStatus(int status, boolean shouldSucceed) { final String username = RandomStringUtils.secure().nextAlphabetic(16); final String password = RandomStringUtils.secure().nextAlphanumeric(32); when(credentialsGenerator.generateForUuid(accountUuid)).thenReturn( new ExternalServiceCredentials(username, password)); - wireMock.stubFor(delete(urlEqualTo(SecureValueRecovery2Client.DELETE_PATH)) + wireMock.stubFor(delete(urlEqualTo(SecureValueRecoveryClient.DELETE_PATH)) .withBasicAuth(username, password) - .willReturn(aResponse().withStatus(202))); + .willReturn(aResponse().withStatus(status))); - assertDoesNotThrow(() -> secureValueRecovery2Client.deleteBackups(accountUuid).join()); - } - - @Test - void deleteStoredDataFailure() { - final String username = RandomStringUtils.secure().nextAlphabetic(16); - final String password = RandomStringUtils.secure().nextAlphanumeric(32); - - when(credentialsGenerator.generateForUuid(accountUuid)).thenReturn( - new ExternalServiceCredentials(username, password)); - - wireMock.stubFor(delete(urlEqualTo(SecureValueRecovery2Client.DELETE_PATH)) - .withBasicAuth(username, password) - .willReturn(aResponse().withStatus(400))); - - final CompletionException completionException = assertThrows(CompletionException.class, - () -> secureValueRecovery2Client.deleteBackups(accountUuid).join()); - assertTrue(completionException.getCause() instanceof SecureValueRecoveryException); + final CompletableFuture deleteFuture = secureValueRecoveryClient.removeData(accountUuid); + if (shouldSucceed) { + assertDoesNotThrow(() -> deleteFuture.join()); + } else { + CompletableFutureTestUtil.assertFailsWithCause(SecureValueRecoveryException.class, deleteFuture); + } } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountCreationDeletionIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountCreationDeletionIntegrationTest.java index 7df1be6bf..f4f9c98b8 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountCreationDeletionIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountCreationDeletionIntegrationTest.java @@ -49,7 +49,7 @@ import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.tests.util.KeysHelper; import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; @@ -134,8 +134,8 @@ public class AccountCreationDeletionIntegrationTest { final SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); when(secureStorageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null)); - final SecureValueRecovery2Client svr2Client = mock(SecureValueRecovery2Client.class); - when(svr2Client.deleteBackups(any())).thenReturn(CompletableFuture.completedFuture(null)); + final SecureValueRecoveryClient svr2Client = mock(SecureValueRecoveryClient.class); + when(svr2Client.removeData(any())).thenReturn(CompletableFuture.completedFuture(null)); final PhoneNumberIdentifiers phoneNumberIdentifiers = new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), @@ -167,6 +167,7 @@ public class AccountCreationDeletionIntegrationTest { profilesManager, secureStorageClient, svr2Client, + svr2Client, disconnectionRequestManager, registrationRecoveryPasswordsManager, clientPublicKeysManager, diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java index 944a266ba..3e52aa341 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java @@ -41,7 +41,7 @@ import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables; import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; import org.whispersystems.textsecuregcm.tests.util.KeysHelper; @@ -126,8 +126,8 @@ class AccountsManagerChangeNumberIntegrationTest { final SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); when(secureStorageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null)); - final SecureValueRecovery2Client svr2Client = mock(SecureValueRecovery2Client.class); - when(svr2Client.deleteBackups(any())).thenReturn(CompletableFuture.completedFuture(null)); + final SecureValueRecoveryClient svr2Client = mock(SecureValueRecoveryClient.class); + when(svr2Client.removeData(any())).thenReturn(CompletableFuture.completedFuture(null)); disconnectionRequestManager = mock(DisconnectionRequestManager.class); @@ -157,6 +157,7 @@ class AccountsManagerChangeNumberIntegrationTest { profilesManager, secureStorageClient, svr2Client, + svr2Client, disconnectionRequestManager, registrationRecoveryPasswordsManager, clientPublicKeysManager, diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java index 2d33d394a..f5382e990 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java @@ -54,7 +54,7 @@ import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables; import org.whispersystems.textsecuregcm.tests.util.DevicesHelper; import org.whispersystems.textsecuregcm.tests.util.JsonHelpers; @@ -135,7 +135,8 @@ class AccountsManagerConcurrentModificationIntegrationTest { mock(MessagesManager.class), mock(ProfilesManager.class), mock(SecureStorageClient.class), - mock(SecureValueRecovery2Client.class), + mock(SecureValueRecoveryClient.class), + mock(SecureValueRecoveryClient.class), mock(DisconnectionRequestManager.class), mock(RegistrationRecoveryPasswordsManager.class), mock(ClientPublicKeysManager.class), diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerDeviceTransferIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerDeviceTransferIntegrationTest.java index 86a9b407b..18e5ee89d 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerDeviceTransferIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerDeviceTransferIntegrationTest.java @@ -20,7 +20,7 @@ import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient; import org.whispersystems.textsecuregcm.redis.RedisServerExtension; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.util.TestRandomUtil; import java.nio.charset.StandardCharsets; @@ -68,7 +68,8 @@ public class AccountsManagerDeviceTransferIntegrationTest { mock(MessagesManager.class), mock(ProfilesManager.class), mock(SecureStorageClient.class), - mock(SecureValueRecovery2Client.class), + mock(SecureValueRecoveryClient.class), + mock(SecureValueRecoveryClient.class), mock(DisconnectionRequestManager.class), mock(RegistrationRecoveryPasswordsManager.class), mock(ClientPublicKeysManager.class), diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java index 57867c53e..abdf124d8 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java @@ -54,7 +54,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; @@ -90,8 +89,7 @@ import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryException; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.storage.AccountsManager.UsernameReservation; import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; import org.whispersystems.textsecuregcm.tests.util.DevicesHelper; @@ -133,7 +131,8 @@ class AccountsManagerTest { private RedisAdvancedClusterCommands clusterCommands; private RedisAdvancedClusterAsyncCommands asyncClusterCommands; private AccountsManager accountsManager; - private SecureValueRecovery2Client svr2Client; + private SecureValueRecoveryClient svr2Client; + private SecureValueRecoveryClient svrbClient; private DynamicConfiguration dynamicConfiguration; private static final Answer ACCOUNT_UPDATE_ANSWER = (answer) -> { @@ -194,8 +193,11 @@ class AccountsManagerTest { final SecureStorageClient storageClient = mock(SecureStorageClient.class); when(storageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null)); - svr2Client = mock(SecureValueRecovery2Client.class); - when(svr2Client.deleteBackups(any())).thenReturn(CompletableFuture.completedFuture(null)); + svr2Client = mock(SecureValueRecoveryClient.class); + when(svr2Client.removeData(any())).thenReturn(CompletableFuture.completedFuture(null)); + + svrbClient = mock(SecureValueRecoveryClient.class); + when(svrbClient.removeData(any())).thenReturn(CompletableFuture.completedFuture(null)); final PhoneNumberIdentifiers phoneNumberIdentifiers = mock(PhoneNumberIdentifiers.class); phoneNumberIdentifiersByE164 = new HashMap<>(); @@ -209,7 +211,6 @@ class AccountsManagerTest { mock(DynamicConfigurationManager.class); when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration); - when(dynamicConfiguration.getSvrStatusCodesToIgnoreForAccountDeletion()).thenReturn(Collections.emptyList()); final AccountLockManager accountLockManager = mock(AccountLockManager.class); @@ -256,6 +257,7 @@ class AccountsManagerTest { profilesManager, storageClient, svr2Client, + svrbClient, disconnectionRequestManager, registrationRecoveryPasswordsManager, clientPublicKeysManager, @@ -266,31 +268,6 @@ class AccountsManagerTest { dynamicConfigurationManager); } - @ParameterizedTest - @MethodSource - void testDeleteWithSvr2ErrorStatusCodes(final String statusCode, final boolean expectError) throws InterruptedException { - when(svr2Client.deleteBackups(any())).thenReturn( - CompletableFuture.failedFuture(new SecureValueRecoveryException("Failed to delete backup", statusCode))); - when(dynamicConfiguration.getSvrStatusCodesToIgnoreForAccountDeletion()).thenReturn(List.of("500")); - - final AccountAttributes attributes = new AccountAttributes(false, 1, 2, null, null, true, null); - - final Account createdAccount = createAccount("+18005550123", attributes); - - if (expectError) { - assertThrows(CompletionException.class, () -> accountsManager.delete(createdAccount, AccountsManager.DeletionReason.USER_REQUEST).toCompletableFuture().join()); - } else { - assertDoesNotThrow(() -> accountsManager.delete(createdAccount, AccountsManager.DeletionReason.USER_REQUEST).toCompletableFuture().join()); - } - } - - private static Stream testDeleteWithSvr2ErrorStatusCodes() { - return Stream.of( - Arguments.of("500", false), - Arguments.of("429", true) - ); - } - @Test void testGetByServiceIdentifier() { final UUID aci = UUID.randomUUID(); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerUsernameIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerUsernameIntegrationTest.java index c4c1cd84e..8a39a9455 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerUsernameIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerUsernameIntegrationTest.java @@ -42,7 +42,7 @@ import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables; import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; import org.whispersystems.textsecuregcm.util.AttributeValues; @@ -160,7 +160,8 @@ class AccountsManagerUsernameIntegrationTest { messageManager, profileManager, mock(SecureStorageClient.class), - mock(SecureValueRecovery2Client.class), + mock(SecureValueRecoveryClient.class), + mock(SecureValueRecoveryClient.class), disconnectionRequestManager, mock(RegistrationRecoveryPasswordsManager.class), mock(ClientPublicKeysManager.class), diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AddRemoveDeviceIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AddRemoveDeviceIntegrationTest.java index 69970697e..e13a27981 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AddRemoveDeviceIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AddRemoveDeviceIntegrationTest.java @@ -41,7 +41,7 @@ import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; import org.whispersystems.textsecuregcm.redis.RedisServerExtension; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; -import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client; +import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient; import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; import org.whispersystems.textsecuregcm.tests.util.KeysHelper; import org.whispersystems.textsecuregcm.util.Pair; @@ -134,8 +134,8 @@ public class AddRemoveDeviceIntegrationTest { final SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); when(secureStorageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null)); - final SecureValueRecovery2Client svr2Client = mock(SecureValueRecovery2Client.class); - when(svr2Client.deleteBackups(any())).thenReturn(CompletableFuture.completedFuture(null)); + final SecureValueRecoveryClient svr2Client = mock(SecureValueRecoveryClient.class); + when(svr2Client.removeData(any())).thenReturn(CompletableFuture.completedFuture(null)); final PhoneNumberIdentifiers phoneNumberIdentifiers = new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), @@ -169,6 +169,7 @@ public class AddRemoveDeviceIntegrationTest { profilesManager, secureStorageClient, svr2Client, + svr2Client, mock(DisconnectionRequestManager.class), mock(RegistrationRecoveryPasswordsManager.class), clientPublicKeysManager,