diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 90d9e5d8e..31245e59f 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -129,6 +129,7 @@ import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.s3.PolicySigner; import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator; +import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; import org.whispersystems.textsecuregcm.sms.SmsSender; import org.whispersystems.textsecuregcm.sms.TwilioSmsSender; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; @@ -317,12 +318,22 @@ public class WhisperServerService extends Application deleteStorageServiceDataFuture = secureStorageClient.deleteStoredData(account.getUuid()); + usernamesManager.delete(account.getUuid()); directoryQueue.deleteAccount(account); profilesManager.deleteAll(account.getUuid()); keysDynamoDb.delete(account); messagesManager.clear(account.getUuid()); + + deleteStorageServiceDataFuture.join(); + redisDelete(account); databaseDelete(account); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java index b991bb0cd..1871580b8 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java @@ -25,8 +25,10 @@ import org.jdbi.v3.core.Jdbi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.WhisperServerConfiguration; +import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator; import org.whispersystems.textsecuregcm.metrics.PushLatencyManager; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; +import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Accounts; @@ -103,6 +105,9 @@ public class DeleteUserCommand extends EnvironmentCommand account = accountsManager.get(user); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java index 39306c27a..fa462aa37 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java @@ -21,6 +21,7 @@ import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfig import java.security.Security; import java.security.cert.CertificateException; import java.util.UUID; +import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -30,6 +31,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -116,6 +118,7 @@ public class SecureStorageClientTest { .withBasicAuth(username, password) .willReturn(aResponse().withStatus(400))); - assertThrows(RuntimeException.class, () -> secureStorageClient.deleteStoredData(accountUuid).join()); + final CompletionException completionException = assertThrows(CompletionException.class, () -> secureStorageClient.deleteStoredData(accountUuid).join()); + assertTrue(completionException.getCause() instanceof SecureStorageException); } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java index 8b05559ef..e08cb2780 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java @@ -9,6 +9,7 @@ import io.lettuce.core.RedisException; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import org.junit.Test; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; +import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Accounts; @@ -38,21 +39,22 @@ public class AccountsManagerTest { @Test public void testGetAccountByNumberInCache() { - RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); - FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); - Accounts accounts = mock(Accounts.class); - DirectoryQueue directoryQueue = mock(DirectoryQueue.class); - KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); - MessagesManager messagesManager = mock(MessagesManager.class); - UsernamesManager usernamesManager = mock(UsernamesManager.class); - ProfilesManager profilesManager = mock(ProfilesManager.class); + RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); + FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); + Accounts accounts = mock(Accounts.class); + DirectoryQueue directoryQueue = mock(DirectoryQueue.class); + KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); + MessagesManager messagesManager = mock(MessagesManager.class); + UsernamesManager usernamesManager = mock(UsernamesManager.class); + ProfilesManager profilesManager = mock(ProfilesManager.class); + SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); UUID uuid = UUID.randomUUID(); when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(uuid.toString()); when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}"); - AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient); Optional account = accountsManager.get("+14152222222"); assertTrue(account.isPresent()); @@ -67,20 +69,21 @@ public class AccountsManagerTest { @Test public void testGetAccountByUuidInCache() { - RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); - FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); - Accounts accounts = mock(Accounts.class); - DirectoryQueue directoryQueue = mock(DirectoryQueue.class); - KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); - MessagesManager messagesManager = mock(MessagesManager.class); - UsernamesManager usernamesManager = mock(UsernamesManager.class); - ProfilesManager profilesManager = mock(ProfilesManager.class); + RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); + FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); + Accounts accounts = mock(Accounts.class); + DirectoryQueue directoryQueue = mock(DirectoryQueue.class); + KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); + MessagesManager messagesManager = mock(MessagesManager.class); + UsernamesManager usernamesManager = mock(UsernamesManager.class); + ProfilesManager profilesManager = mock(ProfilesManager.class); + SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); UUID uuid = UUID.randomUUID(); when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}"); - AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient); Optional account = accountsManager.get(uuid); assertTrue(account.isPresent()); @@ -96,21 +99,22 @@ public class AccountsManagerTest { @Test public void testGetAccountByNumberNotInCache() { - RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); - FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); - Accounts accounts = mock(Accounts.class); - DirectoryQueue directoryQueue = mock(DirectoryQueue.class); - KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); - MessagesManager messagesManager = mock(MessagesManager.class); - UsernamesManager usernamesManager = mock(UsernamesManager.class); - ProfilesManager profilesManager = mock(ProfilesManager.class); - UUID uuid = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); + RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); + FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); + Accounts accounts = mock(Accounts.class); + DirectoryQueue directoryQueue = mock(DirectoryQueue.class); + KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); + MessagesManager messagesManager = mock(MessagesManager.class); + UsernamesManager usernamesManager = mock(UsernamesManager.class); + ProfilesManager profilesManager = mock(ProfilesManager.class); + SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); + UUID uuid = UUID.randomUUID(); + Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null); when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient); Optional retrieved = accountsManager.get("+14152222222"); assertTrue(retrieved.isPresent()); @@ -127,21 +131,22 @@ public class AccountsManagerTest { @Test public void testGetAccountByUuidNotInCache() { - RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); - FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); - Accounts accounts = mock(Accounts.class); - DirectoryQueue directoryQueue = mock(DirectoryQueue.class); - KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); - MessagesManager messagesManager = mock(MessagesManager.class); - UsernamesManager usernamesManager = mock(UsernamesManager.class); - ProfilesManager profilesManager = mock(ProfilesManager.class); - UUID uuid = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); + RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); + FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); + Accounts accounts = mock(Accounts.class); + DirectoryQueue directoryQueue = mock(DirectoryQueue.class); + KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); + MessagesManager messagesManager = mock(MessagesManager.class); + UsernamesManager usernamesManager = mock(UsernamesManager.class); + ProfilesManager profilesManager = mock(ProfilesManager.class); + SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); + UUID uuid = UUID.randomUUID(); + Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); when(commands.get(eq("Account3::" + uuid))).thenReturn(null); when(accounts.get(eq(uuid))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient); Optional retrieved = accountsManager.get(uuid); assertTrue(retrieved.isPresent()); @@ -158,21 +163,22 @@ public class AccountsManagerTest { @Test public void testGetAccountByNumberBrokenCache() { - RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); - FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); - Accounts accounts = mock(Accounts.class); - DirectoryQueue directoryQueue = mock(DirectoryQueue.class); - KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); - MessagesManager messagesManager = mock(MessagesManager.class); - UsernamesManager usernamesManager = mock(UsernamesManager.class); - ProfilesManager profilesManager = mock(ProfilesManager.class); - UUID uuid = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); + RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); + FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); + Accounts accounts = mock(Accounts.class); + DirectoryQueue directoryQueue = mock(DirectoryQueue.class); + KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); + MessagesManager messagesManager = mock(MessagesManager.class); + UsernamesManager usernamesManager = mock(UsernamesManager.class); + ProfilesManager profilesManager = mock(ProfilesManager.class); + SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); + UUID uuid = UUID.randomUUID(); + Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!")); when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient); Optional retrieved = accountsManager.get("+14152222222"); assertTrue(retrieved.isPresent()); @@ -189,21 +195,22 @@ public class AccountsManagerTest { @Test public void testGetAccountByUuidBrokenCache() { - RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); - FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); - Accounts accounts = mock(Accounts.class); - DirectoryQueue directoryQueue = mock(DirectoryQueue.class); - KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); - MessagesManager messagesManager = mock(MessagesManager.class); - UsernamesManager usernamesManager = mock(UsernamesManager.class); - ProfilesManager profilesManager = mock(ProfilesManager.class); - UUID uuid = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); + RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); + FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); + Accounts accounts = mock(Accounts.class); + DirectoryQueue directoryQueue = mock(DirectoryQueue.class); + KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); + MessagesManager messagesManager = mock(MessagesManager.class); + UsernamesManager usernamesManager = mock(UsernamesManager.class); + ProfilesManager profilesManager = mock(ProfilesManager.class); + SecureStorageClient secureStorageClient = mock(SecureStorageClient.class); + UUID uuid = UUID.randomUUID(); + Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]); when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!")); when(accounts.get(eq(uuid))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient); Optional retrieved = accountsManager.get(uuid); assertTrue(retrieved.isPresent());