Absorb `DeletedAccounts` into `Accounts`
This commit is contained in:
		
							parent
							
								
									9945367fa1
								
							
						
					
					
						commit
						1b9bf01ab1
					
				|  | @ -176,7 +176,6 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager; | |||
| import org.whispersystems.textsecuregcm.storage.ChangeNumberManager; | ||||
| import org.whispersystems.textsecuregcm.storage.ClientReleaseManager; | ||||
| import org.whispersystems.textsecuregcm.storage.ClientReleases; | ||||
| import org.whispersystems.textsecuregcm.storage.DeletedAccounts; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; | ||||
| import org.whispersystems.textsecuregcm.storage.IssuedReceiptsManager; | ||||
| import org.whispersystems.textsecuregcm.storage.KeysManager; | ||||
|  | @ -299,9 +298,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration | |||
|     DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(config.getDynamoDbClientConfiguration(), | ||||
|         AWSSDK_CREDENTIALS_PROVIDER); | ||||
| 
 | ||||
|     DeletedAccounts deletedAccounts = new DeletedAccounts(dynamoDbClient, | ||||
|         config.getDynamoDbTables().getDeletedAccounts().getTableName()); | ||||
| 
 | ||||
|     DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = | ||||
|         new DynamicConfigurationManager<>(config.getAppConfig().getApplication(), | ||||
|             config.getAppConfig().getEnvironment(), | ||||
|  | @ -325,6 +321,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration | |||
|         config.getDynamoDbTables().getAccounts().getPhoneNumberTableName(), | ||||
|         config.getDynamoDbTables().getAccounts().getPhoneNumberIdentifierTableName(), | ||||
|         config.getDynamoDbTables().getAccounts().getUsernamesTableName(), | ||||
|         config.getDynamoDbTables().getDeletedAccounts().getTableName(), | ||||
|         config.getDynamoDbTables().getAccounts().getScanPageSize()); | ||||
|     ClientReleases clientReleases = new ClientReleases(dynamoDbAsyncClient, | ||||
|         config.getDynamoDbTables().getClientReleases().getTableName()); | ||||
|  | @ -514,7 +511,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration | |||
|     AccountLockManager accountLockManager = new AccountLockManager(dynamoDbClient, | ||||
|         config.getDynamoDbTables().getDeletedAccountsLock().getTableName()); | ||||
|     AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster, | ||||
|         accountLockManager, deletedAccounts, keys, messagesManager, profilesManager, | ||||
|         accountLockManager, keys, messagesManager, profilesManager, | ||||
|         secureStorageClient, secureBackupClient, secureValueRecovery2Client, | ||||
|         clientPresenceManager, | ||||
|         experimentEnrollmentManager, registrationRecoveryPasswordsManager, clock); | ||||
|  | @ -764,7 +761,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration | |||
|         new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(), | ||||
|             ReceiptCredentialPresentation::new), | ||||
|         new MessageController(rateLimiters, messageByteLimitCardinalityEstimator, messageSender, receiptSender, | ||||
|             accountsManager, deletedAccounts, messagesManager, pushNotificationManager, reportMessageManager, | ||||
|             accountsManager, messagesManager, pushNotificationManager, reportMessageManager, | ||||
|             multiRecipientMessageExecutor, messageDeliveryScheduler, reportSpamTokenProvider, clientReleaseManager, | ||||
|             dynamicConfigurationManager), | ||||
|         new PaymentsController(currencyManager, paymentsCredentialsGenerator), | ||||
|  |  | |||
|  | @ -99,7 +99,6 @@ import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider; | |||
| import org.whispersystems.textsecuregcm.storage.Account; | ||||
| import org.whispersystems.textsecuregcm.storage.AccountsManager; | ||||
| import org.whispersystems.textsecuregcm.storage.ClientReleaseManager; | ||||
| import org.whispersystems.textsecuregcm.storage.DeletedAccounts; | ||||
| import org.whispersystems.textsecuregcm.storage.Device; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesManager; | ||||
|  | @ -123,7 +122,6 @@ public class MessageController { | |||
|   private final MessageSender messageSender; | ||||
|   private final ReceiptSender receiptSender; | ||||
|   private final AccountsManager accountsManager; | ||||
|   private final DeletedAccounts deletedAccounts; | ||||
|   private final MessagesManager messagesManager; | ||||
|   private final PushNotificationManager pushNotificationManager; | ||||
|   private final ReportMessageManager reportMessageManager; | ||||
|  | @ -159,7 +157,6 @@ public class MessageController { | |||
|       MessageSender messageSender, | ||||
|       ReceiptSender receiptSender, | ||||
|       AccountsManager accountsManager, | ||||
|       DeletedAccounts deletedAccounts, | ||||
|       MessagesManager messagesManager, | ||||
|       PushNotificationManager pushNotificationManager, | ||||
|       ReportMessageManager reportMessageManager, | ||||
|  | @ -173,7 +170,6 @@ public class MessageController { | |||
|     this.messageSender = messageSender; | ||||
|     this.receiptSender = receiptSender; | ||||
|     this.accountsManager = accountsManager; | ||||
|     this.deletedAccounts = deletedAccounts; | ||||
|     this.messagesManager = messagesManager; | ||||
|     this.pushNotificationManager = pushNotificationManager; | ||||
|     this.reportMessageManager = reportMessageManager; | ||||
|  | @ -628,7 +624,7 @@ public class MessageController { | |||
|         sourceAci = maybeAccount.map(Account::getUuid); | ||||
|         sourcePni = maybeAccount.map(Account::getPhoneNumberIdentifier); | ||||
|       } else { | ||||
|         sourceAci = deletedAccounts.findUuid(source); | ||||
|         sourceAci = accountsManager.findRecentlyDeletedAccountIdentifier(source); | ||||
|         sourcePni = Optional.ofNullable(accountsManager.getPhoneNumberIdentifier(source)); | ||||
|       } | ||||
|     } else { | ||||
|  | @ -638,7 +634,7 @@ public class MessageController { | |||
| 
 | ||||
|       if (sourceAccount.isEmpty()) { | ||||
|         logger.warn("Could not find source: {}", sourceAci.get()); | ||||
|         sourceNumber = deletedAccounts.findE164(sourceAci.get()); | ||||
|         sourceNumber = accountsManager.findRecentlyDeletedE164(sourceAci.get()); | ||||
|         sourcePni = sourceNumber.map(accountsManager::getPhoneNumberIdentifier); | ||||
|       } else { | ||||
|         sourceNumber = sourceAccount.map(Account::getNumber); | ||||
|  |  | |||
|  | @ -15,10 +15,12 @@ public class AccountLockManager { | |||
| 
 | ||||
|   private final AmazonDynamoDBLockClient lockClient; | ||||
| 
 | ||||
|   static final String KEY_ACCOUNT_E164 = "P"; | ||||
| 
 | ||||
|   public AccountLockManager(final DynamoDbClient lockDynamoDb, final String lockTableName) { | ||||
|     this(new AmazonDynamoDBLockClient( | ||||
|         AmazonDynamoDBLockClientOptions.builder(lockDynamoDb, lockTableName) | ||||
|             .withPartitionKeyName(DeletedAccounts.KEY_ACCOUNT_E164) | ||||
|             .withPartitionKeyName(KEY_ACCOUNT_E164) | ||||
|             .withLeaseDuration(15L) | ||||
|             .withHeartbeatPeriod(2L) | ||||
|             .withTimeUnit(TimeUnit.SECONDS) | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ import java.io.IOException; | |||
| import java.nio.ByteBuffer; | ||||
| import java.time.Clock; | ||||
| import java.time.Duration; | ||||
| import java.time.Instant; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
|  | @ -48,9 +49,11 @@ import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | |||
| import software.amazon.awssdk.services.dynamodb.model.CancellationReason; | ||||
| import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; | ||||
| import software.amazon.awssdk.services.dynamodb.model.Delete; | ||||
| import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; | ||||
| import software.amazon.awssdk.services.dynamodb.model.Put; | ||||
| import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.QueryRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.QueryResponse; | ||||
| import software.amazon.awssdk.services.dynamodb.model.ReturnValuesOnConditionCheckFailure; | ||||
|  | @ -124,18 +127,23 @@ public class Accounts extends AbstractDynamoDbStore { | |||
|   // time to live; number | ||||
|   static final String ATTR_TTL = "TTL"; | ||||
| 
 | ||||
|   static final String DELETED_ACCOUNTS_KEY_ACCOUNT_E164 = "P"; | ||||
|   static final String DELETED_ACCOUNTS_ATTR_ACCOUNT_UUID = "U"; | ||||
|   static final String DELETED_ACCOUNTS_ATTR_EXPIRES = "E"; | ||||
|   static final String DELETED_ACCOUNTS_UUID_TO_E164_INDEX_NAME = "u_to_p"; | ||||
| 
 | ||||
|   static final String USERNAME_LINK_TO_UUID_INDEX = "ul_to_u"; | ||||
| 
 | ||||
|   static final Duration DELETED_ACCOUNTS_TIME_TO_LIVE = Duration.ofDays(30); | ||||
| 
 | ||||
|   private final Clock clock; | ||||
| 
 | ||||
|   private final DynamoDbAsyncClient asyncClient; | ||||
| 
 | ||||
|   private final String phoneNumberConstraintTableName; | ||||
| 
 | ||||
|   private final String phoneNumberIdentifierConstraintTableName; | ||||
| 
 | ||||
|   private final String usernamesConstraintTableName; | ||||
| 
 | ||||
|   private final String deletedAccountsTableName; | ||||
|   private final String accountsTableName; | ||||
| 
 | ||||
|   private final int scanPageSize; | ||||
|  | @ -150,6 +158,7 @@ public class Accounts extends AbstractDynamoDbStore { | |||
|       final String phoneNumberConstraintTableName, | ||||
|       final String phoneNumberIdentifierConstraintTableName, | ||||
|       final String usernamesConstraintTableName, | ||||
|       final String deletedAccountsTableName, | ||||
|       final int scanPageSize) { | ||||
|     super(client); | ||||
|     this.clock = clock; | ||||
|  | @ -158,6 +167,7 @@ public class Accounts extends AbstractDynamoDbStore { | |||
|     this.phoneNumberIdentifierConstraintTableName = phoneNumberIdentifierConstraintTableName; | ||||
|     this.accountsTableName = accountsTableName; | ||||
|     this.usernamesConstraintTableName = usernamesConstraintTableName; | ||||
|     this.deletedAccountsTableName = deletedAccountsTableName; | ||||
|     this.scanPageSize = scanPageSize; | ||||
|   } | ||||
| 
 | ||||
|  | @ -168,10 +178,11 @@ public class Accounts extends AbstractDynamoDbStore { | |||
|       final String phoneNumberConstraintTableName, | ||||
|       final String phoneNumberIdentifierConstraintTableName, | ||||
|       final String usernamesConstraintTableName, | ||||
|       final String deletedAccountsTableName, | ||||
|       final int scanPageSize) { | ||||
|     this(Clock.systemUTC(), client, asyncClient, accountsTableName, | ||||
|         phoneNumberConstraintTableName, phoneNumberIdentifierConstraintTableName, usernamesConstraintTableName, | ||||
|         scanPageSize); | ||||
|         deletedAccountsTableName, scanPageSize); | ||||
|   } | ||||
| 
 | ||||
|   public boolean create(final Account account) { | ||||
|  | @ -706,6 +717,23 @@ public class Accounts extends AbstractDynamoDbStore { | |||
|             .map(Accounts::fromItem))); | ||||
|   } | ||||
| 
 | ||||
|   public void putRecentlyDeletedAccount(final UUID uuid, final String e164) { | ||||
|     db().putItem(PutItemRequest.builder() | ||||
|         .tableName(deletedAccountsTableName) | ||||
|         .item(Map.of( | ||||
|             DELETED_ACCOUNTS_KEY_ACCOUNT_E164, AttributeValues.fromString(e164), | ||||
|             DELETED_ACCOUNTS_ATTR_ACCOUNT_UUID, AttributeValues.fromUUID(uuid), | ||||
|             DELETED_ACCOUNTS_ATTR_EXPIRES, AttributeValues.fromLong(Instant.now().plus(DELETED_ACCOUNTS_TIME_TO_LIVE).getEpochSecond()))) | ||||
|         .build()); | ||||
|   } | ||||
| 
 | ||||
|   public void removeRecentlyDeletedAccount(final String e164) { | ||||
|     db().deleteItem(DeleteItemRequest.builder() | ||||
|         .tableName(deletedAccountsTableName) | ||||
|         .key(Map.of(DELETED_ACCOUNTS_KEY_ACCOUNT_E164, AttributeValues.fromString(e164))) | ||||
|         .build()); | ||||
|   } | ||||
| 
 | ||||
|   @Nonnull | ||||
|   public CompletableFuture<Optional<Account>> getByAccountIdentifierAsync(final UUID uuid) { | ||||
|     return AsyncTimerUtil.record(GET_BY_UUID_TIMER, () -> itemByKeyAsync(accountsTableName, KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)) | ||||
|  | @ -713,6 +741,37 @@ public class Accounts extends AbstractDynamoDbStore { | |||
|         .toCompletableFuture(); | ||||
|   } | ||||
| 
 | ||||
|   public Optional<UUID> findRecentlyDeletedAccountIdentifier(final String e164) { | ||||
|     final GetItemResponse response = db().getItem(GetItemRequest.builder() | ||||
|         .tableName(deletedAccountsTableName) | ||||
|         .consistentRead(true) | ||||
|         .key(Map.of(DELETED_ACCOUNTS_KEY_ACCOUNT_E164, AttributeValues.fromString(e164))) | ||||
|         .build()); | ||||
| 
 | ||||
|     return Optional.ofNullable(AttributeValues.getUUID(response.item(), DELETED_ACCOUNTS_ATTR_ACCOUNT_UUID, null)); | ||||
|   } | ||||
| 
 | ||||
|   public Optional<String> findRecentlyDeletedE164(final UUID uuid) { | ||||
|     final QueryResponse response = db().query(QueryRequest.builder() | ||||
|         .tableName(deletedAccountsTableName) | ||||
|         .indexName(DELETED_ACCOUNTS_UUID_TO_E164_INDEX_NAME) | ||||
|         .keyConditionExpression("#uuid = :uuid") | ||||
|         .projectionExpression("#e164") | ||||
|         .expressionAttributeNames(Map.of("#uuid", DELETED_ACCOUNTS_ATTR_ACCOUNT_UUID, | ||||
|             "#e164", DELETED_ACCOUNTS_KEY_ACCOUNT_E164)) | ||||
|         .expressionAttributeValues(Map.of(":uuid", AttributeValues.fromUUID(uuid))).build()); | ||||
| 
 | ||||
|     if (response.count() == 0) { | ||||
|       return Optional.empty(); | ||||
|     } | ||||
| 
 | ||||
|     if (response.count() > 1) { | ||||
|       throw new RuntimeException("Impossible result: more than one phone number returned for UUID: " + uuid); | ||||
|     } | ||||
| 
 | ||||
|     return Optional.ofNullable(response.items().get(0).get(DELETED_ACCOUNTS_KEY_ACCOUNT_E164).s()); | ||||
|   } | ||||
| 
 | ||||
|   public void delete(final UUID uuid) { | ||||
|     DELETE_TIMER.record(() -> getByAccountIdentifier(uuid).ifPresent(account -> { | ||||
| 
 | ||||
|  |  | |||
|  | @ -101,7 +101,6 @@ public class AccountsManager { | |||
|   private final PhoneNumberIdentifiers phoneNumberIdentifiers; | ||||
|   private final FaultTolerantRedisCluster cacheCluster; | ||||
|   private final AccountLockManager accountLockManager; | ||||
|   private final DeletedAccounts deletedAccounts; | ||||
|   private final KeysManager keysManager; | ||||
|   private final MessagesManager messagesManager; | ||||
|   private final ProfilesManager profilesManager; | ||||
|  | @ -147,7 +146,6 @@ public class AccountsManager { | |||
|       final PhoneNumberIdentifiers phoneNumberIdentifiers, | ||||
|       final FaultTolerantRedisCluster cacheCluster, | ||||
|       final AccountLockManager accountLockManager, | ||||
|       final DeletedAccounts deletedAccounts, | ||||
|       final KeysManager keysManager, | ||||
|       final MessagesManager messagesManager, | ||||
|       final ProfilesManager profilesManager, | ||||
|  | @ -162,7 +160,6 @@ public class AccountsManager { | |||
|     this.phoneNumberIdentifiers = phoneNumberIdentifiers; | ||||
|     this.cacheCluster = cacheCluster; | ||||
|     this.accountLockManager = accountLockManager; | ||||
|     this.deletedAccounts = deletedAccounts; | ||||
|     this.keysManager = keysManager; | ||||
|     this.messagesManager = messagesManager; | ||||
|     this.profilesManager = profilesManager; | ||||
|  | @ -201,7 +198,7 @@ public class AccountsManager { | |||
| 
 | ||||
|         // Reuse the ACI from any recently-deleted account with this number to cover cases where somebody is | ||||
|         // re-registering. | ||||
|         account.setUuid(deletedAccounts.findUuid(number).orElseGet(UUID::randomUUID)); | ||||
|         account.setUuid(accounts.findRecentlyDeletedAccountIdentifier(number).orElseGet(UUID::randomUUID)); | ||||
|         account.addDevice(device); | ||||
|         account.setRegistrationLockFromAttributes(accountAttributes); | ||||
|         account.setUnidentifiedAccessKey(accountAttributes.getUnidentifiedAccessKey()); | ||||
|  | @ -261,7 +258,7 @@ public class AccountsManager { | |||
| 
 | ||||
|         // Clear any "recently deleted account" record for this number since, if it existed, we've used its old ACI for | ||||
|         // the newly-created account. | ||||
|         deletedAccounts.remove(number); | ||||
|         accounts.removeRecentlyDeletedAccount(number); | ||||
|       }); | ||||
| 
 | ||||
|       return account; | ||||
|  | @ -303,7 +300,7 @@ public class AccountsManager { | |||
|       //    of the account changing its number (which facilitates ACI consistency in cases that a party is switching | ||||
|       //    back and forth between numbers). | ||||
|       // 3. No account with the target number exists at all, in which case no additional action is needed. | ||||
|       final Optional<UUID> recentlyDeletedAci = deletedAccounts.findUuid(targetNumber); | ||||
|       final Optional<UUID> recentlyDeletedAci = accounts.findRecentlyDeletedAccountIdentifier(targetNumber); | ||||
|       final Optional<Account> maybeExistingAccount = getByE164(targetNumber); | ||||
|       final Optional<UUID> maybeDisplacedUuid; | ||||
| 
 | ||||
|  | @ -314,7 +311,7 @@ public class AccountsManager { | |||
|         maybeDisplacedUuid = recentlyDeletedAci; | ||||
|       } | ||||
| 
 | ||||
|       maybeDisplacedUuid.ifPresent(displacedUuid -> deletedAccounts.put(displacedUuid, originalNumber)); | ||||
|       maybeDisplacedUuid.ifPresent(displacedUuid -> accounts.putRecentlyDeletedAccount(displacedUuid, originalNumber)); | ||||
| 
 | ||||
|       final UUID uuid = account.getUuid(); | ||||
|       final UUID phoneNumberIdentifier = phoneNumberIdentifiers.getPhoneNumberIdentifier(targetNumber); | ||||
|  | @ -836,6 +833,14 @@ public class AccountsManager { | |||
|     return phoneNumberIdentifiers.getPhoneNumberIdentifier(e164); | ||||
|   } | ||||
| 
 | ||||
|   public Optional<UUID> findRecentlyDeletedAccountIdentifier(final String e164) { | ||||
|     return accounts.findRecentlyDeletedAccountIdentifier(e164); | ||||
|   } | ||||
| 
 | ||||
|   public Optional<String> findRecentlyDeletedE164(final UUID uuid) { | ||||
|     return accounts.findRecentlyDeletedE164(uuid); | ||||
|   } | ||||
| 
 | ||||
|   public AccountCrawlChunk getAllFromDynamo(int length) { | ||||
|     return accounts.getAllFromStart(length); | ||||
|   } | ||||
|  | @ -856,7 +861,7 @@ public class AccountsManager { | |||
| 
 | ||||
|         delete(account); | ||||
| 
 | ||||
|         deletedAccounts.put(accountIdentifier, e164); | ||||
|         accounts.putRecentlyDeletedAccount(accountIdentifier, e164); | ||||
|       }); | ||||
|     } catch (final RuntimeException | InterruptedException e) { | ||||
|       logger.warn("Failed to delete account", e); | ||||
|  |  | |||
|  | @ -1,87 +0,0 @@ | |||
| /* | ||||
|  * Copyright 2013-2021 Signal Messenger, LLC | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
| package org.whispersystems.textsecuregcm.storage; | ||||
| 
 | ||||
| import java.time.Duration; | ||||
| import java.time.Instant; | ||||
| import java.util.Map; | ||||
| import java.util.Optional; | ||||
| import java.util.UUID; | ||||
| import org.whispersystems.textsecuregcm.util.AttributeValues; | ||||
| import software.amazon.awssdk.services.dynamodb.DynamoDbClient; | ||||
| import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; | ||||
| import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.QueryRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.QueryResponse; | ||||
| 
 | ||||
| public class DeletedAccounts { | ||||
| 
 | ||||
|   // e164, primary key | ||||
|   static final String KEY_ACCOUNT_E164 = "P"; | ||||
|   static final String ATTR_ACCOUNT_UUID = "U"; | ||||
|   static final String ATTR_EXPIRES = "E"; | ||||
| 
 | ||||
|   static final String UUID_TO_E164_INDEX_NAME = "u_to_p"; | ||||
| 
 | ||||
|   static final Duration TIME_TO_LIVE = Duration.ofDays(30); | ||||
| 
 | ||||
|   private final DynamoDbClient dynamoDbClient; | ||||
|   private final String tableName; | ||||
| 
 | ||||
|   public DeletedAccounts(final DynamoDbClient dynamoDbClient, final String tableName) { | ||||
|     this.dynamoDbClient = dynamoDbClient; | ||||
|     this.tableName = tableName; | ||||
|   } | ||||
| 
 | ||||
|   public void put(UUID uuid, String e164) { | ||||
|     dynamoDbClient.putItem(PutItemRequest.builder() | ||||
|         .tableName(tableName) | ||||
|         .item(Map.of( | ||||
|             KEY_ACCOUNT_E164, AttributeValues.fromString(e164), | ||||
|             ATTR_ACCOUNT_UUID, AttributeValues.fromUUID(uuid), | ||||
|             ATTR_EXPIRES, AttributeValues.fromLong(Instant.now().plus(TIME_TO_LIVE).getEpochSecond()))) | ||||
|         .build()); | ||||
|   } | ||||
| 
 | ||||
|   public Optional<UUID> findUuid(final String e164) { | ||||
|     final GetItemResponse response = dynamoDbClient.getItem(GetItemRequest.builder() | ||||
|         .tableName(tableName) | ||||
|         .consistentRead(true) | ||||
|         .key(Map.of(KEY_ACCOUNT_E164, AttributeValues.fromString(e164))) | ||||
|         .build()); | ||||
| 
 | ||||
|     return Optional.ofNullable(AttributeValues.getUUID(response.item(), ATTR_ACCOUNT_UUID, null)); | ||||
|   } | ||||
| 
 | ||||
|   public Optional<String> findE164(final UUID uuid) { | ||||
|     final QueryResponse response = dynamoDbClient.query(QueryRequest.builder() | ||||
|         .tableName(tableName) | ||||
|         .indexName(UUID_TO_E164_INDEX_NAME) | ||||
|         .keyConditionExpression("#uuid = :uuid") | ||||
|         .projectionExpression("#e164") | ||||
|         .expressionAttributeNames(Map.of("#uuid", ATTR_ACCOUNT_UUID, | ||||
|             "#e164", KEY_ACCOUNT_E164)) | ||||
|         .expressionAttributeValues(Map.of(":uuid", AttributeValues.fromUUID(uuid))).build()); | ||||
| 
 | ||||
|     if (response.count() == 0) { | ||||
|       return Optional.empty(); | ||||
|     } | ||||
| 
 | ||||
|     if (response.count() > 1) { | ||||
|       throw new RuntimeException("Impossible result: more than one phone number returned for UUID: " + uuid); | ||||
|     } | ||||
| 
 | ||||
|     return Optional.ofNullable(response.items().get(0).get(KEY_ACCOUNT_E164).s()); | ||||
|   } | ||||
| 
 | ||||
|   public void remove(final String e164) { | ||||
|     dynamoDbClient.deleteItem(DeleteItemRequest.builder() | ||||
|         .tableName(tableName) | ||||
|         .key(Map.of(KEY_ACCOUNT_E164, AttributeValues.fromString(e164))) | ||||
|         .build()); | ||||
|   } | ||||
| } | ||||
|  | @ -38,7 +38,6 @@ import org.whispersystems.textsecuregcm.storage.Account; | |||
| import org.whispersystems.textsecuregcm.storage.AccountLockManager; | ||||
| import org.whispersystems.textsecuregcm.storage.Accounts; | ||||
| import org.whispersystems.textsecuregcm.storage.AccountsManager; | ||||
| import org.whispersystems.textsecuregcm.storage.DeletedAccounts; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; | ||||
| import org.whispersystems.textsecuregcm.storage.KeysManager; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesCache; | ||||
|  | @ -141,8 +140,6 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi | |||
|     DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(configuration.getDynamoDbClientConfiguration(), | ||||
|         WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER); | ||||
| 
 | ||||
|     DeletedAccounts deletedAccounts = new DeletedAccounts(dynamoDbClient, | ||||
|         configuration.getDynamoDbTables().getDeletedAccounts().getTableName()); | ||||
|     RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords( | ||||
|         configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(), | ||||
|         configuration.getDynamoDbTables().getRegistrationRecovery().getExpiration(), | ||||
|  | @ -159,6 +156,7 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi | |||
|         configuration.getDynamoDbTables().getAccounts().getPhoneNumberTableName(), | ||||
|         configuration.getDynamoDbTables().getAccounts().getPhoneNumberIdentifierTableName(), | ||||
|         configuration.getDynamoDbTables().getAccounts().getUsernamesTableName(), | ||||
|         configuration.getDynamoDbTables().getDeletedAccounts().getTableName(), | ||||
|         configuration.getDynamoDbTables().getAccounts().getScanPageSize()); | ||||
|     PhoneNumberIdentifiers phoneNumberIdentifiers = new PhoneNumberIdentifiers(dynamoDbClient, | ||||
|         configuration.getDynamoDbTables().getPhoneNumberIdentifiers().getTableName()); | ||||
|  | @ -206,7 +204,7 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi | |||
|     AccountLockManager accountLockManager = new AccountLockManager(dynamoDbClient, | ||||
|         configuration.getDynamoDbTables().getDeletedAccountsLock().getTableName()); | ||||
|     AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster, | ||||
|         accountLockManager, deletedAccounts, keys, messagesManager, profilesManager, | ||||
|         accountLockManager, keys, messagesManager, profilesManager, | ||||
|             secureStorageClient, secureBackupClient, secureValueRecovery2Client, clientPresenceManager, | ||||
|         experimentEnrollmentManager, registrationRecoveryPasswordsManager, Clock.systemUTC()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,7 +31,6 @@ import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2 | |||
| import org.whispersystems.textsecuregcm.storage.AccountLockManager; | ||||
| import org.whispersystems.textsecuregcm.storage.Accounts; | ||||
| import org.whispersystems.textsecuregcm.storage.AccountsManager; | ||||
| import org.whispersystems.textsecuregcm.storage.DeletedAccounts; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; | ||||
| import org.whispersystems.textsecuregcm.storage.KeysManager; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesCache; | ||||
|  | @ -117,8 +116,6 @@ record CommandDependencies( | |||
|     DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client( | ||||
|         configuration.getDynamoDbClientConfiguration(), WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER); | ||||
| 
 | ||||
|     DeletedAccounts deletedAccounts = new DeletedAccounts(dynamoDbClient, | ||||
|         configuration.getDynamoDbTables().getDeletedAccounts().getTableName()); | ||||
|     RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords( | ||||
|         configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(), | ||||
|         configuration.getDynamoDbTables().getRegistrationRecovery().getExpiration(), | ||||
|  | @ -136,6 +133,7 @@ record CommandDependencies( | |||
|         configuration.getDynamoDbTables().getAccounts().getPhoneNumberTableName(), | ||||
|         configuration.getDynamoDbTables().getAccounts().getPhoneNumberIdentifierTableName(), | ||||
|         configuration.getDynamoDbTables().getAccounts().getUsernamesTableName(), | ||||
|         configuration.getDynamoDbTables().getDeletedAccounts().getTableName(), | ||||
|         configuration.getDynamoDbTables().getAccounts().getScanPageSize()); | ||||
|     PhoneNumberIdentifiers phoneNumberIdentifiers = new PhoneNumberIdentifiers(dynamoDbClient, | ||||
|         configuration.getDynamoDbTables().getPhoneNumberIdentifiers().getTableName()); | ||||
|  | @ -184,7 +182,7 @@ record CommandDependencies( | |||
|     AccountLockManager accountLockManager = new AccountLockManager(dynamoDbClient, | ||||
|         configuration.getDynamoDbTables().getDeletedAccountsLock().getTableName()); | ||||
|     AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster, | ||||
|         accountLockManager, deletedAccounts, keys, messagesManager, profilesManager, | ||||
|         accountLockManager, keys, messagesManager, profilesManager, | ||||
|             secureStorageClient, secureBackupClient, secureValueRecovery2Client, | ||||
|         clientPresenceManager, | ||||
|         experimentEnrollmentManager, registrationRecoveryPasswordsManager, clock); | ||||
|  |  | |||
|  | @ -113,7 +113,6 @@ import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider; | |||
| import org.whispersystems.textsecuregcm.storage.Account; | ||||
| import org.whispersystems.textsecuregcm.storage.AccountsManager; | ||||
| import org.whispersystems.textsecuregcm.storage.ClientReleaseManager; | ||||
| import org.whispersystems.textsecuregcm.storage.DeletedAccounts; | ||||
| import org.whispersystems.textsecuregcm.storage.Device; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesManager; | ||||
|  | @ -159,7 +158,6 @@ class MessageControllerTest { | |||
|   private static final MessageSender messageSender = mock(MessageSender.class); | ||||
|   private static final ReceiptSender receiptSender = mock(ReceiptSender.class); | ||||
|   private static final AccountsManager accountsManager = mock(AccountsManager.class); | ||||
|   private static final DeletedAccounts deletedAccounts = mock(DeletedAccounts.class); | ||||
|   private static final MessagesManager messagesManager = mock(MessagesManager.class); | ||||
|   private static final RateLimiters rateLimiters = mock(RateLimiters.class); | ||||
|   private static final CardinalityEstimator cardinalityEstimator = mock(CardinalityEstimator.class); | ||||
|  | @ -179,7 +177,7 @@ class MessageControllerTest { | |||
|       .addProvider(MultiRecipientMessageProvider.class) | ||||
|       .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) | ||||
|       .addResource( | ||||
|           new MessageController(rateLimiters, cardinalityEstimator, messageSender, receiptSender, accountsManager, deletedAccounts, | ||||
|           new MessageController(rateLimiters, cardinalityEstimator, messageSender, receiptSender, accountsManager, | ||||
|               messagesManager, pushNotificationManager, reportMessageManager, multiRecipientMessageExecutor, | ||||
|               messageDeliveryScheduler, ReportSpamTokenProvider.noop(), mock(ClientReleaseManager.class), dynamicConfigurationManager)) | ||||
|       .build(); | ||||
|  | @ -245,7 +243,6 @@ class MessageControllerTest { | |||
|         messageSender, | ||||
|         receiptSender, | ||||
|         accountsManager, | ||||
|         deletedAccounts, | ||||
|         messagesManager, | ||||
|         rateLimiters, | ||||
|         rateLimiter, | ||||
|  | @ -681,7 +678,7 @@ class MessageControllerTest { | |||
|     when(account.getPhoneNumberIdentifier()).thenReturn(senderPni); | ||||
| 
 | ||||
|     when(accountsManager.getByE164(senderNumber)).thenReturn(Optional.of(account)); | ||||
|     when(deletedAccounts.findUuid(senderNumber)).thenReturn(Optional.of(senderAci)); | ||||
|     when(accountsManager.findRecentlyDeletedAccountIdentifier(senderNumber)).thenReturn(Optional.of(senderAci)); | ||||
|     when(accountsManager.getPhoneNumberIdentifier(senderNumber)).thenReturn(senderPni); | ||||
| 
 | ||||
|     Response response = | ||||
|  | @ -696,7 +693,7 @@ class MessageControllerTest { | |||
| 
 | ||||
|     verify(reportMessageManager).report(Optional.of(senderNumber), Optional.of(senderAci), Optional.of(senderPni), | ||||
|         messageGuid, AuthHelper.VALID_UUID, Optional.empty(), userAgent); | ||||
|     verify(deletedAccounts, never()).findE164(any(UUID.class)); | ||||
|     verify(accountsManager, never()).findRecentlyDeletedE164(any(UUID.class)); | ||||
|     verify(accountsManager, never()).getPhoneNumberIdentifier(anyString()); | ||||
| 
 | ||||
|     when(accountsManager.getByE164(senderNumber)).thenReturn(Optional.empty()); | ||||
|  | @ -731,7 +728,7 @@ class MessageControllerTest { | |||
|     when(account.getPhoneNumberIdentifier()).thenReturn(senderPni); | ||||
| 
 | ||||
|     when(accountsManager.getByAccountIdentifier(senderAci)).thenReturn(Optional.of(account)); | ||||
|     when(deletedAccounts.findE164(senderAci)).thenReturn(Optional.of(senderNumber)); | ||||
|     when(accountsManager.findRecentlyDeletedE164(senderAci)).thenReturn(Optional.of(senderNumber)); | ||||
|     when(accountsManager.getPhoneNumberIdentifier(senderNumber)).thenReturn(senderPni); | ||||
| 
 | ||||
|     Response response = | ||||
|  | @ -746,7 +743,7 @@ class MessageControllerTest { | |||
| 
 | ||||
|     verify(reportMessageManager).report(Optional.of(senderNumber), Optional.of(senderAci), Optional.of(senderPni), | ||||
|         messageGuid, AuthHelper.VALID_UUID, Optional.empty(), userAgent); | ||||
|     verify(deletedAccounts, never()).findE164(any(UUID.class)); | ||||
|     verify(accountsManager, never()).findRecentlyDeletedE164(any(UUID.class)); | ||||
|     verify(accountsManager, never()).getPhoneNumberIdentifier(anyString()); | ||||
| 
 | ||||
|     when(accountsManager.getByAccountIdentifier(senderAci)).thenReturn(Optional.empty()); | ||||
|  | @ -781,7 +778,7 @@ class MessageControllerTest { | |||
|     when(account.getPhoneNumberIdentifier()).thenReturn(senderPni); | ||||
| 
 | ||||
|     when(accountsManager.getByAccountIdentifier(senderAci)).thenReturn(Optional.of(account)); | ||||
|     when(deletedAccounts.findE164(senderAci)).thenReturn(Optional.of(senderNumber)); | ||||
|     when(accountsManager.findRecentlyDeletedE164(senderAci)).thenReturn(Optional.of(senderNumber)); | ||||
|     when(accountsManager.getPhoneNumberIdentifier(senderNumber)).thenReturn(senderPni); | ||||
| 
 | ||||
|     Entity<SpamReport> entity = Entity.entity(new SpamReport(new byte[3]), "application/json"); | ||||
|  | @ -800,7 +797,7 @@ class MessageControllerTest { | |||
|         eq(AuthHelper.VALID_UUID), | ||||
|         argThat(maybeBytes -> maybeBytes.map(bytes -> Arrays.equals(bytes, new byte[3])).orElse(false)), | ||||
|         any()); | ||||
|     verify(deletedAccounts, never()).findE164(any(UUID.class)); | ||||
|     verify(accountsManager, never()).findRecentlyDeletedE164(any(UUID.class)); | ||||
|     verify(accountsManager, never()).getPhoneNumberIdentifier(anyString()); | ||||
|     when(accountsManager.getByAccountIdentifier(senderAci)).thenReturn(Optional.empty()); | ||||
| 
 | ||||
|  | @ -839,7 +836,7 @@ class MessageControllerTest { | |||
|     when(account.getPhoneNumberIdentifier()).thenReturn(senderPni); | ||||
| 
 | ||||
|     when(accountsManager.getByAccountIdentifier(senderAci)).thenReturn(Optional.of(account)); | ||||
|     when(deletedAccounts.findE164(senderAci)).thenReturn(Optional.of(senderNumber)); | ||||
|     when(accountsManager.findRecentlyDeletedE164(senderAci)).thenReturn(Optional.of(senderNumber)); | ||||
|     when(accountsManager.getPhoneNumberIdentifier(senderNumber)).thenReturn(senderPni); | ||||
| 
 | ||||
|     Response response = | ||||
|  |  | |||
|  | @ -58,7 +58,6 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
|   static final RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build(); | ||||
| 
 | ||||
|   private ClientPresenceManager clientPresenceManager; | ||||
|   private DeletedAccounts deletedAccounts; | ||||
| 
 | ||||
|   private AccountsManager accountsManager; | ||||
| 
 | ||||
|  | @ -79,11 +78,9 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
|           Tables.NUMBERS.tableName(), | ||||
|           Tables.PNI_ASSIGNMENTS.tableName(), | ||||
|           Tables.USERNAMES.tableName(), | ||||
|           Tables.DELETED_ACCOUNTS.tableName(), | ||||
|           SCAN_PAGE_SIZE); | ||||
| 
 | ||||
|       deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(), | ||||
|           Tables.DELETED_ACCOUNTS.tableName()); | ||||
| 
 | ||||
|       final AccountLockManager accountLockManager = new AccountLockManager(DYNAMO_DB_EXTENSION.getDynamoDbClient(), | ||||
|           Tables.DELETED_ACCOUNTS_LOCK.tableName()); | ||||
| 
 | ||||
|  | @ -112,7 +109,6 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
|           phoneNumberIdentifiers, | ||||
|           CACHE_CLUSTER_EXTENSION.getRedisCluster(), | ||||
|           accountLockManager, | ||||
|           deletedAccounts, | ||||
|           keysManager, | ||||
|           messagesManager, | ||||
|           mock(ProfilesManager.class), | ||||
|  | @ -145,8 +141,8 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
| 
 | ||||
|     assertEquals(secondNumber, accountsManager.getByAccountIdentifier(originalUuid).map(Account::getNumber).orElseThrow()); | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(originalNumber)); | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|  | @ -178,8 +174,8 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
| 
 | ||||
|     assertEquals(secondNumber, accountsManager.getByAccountIdentifier(originalUuid).map(Account::getNumber).orElseThrow()); | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(originalNumber)); | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber)); | ||||
| 
 | ||||
|     assertEquals(pniIdentityKey, updatedAccount.getIdentityKey(IdentityType.PNI)); | ||||
| 
 | ||||
|  | @ -209,8 +205,8 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
| 
 | ||||
|     assertEquals(originalNumber, accountsManager.getByAccountIdentifier(originalUuid).map(Account::getNumber).orElseThrow()); | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(originalNumber)); | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|  | @ -235,8 +231,8 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
| 
 | ||||
|     verify(clientPresenceManager).disconnectPresence(existingAccountUuid, Device.MASTER_ID); | ||||
| 
 | ||||
|     assertEquals(Optional.of(existingAccountUuid), deletedAccounts.findUuid(originalNumber)); | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber)); | ||||
|     assertEquals(Optional.of(existingAccountUuid), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber)); | ||||
| 
 | ||||
|     accountsManager.changeNumber(accountsManager.getByAccountIdentifier(originalUuid).orElseThrow(), originalNumber, null, null, null, null); | ||||
| 
 | ||||
|  | @ -266,13 +262,13 @@ class AccountsManagerChangeNumberIntegrationTest { | |||
|     assertEquals(existingAccountUuid, reRegisteredAccount.getUuid()); | ||||
|     assertEquals(originalPni, reRegisteredAccount.getPhoneNumberIdentifier()); | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(originalNumber)); | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber)); | ||||
| 
 | ||||
|     final Account changedNumberReRegisteredAccount = accountsManager.changeNumber(reRegisteredAccount, secondNumber, null, null, null, null); | ||||
| 
 | ||||
|     assertEquals(Optional.of(originalUuid), deletedAccounts.findUuid(originalNumber)); | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber)); | ||||
|     assertEquals(Optional.of(originalUuid), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber)); | ||||
|     assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber)); | ||||
|     assertEquals(secondPni, changedNumberReRegisteredAccount.getPhoneNumberIdentifier()); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -23,7 +23,6 @@ import java.io.IOException; | |||
| import java.time.Clock; | ||||
| import java.time.Instant; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Optional; | ||||
| import java.util.UUID; | ||||
| import java.util.concurrent.CompletableFuture; | ||||
| import java.util.concurrent.Executor; | ||||
|  | @ -63,7 +62,8 @@ class AccountsManagerConcurrentModificationIntegrationTest { | |||
|   static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension( | ||||
|       Tables.ACCOUNTS, | ||||
|       Tables.NUMBERS, | ||||
|       Tables.PNI_ASSIGNMENTS | ||||
|       Tables.PNI_ASSIGNMENTS, | ||||
|       Tables.DELETED_ACCOUNTS | ||||
|   ); | ||||
| 
 | ||||
|   private Accounts accounts; | ||||
|  | @ -88,6 +88,7 @@ class AccountsManagerConcurrentModificationIntegrationTest { | |||
|         Tables.NUMBERS.tableName(), | ||||
|         Tables.PNI_ASSIGNMENTS.tableName(), | ||||
|         Tables.USERNAMES.tableName(), | ||||
|         Tables.DELETED_ACCOUNTS.tableName(), | ||||
|         SCAN_PAGE_SIZE); | ||||
| 
 | ||||
|     { | ||||
|  | @ -103,9 +104,6 @@ class AccountsManagerConcurrentModificationIntegrationTest { | |||
|         return null; | ||||
|       }).when(accountLockManager).withLock(any(), any()); | ||||
| 
 | ||||
|       final DeletedAccounts deletedAccounts = mock(DeletedAccounts.class); | ||||
|       when(deletedAccounts.findUuid(any())).thenReturn(Optional.empty()); | ||||
| 
 | ||||
|       final PhoneNumberIdentifiers phoneNumberIdentifiers = mock(PhoneNumberIdentifiers.class); | ||||
|       when(phoneNumberIdentifiers.getPhoneNumberIdentifier(anyString())) | ||||
|           .thenAnswer((Answer<UUID>) invocation -> UUID.randomUUID()); | ||||
|  | @ -115,7 +113,6 @@ class AccountsManagerConcurrentModificationIntegrationTest { | |||
|           phoneNumberIdentifiers, | ||||
|           RedisClusterHelper.builder().stringCommands(commands).build(), | ||||
|           accountLockManager, | ||||
|           deletedAccounts, | ||||
|           mock(KeysManager.class), | ||||
|           mock(MessagesManager.class), | ||||
|           mock(ProfilesManager.class), | ||||
|  |  | |||
|  | @ -92,7 +92,6 @@ class AccountsManagerTest { | |||
|   private static final byte[] ENCRYPTED_USERNAME_2 = Base64.getUrlDecoder().decode(BASE_64_URL_ENCRYPTED_USERNAME_2); | ||||
| 
 | ||||
|   private Accounts accounts; | ||||
|   private DeletedAccounts deletedAccounts; | ||||
|   private KeysManager keysManager; | ||||
|   private MessagesManager messagesManager; | ||||
|   private ProfilesManager profilesManager; | ||||
|  | @ -125,7 +124,6 @@ class AccountsManagerTest { | |||
|   @BeforeEach | ||||
|   void setup() throws InterruptedException { | ||||
|     accounts = mock(Accounts.class); | ||||
|     deletedAccounts = mock(DeletedAccounts.class); | ||||
|     keysManager = mock(KeysManager.class); | ||||
|     messagesManager = mock(MessagesManager.class); | ||||
|     profilesManager = mock(ProfilesManager.class); | ||||
|  | @ -152,8 +150,6 @@ class AccountsManagerTest { | |||
|       return null; | ||||
|     }).when(accounts).changeNumber(any(), anyString(), any()); | ||||
| 
 | ||||
|     when(deletedAccounts.findUuid(anyString())).thenReturn(Optional.empty()); | ||||
| 
 | ||||
|     final SecureStorageClient storageClient = mock(SecureStorageClient.class); | ||||
|     when(storageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null)); | ||||
| 
 | ||||
|  | @ -202,7 +198,6 @@ class AccountsManagerTest { | |||
|             .stringAsyncCommands(asyncCommands) | ||||
|             .build(), | ||||
|         accountLockManager, | ||||
|         deletedAccounts, | ||||
|         keysManager, | ||||
|         messagesManager, | ||||
|         profilesManager, | ||||
|  | @ -954,7 +949,7 @@ class AccountsManagerTest { | |||
|   void testCreateAccountRecentlyDeleted() throws InterruptedException { | ||||
|     final UUID recentlyDeletedUuid = UUID.randomUUID(); | ||||
| 
 | ||||
|     when(deletedAccounts.findUuid(anyString())).thenReturn(Optional.of(recentlyDeletedUuid)); | ||||
|     when(accounts.findRecentlyDeletedAccountIdentifier(anyString())).thenReturn(Optional.of(recentlyDeletedUuid)); | ||||
|     when(accounts.create(any())).thenReturn(true); | ||||
| 
 | ||||
|     final String e164 = "+18005550123"; | ||||
|  | @ -1036,7 +1031,7 @@ class AccountsManagerTest { | |||
|     account = accountsManager.changeNumber(account, number, null, null, null, null); | ||||
| 
 | ||||
|     assertEquals(number, account.getNumber()); | ||||
|     verify(deletedAccounts, never()).put(any(), any()); | ||||
|     verify(accounts, never()).putRecentlyDeletedAccount(any(), any()); | ||||
|     verify(keysManager, never()).delete(any()); | ||||
|   } | ||||
| 
 | ||||
|  | @ -1052,7 +1047,6 @@ class AccountsManagerTest { | |||
|         "AccountsManager should not allow use of changeNumber with new PNI keys but without changing number"); | ||||
| 
 | ||||
|     verify(accounts, never()).update(any()); | ||||
|     verifyNoInteractions(deletedAccounts); | ||||
|     verifyNoInteractions(keysManager); | ||||
|   } | ||||
| 
 | ||||
|  | @ -1210,7 +1204,6 @@ class AccountsManagerTest { | |||
|         updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, d -> d.getPhoneNumberIdentityRegistrationId().getAsInt()))); | ||||
| 
 | ||||
|     verify(accounts).update(any()); | ||||
|     verifyNoInteractions(deletedAccounts); | ||||
| 
 | ||||
|     verify(keysManager).delete(oldPni); | ||||
|   } | ||||
|  | @ -1264,7 +1257,6 @@ class AccountsManagerTest { | |||
|         updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, d -> d.getPhoneNumberIdentityRegistrationId().getAsInt()))); | ||||
| 
 | ||||
|     verify(accounts).update(any()); | ||||
|     verifyNoInteractions(deletedAccounts); | ||||
| 
 | ||||
|     verify(keysManager).delete(oldPni); | ||||
|     verify(keysManager).storeEcSignedPreKeys(oldPni, newSignedKeys); | ||||
|  | @ -1297,7 +1289,6 @@ class AccountsManagerTest { | |||
|         () -> accountsManager.updatePniKeys(account, pniIdentityKey, newSignedKeys, null, newRegistrationIds)); | ||||
| 
 | ||||
|     verifyNoInteractions(accounts); | ||||
|     verifyNoInteractions(deletedAccounts); | ||||
|     verifyNoInteractions(keysManager); | ||||
|   } | ||||
| 
 | ||||
|  | @ -1327,7 +1318,6 @@ class AccountsManagerTest { | |||
|         () -> accountsManager.updatePniKeys(account, pniIdentityKey, newSignedKeys, newSignedPqKeys, newRegistrationIds)); | ||||
| 
 | ||||
|     verifyNoInteractions(accounts); | ||||
|     verifyNoInteractions(deletedAccounts); | ||||
|     verifyNoInteractions(keysManager); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,7 +28,6 @@ import java.util.Base64; | |||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Optional; | ||||
| import java.util.UUID; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | @ -65,6 +64,7 @@ class AccountsManagerUsernameIntegrationTest { | |||
|       Tables.ACCOUNTS, | ||||
|       Tables.NUMBERS, | ||||
|       Tables.USERNAMES, | ||||
|       Tables.DELETED_ACCOUNTS, | ||||
|       Tables.PNI, | ||||
|       Tables.PNI_ASSIGNMENTS); | ||||
| 
 | ||||
|  | @ -94,6 +94,7 @@ class AccountsManagerUsernameIntegrationTest { | |||
|         Tables.NUMBERS.tableName(), | ||||
|         Tables.PNI_ASSIGNMENTS.tableName(), | ||||
|         Tables.USERNAMES.tableName(), | ||||
|         Tables.DELETED_ACCOUNTS.tableName(), | ||||
|         SCAN_PAGE_SIZE)); | ||||
| 
 | ||||
|     final AccountLockManager accountLockManager = mock(AccountLockManager.class); | ||||
|  | @ -105,9 +106,6 @@ class AccountsManagerUsernameIntegrationTest { | |||
|       return null; | ||||
|     }).when(accountLockManager).withLock(any(), any()); | ||||
| 
 | ||||
|     final DeletedAccounts deletedAccounts = mock(DeletedAccounts.class); | ||||
|     when(deletedAccounts.findUuid(any())).thenReturn(Optional.empty()); | ||||
| 
 | ||||
|     final PhoneNumberIdentifiers phoneNumberIdentifiers = | ||||
|         new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.PNI.tableName()); | ||||
| 
 | ||||
|  | @ -119,7 +117,6 @@ class AccountsManagerUsernameIntegrationTest { | |||
|         phoneNumberIdentifiers, | ||||
|         CACHE_CLUSTER_EXTENSION.getRedisCluster(), | ||||
|         accountLockManager, | ||||
|         deletedAccounts, | ||||
|         mock(KeysManager.class), | ||||
|         mock(MessagesManager.class), | ||||
|         mock(ProfilesManager.class), | ||||
|  |  | |||
|  | @ -98,7 +98,8 @@ class AccountsTest { | |||
|       Tables.ACCOUNTS, | ||||
|       Tables.NUMBERS, | ||||
|       Tables.PNI_ASSIGNMENTS, | ||||
|       Tables.USERNAMES); | ||||
|       Tables.USERNAMES, | ||||
|       Tables.DELETED_ACCOUNTS); | ||||
| 
 | ||||
|   private final TestClock clock = TestClock.pinned(Instant.EPOCH); | ||||
|   private DynamicConfigurationManager<DynamicConfiguration> mockDynamicConfigManager; | ||||
|  | @ -121,6 +122,7 @@ class AccountsTest { | |||
|         Tables.NUMBERS.tableName(), | ||||
|         Tables.PNI_ASSIGNMENTS.tableName(), | ||||
|         Tables.USERNAMES.tableName(), | ||||
|         Tables.DELETED_ACCOUNTS.tableName(), | ||||
|         SCAN_PAGE_SIZE); | ||||
|   } | ||||
| 
 | ||||
|  | @ -166,7 +168,6 @@ class AccountsTest { | |||
|         mock(PhoneNumberIdentifiers.class), | ||||
|         mock(FaultTolerantRedisCluster.class), | ||||
|         mock(AccountLockManager.class), | ||||
|         mock(DeletedAccounts.class), | ||||
|         mock(KeysManager.class), | ||||
|         mock(MessagesManager.class), | ||||
|         mock(ProfilesManager.class), | ||||
|  | @ -442,7 +443,8 @@ class AccountsTest { | |||
|     final DynamoDbAsyncClient dynamoDbAsyncClient = mock(DynamoDbAsyncClient.class); | ||||
|     accounts = new Accounts(mock(DynamoDbClient.class), | ||||
|         dynamoDbAsyncClient, Tables.ACCOUNTS.tableName(), | ||||
|         Tables.NUMBERS.tableName(), Tables.PNI_ASSIGNMENTS.tableName(), Tables.USERNAMES.tableName(), SCAN_PAGE_SIZE); | ||||
|         Tables.NUMBERS.tableName(), Tables.PNI_ASSIGNMENTS.tableName(), Tables.USERNAMES.tableName(), | ||||
|         Tables.DELETED_ACCOUNTS.tableName(), SCAN_PAGE_SIZE); | ||||
| 
 | ||||
|     Exception e = TransactionConflictException.builder().build(); | ||||
|     e = wrapException ? new CompletionException(e) : e; | ||||
|  | @ -990,6 +992,59 @@ class AccountsTest { | |||
|     assertThat(account.getUsernameHash()).isEmpty(); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testPutFindRecentlyDeletedAccount() { | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), accounts.findRecentlyDeletedAccountIdentifier(e164)); | ||||
| 
 | ||||
|     accounts.putRecentlyDeletedAccount(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(uuid), accounts.findRecentlyDeletedAccountIdentifier(e164)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testRemoveRecentlyDeletedAccount() { | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), accounts.findRecentlyDeletedAccountIdentifier(e164)); | ||||
| 
 | ||||
|     accounts.putRecentlyDeletedAccount(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(uuid), accounts.findRecentlyDeletedAccountIdentifier(e164)); | ||||
| 
 | ||||
|     accounts.removeRecentlyDeletedAccount(e164); | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), accounts.findRecentlyDeletedAccountIdentifier(e164)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testFindRecentlyDeletedE164() { | ||||
|     assertEquals(Optional.empty(), accounts.findRecentlyDeletedE164(UUID.randomUUID())); | ||||
| 
 | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     accounts.putRecentlyDeletedAccount(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(e164), accounts.findRecentlyDeletedE164(uuid)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testFindRecentlyDeletedUUID() { | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), accounts.findRecentlyDeletedAccountIdentifier(e164)); | ||||
| 
 | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
| 
 | ||||
|     accounts.putRecentlyDeletedAccount(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(uuid), accounts.findRecentlyDeletedAccountIdentifier(e164)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void testIgnoredFieldsNotAddedToDataAttribute() throws Exception { | ||||
|     final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID()); | ||||
|  |  | |||
|  | @ -1,80 +0,0 @@ | |||
| /* | ||||
|  * Copyright 2013-2021 Signal Messenger, LLC | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
| package org.whispersystems.textsecuregcm.storage; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| 
 | ||||
| import java.util.Optional; | ||||
| import java.util.UUID; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.RegisterExtension; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables; | ||||
| 
 | ||||
| class DeletedAccountsTest { | ||||
| 
 | ||||
|   @RegisterExtension | ||||
|   static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.DELETED_ACCOUNTS); | ||||
| 
 | ||||
|   private DeletedAccounts deletedAccounts; | ||||
| 
 | ||||
|   @BeforeEach | ||||
|   void setUp() { | ||||
|     deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.DELETED_ACCOUNTS.tableName()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testPutFind() { | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(e164)); | ||||
| 
 | ||||
|     deletedAccounts.put(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(uuid), deletedAccounts.findUuid(e164)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testRemove() { | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(e164)); | ||||
| 
 | ||||
|     deletedAccounts.put(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(uuid), deletedAccounts.findUuid(e164)); | ||||
| 
 | ||||
|     deletedAccounts.remove(e164); | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(e164)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testFindE164() { | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findE164(UUID.randomUUID())); | ||||
| 
 | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     deletedAccounts.put(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(e164), deletedAccounts.findE164(uuid)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   void testFindUUID() { | ||||
|     final String e164 = "+18005551234"; | ||||
| 
 | ||||
|     assertEquals(Optional.empty(), deletedAccounts.findUuid(e164)); | ||||
| 
 | ||||
|     final UUID uuid = UUID.randomUUID(); | ||||
| 
 | ||||
|     deletedAccounts.put(uuid, e164); | ||||
| 
 | ||||
|     assertEquals(Optional.of(uuid), deletedAccounts.findUuid(e164)); | ||||
|   } | ||||
| } | ||||
|  | @ -63,21 +63,21 @@ public final class DynamoDbExtensionSchema { | |||
|         List.of()), | ||||
| 
 | ||||
|     DELETED_ACCOUNTS("deleted_accounts_test", | ||||
|         DeletedAccounts.KEY_ACCOUNT_E164, | ||||
|         Accounts.DELETED_ACCOUNTS_KEY_ACCOUNT_E164, | ||||
|         null, | ||||
|         List.of( | ||||
|             AttributeDefinition.builder() | ||||
|                 .attributeName(DeletedAccounts.KEY_ACCOUNT_E164) | ||||
|                 .attributeName(Accounts.DELETED_ACCOUNTS_KEY_ACCOUNT_E164) | ||||
|                 .attributeType(ScalarAttributeType.S).build(), | ||||
|             AttributeDefinition.builder() | ||||
|                 .attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID) | ||||
|                 .attributeName(Accounts.DELETED_ACCOUNTS_ATTR_ACCOUNT_UUID) | ||||
|                 .attributeType(ScalarAttributeType.B) | ||||
|                 .build()), | ||||
|         List.of( | ||||
|             GlobalSecondaryIndex.builder() | ||||
|                 .indexName(DeletedAccounts.UUID_TO_E164_INDEX_NAME) | ||||
|                 .indexName(Accounts.DELETED_ACCOUNTS_UUID_TO_E164_INDEX_NAME) | ||||
|                 .keySchema( | ||||
|                     KeySchemaElement.builder().attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID).keyType(KeyType.HASH).build() | ||||
|                     KeySchemaElement.builder().attributeName(Accounts.DELETED_ACCOUNTS_ATTR_ACCOUNT_UUID).keyType(KeyType.HASH).build() | ||||
|                 ) | ||||
|                 .projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build()) | ||||
|                 .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build()) | ||||
|  | @ -86,10 +86,10 @@ public final class DynamoDbExtensionSchema { | |||
|     ), | ||||
|    | ||||
|     DELETED_ACCOUNTS_LOCK("deleted_accounts_lock_test", | ||||
|         DeletedAccounts.KEY_ACCOUNT_E164, | ||||
|         AccountLockManager.KEY_ACCOUNT_E164, | ||||
|         null, | ||||
|         List.of(AttributeDefinition.builder() | ||||
|             .attributeName(DeletedAccounts.KEY_ACCOUNT_E164) | ||||
|             .attributeName(AccountLockManager.KEY_ACCOUNT_E164) | ||||
|             .attributeType(ScalarAttributeType.S).build()), | ||||
|         List.of(), List.of()), | ||||
|      | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Jon Chambers
						Jon Chambers