Consolidate account creation/directory updates into `AccountsManager`
This commit is contained in:
parent
917f667229
commit
8579190cdf
|
@ -442,7 +442,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
ReportMessageManager reportMessageManager = new ReportMessageManager(reportMessageDynamoDb, Metrics.globalRegistry);
|
ReportMessageManager reportMessageManager = new ReportMessageManager(reportMessageDynamoDb, Metrics.globalRegistry);
|
||||||
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, pushLatencyManager, reportMessageManager);
|
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, pushLatencyManager, reportMessageManager);
|
||||||
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts, deletedAccountsLockDynamoDbClient, config.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts, deletedAccountsLockDynamoDbClient, config.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, pendingAccountsManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
||||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||||
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(accountsManager, messagesManager);
|
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(accountsManager, messagesManager);
|
||||||
DispatchManager dispatchManager = new DispatchManager(pubSubClientFactory, Optional.of(deadLetterHandler));
|
DispatchManager dispatchManager = new DispatchManager(pubSubClientFactory, Optional.of(deadLetterHandler));
|
||||||
|
@ -476,7 +476,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
final List<DeletedAccountsDirectoryReconciler> deletedAccountsDirectoryReconcilers = new ArrayList<>();
|
final List<DeletedAccountsDirectoryReconciler> deletedAccountsDirectoryReconcilers = new ArrayList<>();
|
||||||
final List<AccountDatabaseCrawlerListener> accountDatabaseCrawlerListeners = new ArrayList<>();
|
final List<AccountDatabaseCrawlerListener> accountDatabaseCrawlerListeners = new ArrayList<>();
|
||||||
accountDatabaseCrawlerListeners.add(new PushFeedbackProcessor(accountsManager, directoryQueue));
|
accountDatabaseCrawlerListeners.add(new PushFeedbackProcessor(accountsManager));
|
||||||
accountDatabaseCrawlerListeners.add(new ActiveUserCounter(config.getMetricsFactory(), cacheCluster));
|
accountDatabaseCrawlerListeners.add(new ActiveUserCounter(config.getMetricsFactory(), cacheCluster));
|
||||||
for (DirectoryServerConfiguration directoryServerConfiguration : config.getDirectoryConfiguration().getDirectoryServerConfiguration()) {
|
for (DirectoryServerConfiguration directoryServerConfiguration : config.getDirectoryConfiguration().getDirectoryServerConfiguration()) {
|
||||||
final DirectoryReconciliationClient directoryReconciliationClient = new DirectoryReconciliationClient(directoryServerConfiguration);
|
final DirectoryReconciliationClient directoryReconciliationClient = new DirectoryReconciliationClient(directoryServerConfiguration);
|
||||||
|
@ -555,8 +555,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
webSocketEnvironment.jersey().register(new KeepAliveController(clientPresenceManager));
|
webSocketEnvironment.jersey().register(new KeepAliveController(clientPresenceManager));
|
||||||
|
|
||||||
// these should be common, but use @Auth DisabledPermittedAccount, which isn’t supported yet on websocket
|
// these should be common, but use @Auth DisabledPermittedAccount, which isn’t supported yet on websocket
|
||||||
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, usernamesManager, abusiveHostRules, rateLimiters, smsSender, directoryQueue, messagesManager, keysDynamoDb, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(), recaptchaClient, gcmSender, apnSender, backupCredentialsGenerator, verifyExperimentEnrollmentManager));
|
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, usernamesManager, abusiveHostRules, rateLimiters, smsSender, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(), recaptchaClient, gcmSender, apnSender, backupCredentialsGenerator, verifyExperimentEnrollmentManager));
|
||||||
environment.jersey().register(new KeysController(rateLimiters, keysDynamoDb, accountsManager, directoryQueue, preKeyRateLimiter, dynamicConfigurationManager, rateLimitChallengeManager));
|
environment.jersey().register(new KeysController(rateLimiters, keysDynamoDb, accountsManager, preKeyRateLimiter, dynamicConfigurationManager, rateLimitChallengeManager));
|
||||||
|
|
||||||
final List<Object> commonControllers = List.of(
|
final List<Object> commonControllers = List.of(
|
||||||
new AttachmentControllerV1(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getBucket()),
|
new AttachmentControllerV1(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getBucket()),
|
||||||
|
@ -564,7 +564,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()),
|
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()),
|
||||||
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations, isZkEnabled),
|
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations, isZkEnabled),
|
||||||
new ChallengeController(rateLimitChallengeManager),
|
new ChallengeController(rateLimitChallengeManager),
|
||||||
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keysDynamoDb, directoryQueue, rateLimiters, config.getMaxDevices()),
|
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keysDynamoDb, rateLimiters, config.getMaxDevices()),
|
||||||
new DirectoryController(directoryCredentialsGenerator),
|
new DirectoryController(directoryCredentialsGenerator),
|
||||||
new DonationController(donationExecutor, config.getDonationConfiguration()),
|
new DonationController(donationExecutor, config.getDonationConfiguration()),
|
||||||
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, unsealedSenderRateLimiter, apnFallbackManager, dynamicConfigurationManager, rateLimitChallengeManager, reportMessageManager, metricsCluster, declinedMessageReceiptExecutor),
|
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, unsealedSenderRateLimiter, apnFallbackManager, dynamicConfigurationManager, rateLimitChallengeManager, reportMessageManager, metricsCluster, declinedMessageReceiptExecutor),
|
||||||
|
@ -576,6 +576,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new SecureStorageController(storageCredentialsGenerator),
|
new SecureStorageController(storageCredentialsGenerator),
|
||||||
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(), config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(), config.getCdnConfiguration().getBucket())
|
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(), config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(), config.getCdnConfiguration().getBucket())
|
||||||
);
|
);
|
||||||
|
|
||||||
for (Object controller : commonControllers) {
|
for (Object controller : commonControllers) {
|
||||||
environment.jersey().register(controller);
|
environment.jersey().register(controller);
|
||||||
webSocketEnvironment.jersey().register(controller);
|
webSocketEnvironment.jersey().register(controller);
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
@ -68,15 +67,12 @@ import org.whispersystems.textsecuregcm.push.GcmMessage;
|
||||||
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
||||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||||
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
|
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.KeysDynamoDb;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
||||||
import org.whispersystems.textsecuregcm.util.Constants;
|
import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
|
@ -91,7 +87,6 @@ public class AccountController {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(AccountController.class);
|
private final Logger logger = LoggerFactory.getLogger(AccountController.class);
|
||||||
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||||
private final Meter newUserMeter = metricRegistry.meter(name(AccountController.class, "brand_new_user" ));
|
|
||||||
private final Meter blockedHostMeter = metricRegistry.meter(name(AccountController.class, "blocked_host" ));
|
private final Meter blockedHostMeter = metricRegistry.meter(name(AccountController.class, "blocked_host" ));
|
||||||
private final Meter filteredHostMeter = metricRegistry.meter(name(AccountController.class, "filtered_host" ));
|
private final Meter filteredHostMeter = metricRegistry.meter(name(AccountController.class, "filtered_host" ));
|
||||||
private final Meter rateLimitedHostMeter = metricRegistry.meter(name(AccountController.class, "rate_limited_host" ));
|
private final Meter rateLimitedHostMeter = metricRegistry.meter(name(AccountController.class, "rate_limited_host" ));
|
||||||
|
@ -119,9 +114,6 @@ public class AccountController {
|
||||||
private final AbusiveHostRules abusiveHostRules;
|
private final AbusiveHostRules abusiveHostRules;
|
||||||
private final RateLimiters rateLimiters;
|
private final RateLimiters rateLimiters;
|
||||||
private final SmsSender smsSender;
|
private final SmsSender smsSender;
|
||||||
private final DirectoryQueue directoryQueue;
|
|
||||||
private final MessagesManager messagesManager;
|
|
||||||
private final KeysDynamoDb keys;
|
|
||||||
private final DynamicConfigurationManager dynamicConfigurationManager;
|
private final DynamicConfigurationManager dynamicConfigurationManager;
|
||||||
private final TurnTokenGenerator turnTokenGenerator;
|
private final TurnTokenGenerator turnTokenGenerator;
|
||||||
private final Map<String, Integer> testDevices;
|
private final Map<String, Integer> testDevices;
|
||||||
|
@ -138,9 +130,6 @@ public class AccountController {
|
||||||
AbusiveHostRules abusiveHostRules,
|
AbusiveHostRules abusiveHostRules,
|
||||||
RateLimiters rateLimiters,
|
RateLimiters rateLimiters,
|
||||||
SmsSender smsSenderFactory,
|
SmsSender smsSenderFactory,
|
||||||
DirectoryQueue directoryQueue,
|
|
||||||
MessagesManager messagesManager,
|
|
||||||
KeysDynamoDb keys,
|
|
||||||
DynamicConfigurationManager dynamicConfigurationManager,
|
DynamicConfigurationManager dynamicConfigurationManager,
|
||||||
TurnTokenGenerator turnTokenGenerator,
|
TurnTokenGenerator turnTokenGenerator,
|
||||||
Map<String, Integer> testDevices,
|
Map<String, Integer> testDevices,
|
||||||
|
@ -156,9 +145,6 @@ public class AccountController {
|
||||||
this.abusiveHostRules = abusiveHostRules;
|
this.abusiveHostRules = abusiveHostRules;
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
this.smsSender = smsSenderFactory;
|
this.smsSender = smsSenderFactory;
|
||||||
this.directoryQueue = directoryQueue;
|
|
||||||
this.messagesManager = messagesManager;
|
|
||||||
this.keys = keys;
|
|
||||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||||
this.testDevices = testDevices;
|
this.testDevices = testDevices;
|
||||||
this.turnTokenGenerator = turnTokenGenerator;
|
this.turnTokenGenerator = turnTokenGenerator;
|
||||||
|
@ -318,6 +304,7 @@ public class AccountController {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO Remove this meter when external dependencies have been resolved
|
||||||
metricRegistry.meter(name(AccountController.class, "create", Util.getCountryCode(number))).mark();
|
metricRegistry.meter(name(AccountController.class, "create", Util.getCountryCode(number))).mark();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -392,7 +379,7 @@ public class AccountController {
|
||||||
throw new WebApplicationException(Response.status(409).build());
|
throw new WebApplicationException(Response.status(409).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
Account account = createAccount(number, password, signalAgent, accountAttributes);
|
Account account = accounts.create(number, password, signalAgent, accountAttributes);
|
||||||
|
|
||||||
{
|
{
|
||||||
metricRegistry.meter(name(AccountController.class, "verify", Util.getCountryCode(number))).mark();
|
metricRegistry.meter(name(AccountController.class, "verify", Util.getCountryCode(number))).mark();
|
||||||
|
@ -431,7 +418,6 @@ public class AccountController {
|
||||||
public void setGcmRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid GcmRegistrationId registrationId) {
|
public void setGcmRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid GcmRegistrationId registrationId) {
|
||||||
Account account = disabledPermittedAccount.getAccount();
|
Account account = disabledPermittedAccount.getAccount();
|
||||||
Device device = account.getAuthenticatedDevice().get();
|
Device device = account.getAuthenticatedDevice().get();
|
||||||
boolean wasAccountEnabled = account.isEnabled();
|
|
||||||
|
|
||||||
if (device.getGcmId() != null &&
|
if (device.getGcmId() != null &&
|
||||||
device.getGcmId().equals(registrationId.getGcmRegistrationId()))
|
device.getGcmId().equals(registrationId.getGcmRegistrationId()))
|
||||||
|
@ -439,16 +425,12 @@ public class AccountController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
account = accounts.updateDevice(account, device.getId(), d -> {
|
accounts.updateDevice(account, device.getId(), d -> {
|
||||||
d.setApnId(null);
|
d.setApnId(null);
|
||||||
d.setVoipApnId(null);
|
d.setVoipApnId(null);
|
||||||
d.setGcmId(registrationId.getGcmRegistrationId());
|
d.setGcmId(registrationId.getGcmRegistrationId());
|
||||||
d.setFetchesMessages(false);
|
d.setFetchesMessages(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!wasAccountEnabled && account.isEnabled()) {
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -458,12 +440,11 @@ public class AccountController {
|
||||||
Account account = disabledPermittedAccount.getAccount();
|
Account account = disabledPermittedAccount.getAccount();
|
||||||
Device device = account.getAuthenticatedDevice().get();
|
Device device = account.getAuthenticatedDevice().get();
|
||||||
|
|
||||||
account = accounts.updateDevice(account, device.getId(), d -> {
|
accounts.updateDevice(account, device.getId(), d -> {
|
||||||
d.setGcmId(null);
|
d.setGcmId(null);
|
||||||
d.setFetchesMessages(false);
|
d.setFetchesMessages(false);
|
||||||
d.setUserAgent("OWA");
|
d.setUserAgent("OWA");
|
||||||
});
|
});
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -473,18 +454,13 @@ public class AccountController {
|
||||||
public void setApnRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid ApnRegistrationId registrationId) {
|
public void setApnRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid ApnRegistrationId registrationId) {
|
||||||
Account account = disabledPermittedAccount.getAccount();
|
Account account = disabledPermittedAccount.getAccount();
|
||||||
Device device = account.getAuthenticatedDevice().get();
|
Device device = account.getAuthenticatedDevice().get();
|
||||||
boolean wasAccountEnabled = account.isEnabled();
|
|
||||||
|
|
||||||
account = accounts.updateDevice(account, device.getId(), d -> {
|
accounts.updateDevice(account, device.getId(), d -> {
|
||||||
d.setApnId(registrationId.getApnRegistrationId());
|
d.setApnId(registrationId.getApnRegistrationId());
|
||||||
d.setVoipApnId(registrationId.getVoipRegistrationId());
|
d.setVoipApnId(registrationId.getVoipRegistrationId());
|
||||||
d.setGcmId(null);
|
d.setGcmId(null);
|
||||||
d.setFetchesMessages(false);
|
d.setFetchesMessages(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!wasAccountEnabled && account.isEnabled()) {
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -503,7 +479,6 @@ public class AccountController {
|
||||||
d.setUserAgent("OWP");
|
d.setUserAgent("OWP");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -577,7 +552,7 @@ public class AccountController {
|
||||||
Account account = disabledPermittedAccount.getAccount();
|
Account account = disabledPermittedAccount.getAccount();
|
||||||
long deviceId = account.getAuthenticatedDevice().get().getId();
|
long deviceId = account.getAuthenticatedDevice().get().getId();
|
||||||
|
|
||||||
account = accounts.update(account, a-> {
|
accounts.update(account, a-> {
|
||||||
|
|
||||||
a.getDevice(deviceId).ifPresent(d -> {
|
a.getDevice(deviceId).ifPresent(d -> {
|
||||||
d.setFetchesMessages(attributes.getFetchesMessages());
|
d.setFetchesMessages(attributes.getFetchesMessages());
|
||||||
|
@ -588,19 +563,12 @@ public class AccountController {
|
||||||
d.setUserAgent(userAgent);
|
d.setUserAgent(userAgent);
|
||||||
});
|
});
|
||||||
|
|
||||||
setAccountRegistrationLockFromAttributes(a, attributes);
|
a.setRegistrationLockFromAttributes(attributes);
|
||||||
|
|
||||||
a.setUnidentifiedAccessKey(attributes.getUnidentifiedAccessKey());
|
a.setUnidentifiedAccessKey(attributes.getUnidentifiedAccessKey());
|
||||||
a.setUnrestrictedUnidentifiedAccess(attributes.isUnrestrictedUnidentifiedAccess());
|
a.setUnrestrictedUnidentifiedAccess(attributes.isUnrestrictedUnidentifiedAccess());
|
||||||
a.setDiscoverableByPhoneNumber(attributes.isDiscoverableByPhoneNumber());
|
a.setDiscoverableByPhoneNumber(attributes.isDiscoverableByPhoneNumber());
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final boolean hasDiscoverabilityChange = (account.isDiscoverableByPhoneNumber() != attributes.isDiscoverableByPhoneNumber());
|
|
||||||
|
|
||||||
if (hasDiscoverabilityChange) {
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -752,57 +720,6 @@ public class AccountController {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account createAccount(String number, String password, String signalAgent, AccountAttributes accountAttributes) {
|
|
||||||
Optional<Account> maybeExistingAccount = accounts.get(number);
|
|
||||||
|
|
||||||
Device device = new Device();
|
|
||||||
device.setId(Device.MASTER_ID);
|
|
||||||
device.setAuthenticationCredentials(new AuthenticationCredentials(password));
|
|
||||||
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
|
||||||
device.setRegistrationId(accountAttributes.getRegistrationId());
|
|
||||||
device.setName(accountAttributes.getName());
|
|
||||||
device.setCapabilities(accountAttributes.getCapabilities());
|
|
||||||
device.setCreated(System.currentTimeMillis());
|
|
||||||
device.setLastSeen(Util.todayInMillis());
|
|
||||||
device.setUserAgent(signalAgent);
|
|
||||||
|
|
||||||
Account account = new Account();
|
|
||||||
account.setNumber(number);
|
|
||||||
account.setUuid(UUID.randomUUID());
|
|
||||||
account.addDevice(device);
|
|
||||||
setAccountRegistrationLockFromAttributes(account, accountAttributes);
|
|
||||||
account.setUnidentifiedAccessKey(accountAttributes.getUnidentifiedAccessKey());
|
|
||||||
account.setUnrestrictedUnidentifiedAccess(accountAttributes.isUnrestrictedUnidentifiedAccess());
|
|
||||||
account.setDiscoverableByPhoneNumber(accountAttributes.isDiscoverableByPhoneNumber());
|
|
||||||
|
|
||||||
if (accounts.create(account)) {
|
|
||||||
newUserMeter.mark();
|
|
||||||
}
|
|
||||||
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
|
|
||||||
maybeExistingAccount.ifPresent(definitelyExistingAccount -> {
|
|
||||||
messagesManager.clear(definitelyExistingAccount.getUuid());
|
|
||||||
keys.delete(definitelyExistingAccount);
|
|
||||||
});
|
|
||||||
|
|
||||||
pendingAccounts.remove(number);
|
|
||||||
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAccountRegistrationLockFromAttributes(Account account, @Valid AccountAttributes attributes) {
|
|
||||||
if (!Util.isEmpty(attributes.getPin())) {
|
|
||||||
account.setPin(attributes.getPin());
|
|
||||||
} else if (!Util.isEmpty(attributes.getRegistrationLock())) {
|
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials(attributes.getRegistrationLock());
|
|
||||||
account.setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt());
|
|
||||||
} else {
|
|
||||||
account.setPin(null);
|
|
||||||
account.setRegistrationLock(null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting protected
|
@VisibleForTesting protected
|
||||||
VerificationCode generateVerificationCode(String number) {
|
VerificationCode generateVerificationCode(String number) {
|
||||||
if (testDevices.containsKey(number)) {
|
if (testDevices.containsKey(number)) {
|
||||||
|
|
|
@ -62,13 +62,11 @@ public class DeviceController {
|
||||||
private final KeysDynamoDb keys;
|
private final KeysDynamoDb keys;
|
||||||
private final RateLimiters rateLimiters;
|
private final RateLimiters rateLimiters;
|
||||||
private final Map<String, Integer> maxDeviceConfiguration;
|
private final Map<String, Integer> maxDeviceConfiguration;
|
||||||
private final DirectoryQueue directoryQueue;
|
|
||||||
|
|
||||||
public DeviceController(StoredVerificationCodeManager pendingDevices,
|
public DeviceController(StoredVerificationCodeManager pendingDevices,
|
||||||
AccountsManager accounts,
|
AccountsManager accounts,
|
||||||
MessagesManager messages,
|
MessagesManager messages,
|
||||||
KeysDynamoDb keys,
|
KeysDynamoDb keys,
|
||||||
DirectoryQueue directoryQueue,
|
|
||||||
RateLimiters rateLimiters,
|
RateLimiters rateLimiters,
|
||||||
Map<String, Integer> maxDeviceConfiguration)
|
Map<String, Integer> maxDeviceConfiguration)
|
||||||
{
|
{
|
||||||
|
@ -76,7 +74,6 @@ public class DeviceController {
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
this.keys = keys;
|
this.keys = keys;
|
||||||
this.directoryQueue = directoryQueue;
|
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
this.maxDeviceConfiguration = maxDeviceConfiguration;
|
this.maxDeviceConfiguration = maxDeviceConfiguration;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +102,6 @@ public class DeviceController {
|
||||||
|
|
||||||
messages.clear(account.getUuid(), deviceId);
|
messages.clear(account.getUuid(), deviceId);
|
||||||
account = accounts.update(account, a -> a.removeDevice(deviceId));
|
account = accounts.update(account, a -> a.removeDevice(deviceId));
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
keys.delete(account, deviceId);
|
keys.delete(account, deviceId);
|
||||||
// ensure any messages that came in after the first clear() are also removed
|
// ensure any messages that came in after the first clear() are also removed
|
||||||
messages.clear(account.getUuid(), deviceId);
|
messages.clear(account.getUuid(), deviceId);
|
||||||
|
|
|
@ -55,7 +55,6 @@ public class KeysController {
|
||||||
private final RateLimiters rateLimiters;
|
private final RateLimiters rateLimiters;
|
||||||
private final KeysDynamoDb keysDynamoDb;
|
private final KeysDynamoDb keysDynamoDb;
|
||||||
private final AccountsManager accounts;
|
private final AccountsManager accounts;
|
||||||
private final DirectoryQueue directoryQueue;
|
|
||||||
private final PreKeyRateLimiter preKeyRateLimiter;
|
private final PreKeyRateLimiter preKeyRateLimiter;
|
||||||
|
|
||||||
private final DynamicConfigurationManager dynamicConfigurationManager;
|
private final DynamicConfigurationManager dynamicConfigurationManager;
|
||||||
|
@ -69,13 +68,12 @@ public class KeysController {
|
||||||
private static final String PREKEY_TARGET_IDENTIFIER_TAG_NAME = "identifierType";
|
private static final String PREKEY_TARGET_IDENTIFIER_TAG_NAME = "identifierType";
|
||||||
|
|
||||||
public KeysController(RateLimiters rateLimiters, KeysDynamoDb keysDynamoDb, AccountsManager accounts,
|
public KeysController(RateLimiters rateLimiters, KeysDynamoDb keysDynamoDb, AccountsManager accounts,
|
||||||
DirectoryQueue directoryQueue, PreKeyRateLimiter preKeyRateLimiter,
|
PreKeyRateLimiter preKeyRateLimiter,
|
||||||
DynamicConfigurationManager dynamicConfigurationManager,
|
DynamicConfigurationManager dynamicConfigurationManager,
|
||||||
RateLimitChallengeManager rateLimitChallengeManager) {
|
RateLimitChallengeManager rateLimitChallengeManager) {
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
this.keysDynamoDb = keysDynamoDb;
|
this.keysDynamoDb = keysDynamoDb;
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
this.directoryQueue = directoryQueue;
|
|
||||||
this.preKeyRateLimiter = preKeyRateLimiter;
|
this.preKeyRateLimiter = preKeyRateLimiter;
|
||||||
|
|
||||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||||
|
@ -100,7 +98,6 @@ public class KeysController {
|
||||||
public void setKeys(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid PreKeyState preKeys) {
|
public void setKeys(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid PreKeyState preKeys) {
|
||||||
Account account = disabledPermittedAccount.getAccount();
|
Account account = disabledPermittedAccount.getAccount();
|
||||||
Device device = account.getAuthenticatedDevice().get();
|
Device device = account.getAuthenticatedDevice().get();
|
||||||
boolean wasAccountEnabled = account.isEnabled();
|
|
||||||
boolean updateAccount = false;
|
boolean updateAccount = false;
|
||||||
|
|
||||||
if (!preKeys.getSignedPreKey().equals(device.getSignedPreKey())) {
|
if (!preKeys.getSignedPreKey().equals(device.getSignedPreKey())) {
|
||||||
|
@ -116,10 +113,6 @@ public class KeysController {
|
||||||
a.getDevice(device.getId()).ifPresent(d -> d.setSignedPreKey(preKeys.getSignedPreKey()));
|
a.getDevice(device.getId()).ifPresent(d -> d.setSignedPreKey(preKeys.getSignedPreKey()));
|
||||||
a.setIdentityKey(preKeys.getIdentityKey());
|
a.setIdentityKey(preKeys.getIdentityKey());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!wasAccountEnabled && account.isEnabled()) {
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keysDynamoDb.store(account, device.getId(), preKeys.getPreKeys());
|
keysDynamoDb.store(account, device.getId(), preKeys.getPreKeys());
|
||||||
|
@ -198,14 +191,9 @@ public class KeysController {
|
||||||
@Path("/signed")
|
@Path("/signed")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public void setSignedKey(@Auth Account account, @Valid SignedPreKey signedPreKey) {
|
public void setSignedKey(@Auth Account account, @Valid SignedPreKey signedPreKey) {
|
||||||
Device device = account.getAuthenticatedDevice().get();
|
Device device = account.getAuthenticatedDevice().get();
|
||||||
boolean wasAccountEnabled = account.isEnabled();
|
|
||||||
|
|
||||||
account = accounts.updateDevice(account, device.getId(), d -> d.setSignedPreKey(signedPreKey));
|
accounts.updateDevice(account, device.getId(), d -> d.setSignedPreKey(signedPreKey));
|
||||||
|
|
||||||
if (!wasAccountEnabled && account.isEnabled()) {
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
|
|
@ -61,11 +61,15 @@ public class DirectoryQueue {
|
||||||
this.sqs = sqs;
|
this.sqs = sqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshRegisteredUser(final Account account) {
|
public boolean isDiscoverable(final Account account) {
|
||||||
refreshRegisteredUsers(List.of(account));
|
return account.isEnabled() && account.isDiscoverableByPhoneNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshRegisteredUsers(final List<Account> accounts) {
|
public void refreshAccount(final Account account) {
|
||||||
|
refreshAccounts(List.of(account));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshAccounts(final List<Account> accounts) {
|
||||||
final List<Pair<Account, String>> accountsAndActions = accounts.stream()
|
final List<Pair<Account, String>> accountsAndActions = accounts.stream()
|
||||||
.map(account -> new Pair<>(account, account.isEnabled() && account.isDiscoverableByPhoneNumber() ? "add" : "delete"))
|
.map(account -> new Pair<>(account, account.isEnabled() && account.isDiscoverableByPhoneNumber() ? "add" : "delete"))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
|
@ -17,7 +17,10 @@ import javax.security.auth.Subject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||||
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
public class Account implements Principal {
|
public class Account implements Principal {
|
||||||
|
|
||||||
|
@ -312,6 +315,18 @@ public class Account implements Principal {
|
||||||
this.pin = pin;
|
this.pin = pin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRegistrationLockFromAttributes(final AccountAttributes attributes) {
|
||||||
|
if (!Util.isEmpty(attributes.getPin())) {
|
||||||
|
setPin(attributes.getPin());
|
||||||
|
} else if (!Util.isEmpty(attributes.getRegistrationLock())) {
|
||||||
|
AuthenticationCredentials credentials = new AuthenticationCredentials(attributes.getRegistrationLock());
|
||||||
|
setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt());
|
||||||
|
} else {
|
||||||
|
setPin(null);
|
||||||
|
setRegistrationLock(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setRegistrationLock(String registrationLock, String registrationLockSalt) {
|
public void setRegistrationLock(String registrationLock, String registrationLockSalt) {
|
||||||
requireNotStale();
|
requireNotStale();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.storage;
|
||||||
|
|
||||||
import static com.codahale.metrics.MetricRegistry.name;
|
import static com.codahale.metrics.MetricRegistry.name;
|
||||||
|
|
||||||
|
import com.codahale.metrics.Meter;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.codahale.metrics.SharedMetricRegistries;
|
import com.codahale.metrics.SharedMetricRegistries;
|
||||||
import com.codahale.metrics.Timer;
|
import com.codahale.metrics.Timer;
|
||||||
|
@ -17,6 +18,7 @@ import io.lettuce.core.RedisException;
|
||||||
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||||
import io.micrometer.core.instrument.Counter;
|
import io.micrometer.core.instrument.Counter;
|
||||||
import io.micrometer.core.instrument.Metrics;
|
import io.micrometer.core.instrument.Metrics;
|
||||||
|
import io.micrometer.core.instrument.Tags;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -34,6 +36,9 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
|
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||||
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
|
@ -52,11 +57,16 @@ public class AccountsManager {
|
||||||
private static final Timer getByUuidTimer = metricRegistry.timer(name(AccountsManager.class, "getByUuid" ));
|
private static final Timer getByUuidTimer = metricRegistry.timer(name(AccountsManager.class, "getByUuid" ));
|
||||||
private static final Timer deleteTimer = metricRegistry.timer(name(AccountsManager.class, "delete"));
|
private static final Timer deleteTimer = metricRegistry.timer(name(AccountsManager.class, "delete"));
|
||||||
|
|
||||||
|
// TODO Remove this meter when external dependencies have been resolved
|
||||||
|
// Note that this is deliberately namespaced to `AccountController` for metric continuity.
|
||||||
|
private static final Meter newUserMeter = metricRegistry.meter(name(AccountController.class, "brand_new_user"));
|
||||||
|
|
||||||
private static final Timer redisSetTimer = metricRegistry.timer(name(AccountsManager.class, "redisSet" ));
|
private static final Timer redisSetTimer = metricRegistry.timer(name(AccountsManager.class, "redisSet" ));
|
||||||
private static final Timer redisNumberGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisNumberGet"));
|
private static final Timer redisNumberGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisNumberGet"));
|
||||||
private static final Timer redisUuidGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisUuidGet" ));
|
private static final Timer redisUuidGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisUuidGet" ));
|
||||||
private static final Timer redisDeleteTimer = metricRegistry.timer(name(AccountsManager.class, "redisDelete" ));
|
private static final Timer redisDeleteTimer = metricRegistry.timer(name(AccountsManager.class, "redisDelete" ));
|
||||||
|
|
||||||
|
private static final String CREATE_COUNTER_NAME = name(AccountsManager.class, "createCounter");
|
||||||
private static final String DELETE_COUNTER_NAME = name(AccountsManager.class, "deleteCounter");
|
private static final String DELETE_COUNTER_NAME = name(AccountsManager.class, "deleteCounter");
|
||||||
private static final String DELETE_ERROR_COUNTER_NAME = name(AccountsManager.class, "deleteError");
|
private static final String DELETE_ERROR_COUNTER_NAME = name(AccountsManager.class, "deleteError");
|
||||||
private static final String COUNTRY_CODE_TAG_NAME = "country";
|
private static final String COUNTRY_CODE_TAG_NAME = "country";
|
||||||
|
@ -77,6 +87,7 @@ public class AccountsManager {
|
||||||
private final MessagesManager messagesManager;
|
private final MessagesManager messagesManager;
|
||||||
private final UsernamesManager usernamesManager;
|
private final UsernamesManager usernamesManager;
|
||||||
private final ProfilesManager profilesManager;
|
private final ProfilesManager profilesManager;
|
||||||
|
private final StoredVerificationCodeManager pendingAccounts;
|
||||||
private final SecureStorageClient secureStorageClient;
|
private final SecureStorageClient secureStorageClient;
|
||||||
private final SecureBackupClient secureBackupClient;
|
private final SecureBackupClient secureBackupClient;
|
||||||
private final ObjectMapper mapper;
|
private final ObjectMapper mapper;
|
||||||
|
@ -102,7 +113,9 @@ public class AccountsManager {
|
||||||
final DeletedAccountsManager deletedAccountsManager,
|
final DeletedAccountsManager deletedAccountsManager,
|
||||||
final DirectoryQueue directoryQueue,
|
final DirectoryQueue directoryQueue,
|
||||||
final KeysDynamoDb keysDynamoDb, final MessagesManager messagesManager, final UsernamesManager usernamesManager,
|
final KeysDynamoDb keysDynamoDb, final MessagesManager messagesManager, final UsernamesManager usernamesManager,
|
||||||
final ProfilesManager profilesManager, final SecureStorageClient secureStorageClient,
|
final ProfilesManager profilesManager,
|
||||||
|
final StoredVerificationCodeManager pendingAccounts,
|
||||||
|
final SecureStorageClient secureStorageClient,
|
||||||
final SecureBackupClient secureBackupClient,
|
final SecureBackupClient secureBackupClient,
|
||||||
final ExperimentEnrollmentManager experimentEnrollmentManager,
|
final ExperimentEnrollmentManager experimentEnrollmentManager,
|
||||||
final DynamicConfigurationManager dynamicConfigurationManager) {
|
final DynamicConfigurationManager dynamicConfigurationManager) {
|
||||||
|
@ -115,6 +128,7 @@ public class AccountsManager {
|
||||||
this.messagesManager = messagesManager;
|
this.messagesManager = messagesManager;
|
||||||
this.usernamesManager = usernamesManager;
|
this.usernamesManager = usernamesManager;
|
||||||
this.profilesManager = profilesManager;
|
this.profilesManager = profilesManager;
|
||||||
|
this.pendingAccounts = pendingAccounts;
|
||||||
this.secureStorageClient = secureStorageClient;
|
this.secureStorageClient = secureStorageClient;
|
||||||
this.secureBackupClient = secureBackupClient;
|
this.secureBackupClient = secureBackupClient;
|
||||||
this.mapper = SystemMapper.getMapper();
|
this.mapper = SystemMapper.getMapper();
|
||||||
|
@ -126,8 +140,34 @@ public class AccountsManager {
|
||||||
this.experimentEnrollmentManager = experimentEnrollmentManager;
|
this.experimentEnrollmentManager = experimentEnrollmentManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean create(Account account) {
|
public Account create(final String number,
|
||||||
|
final String password,
|
||||||
|
final String signalAgent,
|
||||||
|
final AccountAttributes accountAttributes) {
|
||||||
|
|
||||||
try (Timer.Context ignored = createTimer.time()) {
|
try (Timer.Context ignored = createTimer.time()) {
|
||||||
|
Optional<Account> maybeExistingAccount = get(number);
|
||||||
|
|
||||||
|
Device device = new Device();
|
||||||
|
device.setId(Device.MASTER_ID);
|
||||||
|
device.setAuthenticationCredentials(new AuthenticationCredentials(password));
|
||||||
|
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
||||||
|
device.setRegistrationId(accountAttributes.getRegistrationId());
|
||||||
|
device.setName(accountAttributes.getName());
|
||||||
|
device.setCapabilities(accountAttributes.getCapabilities());
|
||||||
|
device.setCreated(System.currentTimeMillis());
|
||||||
|
device.setLastSeen(Util.todayInMillis());
|
||||||
|
device.setUserAgent(signalAgent);
|
||||||
|
|
||||||
|
Account account = new Account();
|
||||||
|
account.setNumber(number);
|
||||||
|
account.setUuid(UUID.randomUUID());
|
||||||
|
account.addDevice(device);
|
||||||
|
account.setRegistrationLockFromAttributes(accountAttributes);
|
||||||
|
account.setUnidentifiedAccessKey(accountAttributes.getUnidentifiedAccessKey());
|
||||||
|
account.setUnrestrictedUnidentifiedAccess(accountAttributes.isUnrestrictedUnidentifiedAccess());
|
||||||
|
account.setDiscoverableByPhoneNumber(accountAttributes.isDiscoverableByPhoneNumber());
|
||||||
|
|
||||||
final UUID originalUuid = account.getUuid();
|
final UUID originalUuid = account.getUuid();
|
||||||
boolean freshUser = databaseCreate(account);
|
boolean freshUser = databaseCreate(account);
|
||||||
|
|
||||||
|
@ -165,12 +205,36 @@ public class AccountsManager {
|
||||||
|
|
||||||
redisSet(account);
|
redisSet(account);
|
||||||
|
|
||||||
return freshUser;
|
final Tags tags;
|
||||||
|
|
||||||
|
if (freshUser) {
|
||||||
|
tags = Tags.of("type", "new");
|
||||||
|
} else {
|
||||||
|
tags = Tags.of("type", "reregister");
|
||||||
|
}
|
||||||
|
|
||||||
|
Metrics.counter(CREATE_COUNTER_NAME, tags).increment();
|
||||||
|
|
||||||
|
if (!account.isDiscoverableByPhoneNumber()) {
|
||||||
|
// The newly-created account has explicitly opted out of discoverability
|
||||||
|
directoryQueue.deleteAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
maybeExistingAccount.ifPresent(definitelyExistingAccount -> {
|
||||||
|
messagesManager.clear(definitelyExistingAccount.getUuid());
|
||||||
|
keysDynamoDb.delete(definitelyExistingAccount);
|
||||||
|
});
|
||||||
|
|
||||||
|
pendingAccounts.remove(number);
|
||||||
|
|
||||||
|
return account;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account update(Account account, Consumer<Account> updater) {
|
public Account update(Account account, Consumer<Account> updater) {
|
||||||
|
|
||||||
|
final boolean wasDiscoverableBeforeUpdate = directoryQueue.isDiscoverable(account);
|
||||||
|
|
||||||
final Account updatedAccount;
|
final Account updatedAccount;
|
||||||
|
|
||||||
try (Timer.Context ignored = updateTimer.time()) {
|
try (Timer.Context ignored = updateTimer.time()) {
|
||||||
|
@ -212,6 +276,12 @@ public class AccountsManager {
|
||||||
redisSet(updatedAccount);
|
redisSet(updatedAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final boolean isDiscoverableAfterUpdate = directoryQueue.isDiscoverable(updatedAccount);
|
||||||
|
|
||||||
|
if (wasDiscoverableBeforeUpdate != isDiscoverableAfterUpdate) {
|
||||||
|
directoryQueue.refreshAccount(updatedAccount);
|
||||||
|
}
|
||||||
|
|
||||||
return updatedAccount;
|
return updatedAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,9 @@ public class PushFeedbackProcessor extends AccountDatabaseCrawlerListener {
|
||||||
private final Meter recovered = metricRegistry.meter(name(getClass(), "unregistered", "recovered"));
|
private final Meter recovered = metricRegistry.meter(name(getClass(), "unregistered", "recovered"));
|
||||||
|
|
||||||
private final AccountsManager accountsManager;
|
private final AccountsManager accountsManager;
|
||||||
private final DirectoryQueue directoryQueue;
|
|
||||||
|
|
||||||
public PushFeedbackProcessor(AccountsManager accountsManager, DirectoryQueue directoryQueue) {
|
public PushFeedbackProcessor(AccountsManager accountsManager) {
|
||||||
this.accountsManager = accountsManager;
|
this.accountsManager = accountsManager;
|
||||||
this.directoryQueue = directoryQueue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,8 +40,6 @@ public class PushFeedbackProcessor extends AccountDatabaseCrawlerListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCrawlChunk(Optional<UUID> fromUuid, List<Account> chunkAccounts) {
|
protected void onCrawlChunk(Optional<UUID> fromUuid, List<Account> chunkAccounts) {
|
||||||
final List<Account> directoryUpdateAccounts = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Account account : chunkAccounts) {
|
for (Account account : chunkAccounts) {
|
||||||
boolean update = false;
|
boolean update = false;
|
||||||
|
|
||||||
|
@ -62,7 +58,7 @@ public class PushFeedbackProcessor extends AccountDatabaseCrawlerListener {
|
||||||
if (update) {
|
if (update) {
|
||||||
// fetch a new version, since the chunk is shared and implicitly read-only
|
// fetch a new version, since the chunk is shared and implicitly read-only
|
||||||
accountsManager.get(account.getUuid()).ifPresent(accountToUpdate -> {
|
accountsManager.get(account.getUuid()).ifPresent(accountToUpdate -> {
|
||||||
Account updatedAccount = accountsManager.update(accountToUpdate, a -> {
|
accountsManager.update(accountToUpdate, a -> {
|
||||||
for (Device device : a.getDevices()) {
|
for (Device device : a.getDevices()) {
|
||||||
if (deviceNeedsUpdate(device)) {
|
if (deviceNeedsUpdate(device)) {
|
||||||
if (deviceExpired(device)) {
|
if (deviceExpired(device)) {
|
||||||
|
@ -85,14 +81,9 @@ public class PushFeedbackProcessor extends AccountDatabaseCrawlerListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
directoryUpdateAccounts.add(updatedAccount);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!directoryUpdateAccounts.isEmpty()) {
|
|
||||||
directoryQueue.refreshRegisteredUsers(directoryUpdateAccounts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean deviceNeedsUpdate(final Device device) {
|
private boolean deviceNeedsUpdate(final Device device) {
|
||||||
|
|
|
@ -56,8 +56,10 @@ import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
||||||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ReservedUsernames;
|
import org.whispersystems.textsecuregcm.storage.ReservedUsernames;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Usernames;
|
import org.whispersystems.textsecuregcm.storage.Usernames;
|
||||||
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.VerificationCodeStore;
|
||||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
@ -137,6 +139,8 @@ public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfigura
|
||||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||||
DynamoDbClient migrationRetryAccountsDynamoDb = DynamoDbFromConfig.client(configuration.getMigrationRetryAccountsDynamoDbConfiguration(),
|
DynamoDbClient migrationRetryAccountsDynamoDb = DynamoDbFromConfig.client(configuration.getMigrationRetryAccountsDynamoDbConfiguration(),
|
||||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||||
|
DynamoDbClient pendingAccountsDynamoDbClient = DynamoDbFromConfig.client(configuration.getPendingAccountsDynamoDbConfiguration(),
|
||||||
|
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||||
|
|
||||||
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
|
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
|
||||||
.withRegion(configuration.getDeletedAccountsLockDynamoDbConfiguration().getRegion())
|
.withRegion(configuration.getDeletedAccountsLockDynamoDbConfiguration().getRegion())
|
||||||
|
@ -148,6 +152,7 @@ public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfigura
|
||||||
DeletedAccounts deletedAccounts = new DeletedAccounts(deletedAccountsDynamoDbClient, configuration.getDeletedAccountsDynamoDbConfiguration().getTableName(), configuration.getDeletedAccountsDynamoDbConfiguration().getNeedsReconciliationIndexName());
|
DeletedAccounts deletedAccounts = new DeletedAccounts(deletedAccountsDynamoDbClient, configuration.getDeletedAccountsDynamoDbConfiguration().getTableName(), configuration.getDeletedAccountsDynamoDbConfiguration().getNeedsReconciliationIndexName());
|
||||||
MigrationDeletedAccounts migrationDeletedAccounts = new MigrationDeletedAccounts(migrationDeletedAccountsDynamoDb, configuration.getMigrationDeletedAccountsDynamoDbConfiguration().getTableName());
|
MigrationDeletedAccounts migrationDeletedAccounts = new MigrationDeletedAccounts(migrationDeletedAccountsDynamoDb, configuration.getMigrationDeletedAccountsDynamoDbConfiguration().getTableName());
|
||||||
MigrationRetryAccounts migrationRetryAccounts = new MigrationRetryAccounts(migrationRetryAccountsDynamoDb, configuration.getMigrationRetryAccountsDynamoDbConfiguration().getTableName());
|
MigrationRetryAccounts migrationRetryAccounts = new MigrationRetryAccounts(migrationRetryAccountsDynamoDb, configuration.getMigrationRetryAccountsDynamoDbConfiguration().getTableName());
|
||||||
|
VerificationCodeStore pendingAccounts = new VerificationCodeStore(pendingAccountsDynamoDbClient, configuration.getPendingAccountsDynamoDbConfiguration().getTableName());
|
||||||
|
|
||||||
Accounts accounts = new Accounts(accountDatabase);
|
Accounts accounts = new Accounts(accountDatabase);
|
||||||
AccountsDynamoDb accountsDynamoDb = new AccountsDynamoDb(accountsDynamoDbClient, accountsDynamoDbAsyncClient, accountsDynamoDbMigrationThreadPool, configuration.getAccountsDynamoDbConfiguration().getTableName(), configuration.getAccountsDynamoDbConfiguration().getPhoneNumberTableName(), migrationDeletedAccounts, migrationRetryAccounts);
|
AccountsDynamoDb accountsDynamoDb = new AccountsDynamoDb(accountsDynamoDbClient, accountsDynamoDbAsyncClient, accountsDynamoDbMigrationThreadPool, configuration.getAccountsDynamoDbConfiguration().getTableName(), configuration.getAccountsDynamoDbConfiguration().getPhoneNumberTableName(), migrationDeletedAccounts, migrationRetryAccounts);
|
||||||
|
@ -170,7 +175,8 @@ public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfigura
|
||||||
ReportMessageManager reportMessageManager = new ReportMessageManager(reportMessageDynamoDb, Metrics.globalRegistry);
|
ReportMessageManager reportMessageManager = new ReportMessageManager(reportMessageDynamoDb, Metrics.globalRegistry);
|
||||||
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, pushLatencyManager, reportMessageManager);
|
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, pushLatencyManager, reportMessageManager);
|
||||||
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts, deletedAccountsLockDynamoDbClient, configuration.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts, deletedAccountsLockDynamoDbClient, configuration.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
StoredVerificationCodeManager pendingAccountsManager = new StoredVerificationCodeManager(pendingAccounts);
|
||||||
|
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, pendingAccountsManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
||||||
|
|
||||||
for (String user: users) {
|
for (String user: users) {
|
||||||
Optional<Account> account = accountsManager.get(user);
|
Optional<Account> account = accountsManager.get(user);
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class DirectoryQueueTest {
|
||||||
when(account.isEnabled()).thenReturn(accountEnabled);
|
when(account.isEnabled()).thenReturn(accountEnabled);
|
||||||
when(account.isDiscoverableByPhoneNumber()).thenReturn(accountDiscoverableByPhoneNumber);
|
when(account.isDiscoverableByPhoneNumber()).thenReturn(accountDiscoverableByPhoneNumber);
|
||||||
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
directoryQueue.refreshAccount(account);
|
||||||
|
|
||||||
final ArgumentCaptor<SendMessageBatchRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageBatchRequest.class);
|
final ArgumentCaptor<SendMessageBatchRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageBatchRequest.class);
|
||||||
verify(sqs).sendMessageBatch(requestCaptor.capture());
|
verify(sqs).sendMessageBatch(requestCaptor.capture());
|
||||||
|
@ -68,7 +68,7 @@ public class DirectoryQueueTest {
|
||||||
when(undiscoverableAccount.isEnabled()).thenReturn(true);
|
when(undiscoverableAccount.isEnabled()).thenReturn(true);
|
||||||
when(undiscoverableAccount.isDiscoverableByPhoneNumber()).thenReturn(false);
|
when(undiscoverableAccount.isDiscoverableByPhoneNumber()).thenReturn(false);
|
||||||
|
|
||||||
directoryQueue.refreshRegisteredUsers(List.of(discoverableAccount, undiscoverableAccount));
|
directoryQueue.refreshAccounts(List.of(discoverableAccount, undiscoverableAccount));
|
||||||
|
|
||||||
final ArgumentCaptor<SendMessageBatchRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageBatchRequest.class);
|
final ArgumentCaptor<SendMessageBatchRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageBatchRequest.class);
|
||||||
verify(sqs).sendMessageBatch(requestCaptor.capture());
|
verify(sqs).sendMessageBatch(requestCaptor.capture());
|
||||||
|
@ -97,7 +97,7 @@ public class DirectoryQueueTest {
|
||||||
when(account.isEnabled()).thenReturn(true);
|
when(account.isEnabled()).thenReturn(true);
|
||||||
when(account.isDiscoverableByPhoneNumber()).thenReturn(true);
|
when(account.isDiscoverableByPhoneNumber()).thenReturn(true);
|
||||||
|
|
||||||
directoryQueue.refreshRegisteredUser(account);
|
directoryQueue.refreshAccount(account);
|
||||||
|
|
||||||
final ArgumentCaptor<SendMessageBatchRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageBatchRequest.class);
|
final ArgumentCaptor<SendMessageBatchRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageBatchRequest.class);
|
||||||
verify(sqs, times(2)).sendMessageBatch(requestCaptor.capture());
|
verify(sqs, times(2)).sendMessageBatch(requestCaptor.capture());
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicAccountsDynamoDbMigrationConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicAccountsDynamoDbMigrationConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
|
@ -154,6 +155,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
mock(MessagesManager.class),
|
mock(MessagesManager.class),
|
||||||
mock(UsernamesManager.class),
|
mock(UsernamesManager.class),
|
||||||
mock(ProfilesManager.class),
|
mock(ProfilesManager.class),
|
||||||
|
mock(StoredVerificationCodeManager.class),
|
||||||
mock(SecureStorageClient.class),
|
mock(SecureStorageClient.class),
|
||||||
mock(SecureBackupClient.class),
|
mock(SecureBackupClient.class),
|
||||||
experimentEnrollmentManager,
|
experimentEnrollmentManager,
|
||||||
|
@ -164,9 +166,30 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
void testConcurrentUpdate() throws IOException {
|
void testConcurrentUpdate() throws IOException {
|
||||||
|
|
||||||
final UUID uuid = UUID.randomUUID();
|
final UUID uuid;
|
||||||
|
{
|
||||||
|
final Account account = accountsManager.update(
|
||||||
|
accountsManager.create("+14155551212", "password", null, new AccountAttributes()),
|
||||||
|
a -> {
|
||||||
|
a.setUnidentifiedAccessKey(new byte[16]);
|
||||||
|
|
||||||
accountsManager.create(generateAccount("+14155551212", uuid));
|
final Random random = new Random();
|
||||||
|
final SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(),
|
||||||
|
"testSignature-" + random.nextInt());
|
||||||
|
|
||||||
|
a.removeDevice(1);
|
||||||
|
a.addDevice(new Device(1, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(),
|
||||||
|
"testSalt-" + random.nextInt(),
|
||||||
|
"testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(),
|
||||||
|
random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(),
|
||||||
|
"testUserAgent-" + random.nextInt(), 0,
|
||||||
|
new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
|
||||||
|
random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
|
||||||
|
random.nextBoolean(), random.nextBoolean())));
|
||||||
|
});
|
||||||
|
|
||||||
|
uuid = account.getUuid();
|
||||||
|
}
|
||||||
|
|
||||||
final String profileName = "name";
|
final String profileName = "name";
|
||||||
final String avatar = "avatar";
|
final String avatar = "avatar";
|
||||||
|
@ -249,26 +272,4 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
accountsManager.updateDevice(account, deviceId, deviceMutation);
|
accountsManager.updateDevice(account, deviceId, deviceMutation);
|
||||||
}, mutationExecutor);
|
}, mutationExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account generateAccount(String number, UUID uuid) {
|
|
||||||
Device device = generateDevice(1);
|
|
||||||
return generateAccount(number, uuid, Collections.singleton(device));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Account generateAccount(String number, UUID uuid, Set<Device> devices) {
|
|
||||||
byte[] unidentifiedAccessKey = new byte[16];
|
|
||||||
Random random = new Random(System.currentTimeMillis());
|
|
||||||
Arrays.fill(unidentifiedAccessKey, (byte)random.nextInt(255));
|
|
||||||
|
|
||||||
return new Account(number, uuid, devices, unidentifiedAccessKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Device generateDevice(long id) {
|
|
||||||
Random random = new Random(System.currentTimeMillis());
|
|
||||||
SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), "testSignature-" + random.nextInt());
|
|
||||||
return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(),
|
|
||||||
"testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), "testUserAgent-" + random.nextInt() , 0, new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
|
|
||||||
random.nextBoolean(), random.nextBoolean()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,17 @@ import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyList;
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.anyLong;
|
||||||
import static org.whispersystems.textsecuregcm.tests.util.AccountsHelper.eqUuid;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.reset;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||||
|
@ -42,7 +51,7 @@ import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.ArgumentMatcher;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||||
|
@ -70,14 +79,11 @@ import org.whispersystems.textsecuregcm.push.GcmMessage;
|
||||||
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
||||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||||
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
|
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.KeysDynamoDb;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||||
|
@ -121,9 +127,6 @@ class AccountControllerTest {
|
||||||
private static RateLimiter autoBlockLimiter = mock(RateLimiter.class);
|
private static RateLimiter autoBlockLimiter = mock(RateLimiter.class);
|
||||||
private static RateLimiter usernameSetLimiter = mock(RateLimiter.class);
|
private static RateLimiter usernameSetLimiter = mock(RateLimiter.class);
|
||||||
private static SmsSender smsSender = mock(SmsSender.class);
|
private static SmsSender smsSender = mock(SmsSender.class);
|
||||||
private static DirectoryQueue directoryQueue = mock(DirectoryQueue.class);
|
|
||||||
private static MessagesManager storedMessages = mock(MessagesManager.class);
|
|
||||||
private static KeysDynamoDb keys = mock(KeysDynamoDb.class);
|
|
||||||
private static TurnTokenGenerator turnTokenGenerator = mock(TurnTokenGenerator.class);
|
private static TurnTokenGenerator turnTokenGenerator = mock(TurnTokenGenerator.class);
|
||||||
private static Account senderPinAccount = mock(Account.class);
|
private static Account senderPinAccount = mock(Account.class);
|
||||||
private static Account senderRegLockAccount = mock(Account.class);
|
private static Account senderRegLockAccount = mock(Account.class);
|
||||||
|
@ -155,9 +158,6 @@ class AccountControllerTest {
|
||||||
abusiveHostRules,
|
abusiveHostRules,
|
||||||
rateLimiters,
|
rateLimiters,
|
||||||
smsSender,
|
smsSender,
|
||||||
directoryQueue,
|
|
||||||
storedMessages,
|
|
||||||
keys,
|
|
||||||
dynamicConfigurationManager,
|
dynamicConfigurationManager,
|
||||||
turnTokenGenerator,
|
turnTokenGenerator,
|
||||||
new HashMap<>(),
|
new HashMap<>(),
|
||||||
|
@ -218,6 +218,14 @@ class AccountControllerTest {
|
||||||
when(accountsManager.get(eq(SENDER_HAS_STORAGE))).thenReturn(Optional.of(senderHasStorage));
|
when(accountsManager.get(eq(SENDER_HAS_STORAGE))).thenReturn(Optional.of(senderHasStorage));
|
||||||
when(accountsManager.get(eq(SENDER_TRANSFER))).thenReturn(Optional.of(senderTransfer));
|
when(accountsManager.get(eq(SENDER_TRANSFER))).thenReturn(Optional.of(senderTransfer));
|
||||||
|
|
||||||
|
when(accountsManager.create(any(), any(), any(), any())).thenAnswer((Answer<Account>) invocation -> {
|
||||||
|
final Account account = mock(Account.class);
|
||||||
|
when(account.getUuid()).thenReturn(UUID.randomUUID());
|
||||||
|
when(account.getNumber()).thenReturn(invocation.getArgument(0, String.class));
|
||||||
|
|
||||||
|
return account;
|
||||||
|
});
|
||||||
|
|
||||||
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("n00bkiller"))).thenReturn(true);
|
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("n00bkiller"))).thenReturn(true);
|
||||||
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("takenusername"))).thenReturn(false);
|
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("takenusername"))).thenReturn(false);
|
||||||
|
|
||||||
|
@ -261,9 +269,6 @@ class AccountControllerTest {
|
||||||
autoBlockLimiter,
|
autoBlockLimiter,
|
||||||
usernameSetLimiter,
|
usernameSetLimiter,
|
||||||
smsSender,
|
smsSender,
|
||||||
directoryQueue,
|
|
||||||
storedMessages,
|
|
||||||
keys,
|
|
||||||
turnTokenGenerator,
|
turnTokenGenerator,
|
||||||
senderPinAccount,
|
senderPinAccount,
|
||||||
senderRegLockAccount,
|
senderRegLockAccount,
|
||||||
|
@ -922,23 +927,13 @@ class AccountControllerTest {
|
||||||
Optional.of(new StoredVerificationCode("1234", System.currentTimeMillis(), "1234-push", "VerificationSid")));;
|
Optional.of(new StoredVerificationCode("1234", System.currentTimeMillis(), "1234-push", "VerificationSid")));;
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountCreationResult result =
|
resources.getJerseyTest()
|
||||||
resources.getJerseyTest()
|
.target(String.format("/v1/accounts/code/%s", "1234"))
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
.request()
|
||||||
.request()
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
.put(Entity.entity(new AccountAttributes(), MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
verify(accountsManager).create(eq(SENDER), eq("bar"), any(), any());
|
||||||
assertThat(result.isStorageCapable()).isFalse();
|
|
||||||
|
|
||||||
final ArgumentCaptor<Account> accountArgumentCaptor = ArgumentCaptor.forClass(Account.class);
|
|
||||||
|
|
||||||
verify(accountsManager, times(1)).create(accountArgumentCaptor.capture());
|
|
||||||
verify(directoryQueue, times(1)).refreshRegisteredUser(argThat(account -> SENDER.equals(account.getNumber())));
|
|
||||||
|
|
||||||
assertThat(accountArgumentCaptor.getValue().isDiscoverableByPhoneNumber()).isTrue();
|
|
||||||
|
|
||||||
if (enrolledInVerifyExperiment) {
|
if (enrolledInVerifyExperiment) {
|
||||||
verify(smsSender).reportVerificationSucceeded("VerificationSid");
|
verify(smsSender).reportVerificationSucceeded("VerificationSid");
|
||||||
|
@ -946,52 +941,14 @@ class AccountControllerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testVerifyCodeUndiscoverable() throws Exception {
|
void testVerifyCodeOld() {
|
||||||
AccountCreationResult result =
|
|
||||||
resources.getJerseyTest()
|
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
|
||||||
.request()
|
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, false, null),
|
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
|
||||||
assertThat(result.isStorageCapable()).isFalse();
|
|
||||||
|
|
||||||
final ArgumentCaptor<Account> accountArgumentCaptor = ArgumentCaptor.forClass(Account.class);
|
|
||||||
|
|
||||||
verify(accountsManager, times(1)).create(accountArgumentCaptor.capture());
|
|
||||||
verify(directoryQueue, times(1)).refreshRegisteredUser(argThat(account -> SENDER.equals(account.getNumber())));
|
|
||||||
|
|
||||||
assertThat(accountArgumentCaptor.getValue().isDiscoverableByPhoneNumber()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testVerifySupportsStorage() throws Exception {
|
|
||||||
AccountCreationResult result =
|
|
||||||
resources.getJerseyTest()
|
|
||||||
.target(String.format("/v1/accounts/code/%s", "666666"))
|
|
||||||
.request()
|
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_HAS_STORAGE, "bar"))
|
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
|
||||||
assertThat(result.isStorageCapable()).isTrue();
|
|
||||||
|
|
||||||
verify(accountsManager, times(1)).create(isA(Account.class));
|
|
||||||
verify(directoryQueue, times(1)).refreshRegisteredUser(argThat(account -> SENDER_HAS_STORAGE.equals(account.getNumber())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testVerifyCodeOld() throws Exception {
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
.target(String.format("/v1/accounts/code/%s", "1234"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_OLD, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_OLD, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(403);
|
assertThat(response.getStatus()).isEqualTo(403);
|
||||||
|
|
||||||
|
@ -999,14 +956,14 @@ class AccountControllerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testVerifyBadCode() throws Exception {
|
void testVerifyBadCode() {
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "1111"))
|
.target(String.format("/v1/accounts/code/%s", "1111"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(403);
|
assertThat(response.getStatus()).isEqualTo(403);
|
||||||
|
|
||||||
|
@ -1017,11 +974,11 @@ class AccountControllerTest {
|
||||||
void testVerifyPin() throws Exception {
|
void testVerifyPin() throws Exception {
|
||||||
AccountCreationResult result =
|
AccountCreationResult result =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "333333"))
|
.target(String.format("/v1/accounts/code/%s", "333333"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_PIN, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_PIN, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, "31337", null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, "31337", null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
|
||||||
|
@ -1032,11 +989,11 @@ class AccountControllerTest {
|
||||||
void testVerifyRegistrationLock() throws Exception {
|
void testVerifyRegistrationLock() throws Exception {
|
||||||
AccountCreationResult result =
|
AccountCreationResult result =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "666666"))
|
.target(String.format("/v1/accounts/code/%s", "666666"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, Hex.toStringCondensed(registration_lock_key), true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, Hex.toStringCondensed(registration_lock_key), true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
|
||||||
|
@ -1045,36 +1002,24 @@ class AccountControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testVerifyRegistrationLockSetsRegistrationLockOnNewAccount() throws Exception {
|
void testVerifyRegistrationLockSetsRegistrationLockOnNewAccount() throws Exception {
|
||||||
|
|
||||||
AccountCreationResult result =
|
AccountCreationResult result =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "666666"))
|
.target(String.format("/v1/accounts/code/%s", "666666"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, Hex.toStringCondensed(registration_lock_key), true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, Hex.toStringCondensed(registration_lock_key), true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
|
||||||
verify(pinLimiter).validate(eq(SENDER_REG_LOCK));
|
verify(pinLimiter).validate(eq(SENDER_REG_LOCK));
|
||||||
|
|
||||||
verify(accountsManager).create(argThat(new ArgumentMatcher<>() {
|
verify(accountsManager).create(eq(SENDER_REG_LOCK), eq("bar"), any(), argThat(
|
||||||
@Override
|
attributes -> Hex.toStringCondensed(registration_lock_key).equals(attributes.getRegistrationLock())));
|
||||||
public boolean matches(final Account account) {
|
|
||||||
final StoredRegistrationLock regLock = account.getRegistrationLock();
|
|
||||||
return regLock.requiresClientRegistrationLock() && regLock.verify(Hex.toStringCondensed(registration_lock_key), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Account that has registration lock set";
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testVerifyRegistrationLockOld() throws Exception {
|
void testVerifyRegistrationLockOld() {
|
||||||
StoredRegistrationLock lock = senderRegLockAccount.getRegistrationLock();
|
StoredRegistrationLock lock = senderRegLockAccount.getRegistrationLock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1082,11 +1027,11 @@ class AccountControllerTest {
|
||||||
|
|
||||||
AccountCreationResult result =
|
AccountCreationResult result =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "666666"))
|
.target(String.format("/v1/accounts/code/%s", "666666"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
|
||||||
|
@ -1100,11 +1045,11 @@ class AccountControllerTest {
|
||||||
void testVerifyWrongPin() throws Exception {
|
void testVerifyWrongPin() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "333333"))
|
.target(String.format("/v1/accounts/code/%s", "333333"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_PIN, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_PIN, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, "31338", null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, "31338", null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(423);
|
assertThat(response.getStatus()).isEqualTo(423);
|
||||||
|
|
||||||
|
@ -1115,12 +1060,12 @@ class AccountControllerTest {
|
||||||
void testVerifyWrongRegistrationLock() throws Exception {
|
void testVerifyWrongRegistrationLock() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "666666"))
|
.target(String.format("/v1/accounts/code/%s", "666666"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null,
|
.put(Entity.entity(new AccountAttributes(false, 3333, null,
|
||||||
Hex.toStringCondensed(new byte[32]), null, true, null),
|
Hex.toStringCondensed(new byte[32]), null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(423);
|
assertThat(response.getStatus()).isEqualTo(423);
|
||||||
|
|
||||||
|
@ -1131,11 +1076,11 @@ class AccountControllerTest {
|
||||||
void testVerifyNoPin() throws Exception {
|
void testVerifyNoPin() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "333333"))
|
.target(String.format("/v1/accounts/code/%s", "333333"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_PIN, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_PIN, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(423);
|
assertThat(response.getStatus()).isEqualTo(423);
|
||||||
|
|
||||||
|
@ -1149,11 +1094,11 @@ class AccountControllerTest {
|
||||||
void testVerifyNoRegistrationLock() throws Exception {
|
void testVerifyNoRegistrationLock() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "666666"))
|
.target(String.format("/v1/accounts/code/%s", "666666"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(423);
|
assertThat(response.getStatus()).isEqualTo(423);
|
||||||
|
|
||||||
|
@ -1172,11 +1117,11 @@ class AccountControllerTest {
|
||||||
void testVerifyLimitPin() throws Exception {
|
void testVerifyLimitPin() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "444444"))
|
.target(String.format("/v1/accounts/code/%s", "444444"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_OVER_PIN, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_OVER_PIN, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, "31337", null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, "31337", null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(413);
|
assertThat(response.getStatus()).isEqualTo(413);
|
||||||
|
|
||||||
|
@ -1190,11 +1135,11 @@ class AccountControllerTest {
|
||||||
|
|
||||||
AccountCreationResult result =
|
AccountCreationResult result =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "444444"))
|
.target(String.format("/v1/accounts/code/%s", "444444"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_OVER_PIN, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_OVER_PIN, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
|
||||||
|
@ -1208,13 +1153,13 @@ class AccountControllerTest {
|
||||||
when(senderTransfer.isTransferSupported()).thenReturn(true);
|
when(senderTransfer.isTransferSupported()).thenReturn(true);
|
||||||
|
|
||||||
final Response response =
|
final Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
.target(String.format("/v1/accounts/code/%s", "1234"))
|
||||||
.queryParam("transfer", true)
|
.queryParam("transfer", true)
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_TRANSFER, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_TRANSFER, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(409);
|
assertThat(response.getStatus()).isEqualTo(409);
|
||||||
}
|
}
|
||||||
|
@ -1224,13 +1169,13 @@ class AccountControllerTest {
|
||||||
when(senderTransfer.isTransferSupported()).thenReturn(false);
|
when(senderTransfer.isTransferSupported()).thenReturn(false);
|
||||||
|
|
||||||
final Response response =
|
final Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
.target(String.format("/v1/accounts/code/%s", "1234"))
|
||||||
.queryParam("transfer", true)
|
.queryParam("transfer", true)
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_TRANSFER, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_TRANSFER, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
}
|
}
|
||||||
|
@ -1240,41 +1185,14 @@ class AccountControllerTest {
|
||||||
when(senderTransfer.isTransferSupported()).thenReturn(true);
|
when(senderTransfer.isTransferSupported()).thenReturn(true);
|
||||||
|
|
||||||
final Response response =
|
final Response response =
|
||||||
resources.getJerseyTest()
|
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
|
||||||
.request()
|
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER_TRANSFER, "bar"))
|
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testVerifyReregistration() throws InterruptedException {
|
|
||||||
final UUID existingUuid = UUID.randomUUID();
|
|
||||||
final Account existingAccount = mock(Account.class);
|
|
||||||
|
|
||||||
when(existingAccount.getUuid()).thenReturn(existingUuid);
|
|
||||||
when(accountsManager.get(SENDER)).thenReturn(Optional.of(existingAccount));
|
|
||||||
|
|
||||||
AccountCreationResult result =
|
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
.target(String.format("/v1/accounts/code/%s", "1234"))
|
||||||
.request()
|
.request()
|
||||||
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_TRANSFER, "bar"))
|
||||||
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, null, true, null),
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
final ArgumentCaptor<Account> accountArgumentCaptor = ArgumentCaptor.forClass(Account.class);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
verify(accountsManager, times(1)).create(accountArgumentCaptor.capture());
|
|
||||||
verify(directoryQueue, times(1)).refreshRegisteredUser(argThat(account -> SENDER.equals(account.getNumber())));
|
|
||||||
|
|
||||||
verify(storedMessages).clear(existingUuid);
|
|
||||||
verify(keys).delete(existingAccount);
|
|
||||||
|
|
||||||
assertThat(accountArgumentCaptor.getValue().isDiscoverableByPhoneNumber()).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1388,7 +1306,6 @@ class AccountControllerTest {
|
||||||
|
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setGcmId(eq("c00lz0rz"));
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setGcmId(eq("c00lz0rz"));
|
||||||
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
||||||
verify(directoryQueue, never()).refreshRegisteredUser(any(Account.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1404,7 +1321,6 @@ class AccountControllerTest {
|
||||||
|
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setGcmId(eq("z000"));
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setGcmId(eq("z000"));
|
||||||
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
||||||
verify(directoryQueue, never()).refreshRegisteredUser(any(Account.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1421,7 +1337,6 @@ class AccountControllerTest {
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first"));
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first"));
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(eq("second"));
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(eq("second"));
|
||||||
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
||||||
verify(directoryQueue, never()).refreshRegisteredUser(any(Account.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1438,7 +1353,6 @@ class AccountControllerTest {
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first"));
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first"));
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(null);
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(null);
|
||||||
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
||||||
verify(directoryQueue, never()).refreshRegisteredUser(any(Account.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1455,7 +1369,6 @@ class AccountControllerTest {
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("third"));
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("third"));
|
||||||
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(eq("fourth"));
|
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(eq("fourth"));
|
||||||
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyLong(), any());
|
||||||
verify(directoryQueue, never()).refreshRegisteredUser(any(Account.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -1566,7 +1479,6 @@ class AccountControllerTest {
|
||||||
.put(Entity.json(new AccountAttributes(false, 2222, null, null, null, true, null)));
|
.put(Entity.json(new AccountAttributes(false, 2222, null, null, null, true, null)));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(204);
|
assertThat(response.getStatus()).isEqualTo(204);
|
||||||
verify(directoryQueue, never()).refreshRegisteredUser(any());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1579,7 +1491,6 @@ class AccountControllerTest {
|
||||||
.put(Entity.json(new AccountAttributes(false, 2222, null, null, null, true, null)));
|
.put(Entity.json(new AccountAttributes(false, 2222, null, null, null, true, null)));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(204);
|
assertThat(response.getStatus()).isEqualTo(204);
|
||||||
verify(directoryQueue, times(1)).refreshRegisteredUser(eqUuid(AuthHelper.UNDISCOVERABLE_ACCOUNT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1592,7 +1503,6 @@ class AccountControllerTest {
|
||||||
.put(Entity.json(new AccountAttributes(false, 2222, null, null, null, false, null)));
|
.put(Entity.json(new AccountAttributes(false, 2222, null, null, null, false, null)));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(204);
|
assertThat(response.getStatus()).isEqualTo(204);
|
||||||
verify(directoryQueue, times(1)).refreshRegisteredUser(eqUuid(AuthHelper.VALID_ACCOUNT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -45,7 +45,6 @@ import org.whispersystems.textsecuregcm.entities.DeviceResponse;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
|
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
|
@ -65,11 +64,10 @@ class DeviceControllerTest {
|
||||||
AccountsManager accounts,
|
AccountsManager accounts,
|
||||||
MessagesManager messages,
|
MessagesManager messages,
|
||||||
KeysDynamoDb keys,
|
KeysDynamoDb keys,
|
||||||
DirectoryQueue cdsSender,
|
|
||||||
RateLimiters rateLimiters,
|
RateLimiters rateLimiters,
|
||||||
Map<String, Integer> deviceConfiguration)
|
Map<String, Integer> deviceConfiguration)
|
||||||
{
|
{
|
||||||
super(pendingDevices, accounts, messages, keys, cdsSender, rateLimiters, deviceConfiguration);
|
super(pendingDevices, accounts, messages, keys, rateLimiters, deviceConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,7 +80,6 @@ class DeviceControllerTest {
|
||||||
private static AccountsManager accountsManager = mock(AccountsManager.class );
|
private static AccountsManager accountsManager = mock(AccountsManager.class );
|
||||||
private static MessagesManager messagesManager = mock(MessagesManager.class);
|
private static MessagesManager messagesManager = mock(MessagesManager.class);
|
||||||
private static KeysDynamoDb keys = mock(KeysDynamoDb.class);
|
private static KeysDynamoDb keys = mock(KeysDynamoDb.class);
|
||||||
private static DirectoryQueue directoryQueue = mock(DirectoryQueue.class);
|
|
||||||
private static RateLimiters rateLimiters = mock(RateLimiters.class );
|
private static RateLimiters rateLimiters = mock(RateLimiters.class );
|
||||||
private static RateLimiter rateLimiter = mock(RateLimiter.class );
|
private static RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||||
private static Account account = mock(Account.class );
|
private static Account account = mock(Account.class );
|
||||||
|
@ -100,7 +97,6 @@ class DeviceControllerTest {
|
||||||
accountsManager,
|
accountsManager,
|
||||||
messagesManager,
|
messagesManager,
|
||||||
keys,
|
keys,
|
||||||
directoryQueue,
|
|
||||||
rateLimiters,
|
rateLimiters,
|
||||||
deviceConfiguration))
|
deviceConfiguration))
|
||||||
.build();
|
.build();
|
||||||
|
@ -142,7 +138,6 @@ class DeviceControllerTest {
|
||||||
accountsManager,
|
accountsManager,
|
||||||
messagesManager,
|
messagesManager,
|
||||||
keys,
|
keys,
|
||||||
directoryQueue,
|
|
||||||
rateLimiters,
|
rateLimiters,
|
||||||
rateLimiter,
|
rateLimiter,
|
||||||
account,
|
account,
|
||||||
|
|
|
@ -57,7 +57,6 @@ import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.mappers.RateLimitChallengeExceptionMapper;
|
import org.whispersystems.textsecuregcm.mappers.RateLimitChallengeExceptionMapper;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
|
@ -92,7 +91,6 @@ class KeysControllerTest {
|
||||||
|
|
||||||
private final static KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class );
|
private final static KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class );
|
||||||
private final static AccountsManager accounts = mock(AccountsManager.class );
|
private final static AccountsManager accounts = mock(AccountsManager.class );
|
||||||
private final static DirectoryQueue directoryQueue = mock(DirectoryQueue.class );
|
|
||||||
private final static PreKeyRateLimiter preKeyRateLimiter = mock(PreKeyRateLimiter.class );
|
private final static PreKeyRateLimiter preKeyRateLimiter = mock(PreKeyRateLimiter.class );
|
||||||
private final static RateLimitChallengeManager rateLimitChallengeManager = mock(RateLimitChallengeManager.class );
|
private final static RateLimitChallengeManager rateLimitChallengeManager = mock(RateLimitChallengeManager.class );
|
||||||
private final static Account existsAccount = mock(Account.class );
|
private final static Account existsAccount = mock(Account.class );
|
||||||
|
@ -107,7 +105,7 @@ class KeysControllerTest {
|
||||||
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(Account.class, DisabledPermittedAccount.class)))
|
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(Account.class, DisabledPermittedAccount.class)))
|
||||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||||
.addResource(new RateLimitChallengeExceptionMapper(rateLimitChallengeManager))
|
.addResource(new RateLimitChallengeExceptionMapper(rateLimitChallengeManager))
|
||||||
.addResource(new KeysController(rateLimiters, keysDynamoDb, accounts, directoryQueue, preKeyRateLimiter, dynamicConfigurationManager, rateLimitChallengeManager))
|
.addResource(new KeysController(rateLimiters, keysDynamoDb, accounts, preKeyRateLimiter, dynamicConfigurationManager, rateLimitChallengeManager))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
@ -184,7 +182,6 @@ class KeysControllerTest {
|
||||||
reset(
|
reset(
|
||||||
keysDynamoDb,
|
keysDynamoDb,
|
||||||
accounts,
|
accounts,
|
||||||
directoryQueue,
|
|
||||||
preKeyRateLimiter,
|
preKeyRateLimiter,
|
||||||
existsAccount,
|
existsAccount,
|
||||||
rateLimiters,
|
rateLimiters,
|
||||||
|
|
|
@ -19,8 +19,8 @@ import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import io.lettuce.core.RedisException;
|
import io.lettuce.core.RedisException;
|
||||||
|
@ -30,17 +30,20 @@ import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicAccountsDynamoDbMigrationConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicAccountsDynamoDbMigrationConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
|
||||||
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
|
@ -49,21 +52,28 @@ import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.AccountsDynamoDb;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ContestedOptimisticLockException;
|
import org.whispersystems.textsecuregcm.storage.ContestedOptimisticLockException;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager;
|
import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.KeysDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.KeysDynamoDb;
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.JsonHelpers;
|
import org.whispersystems.textsecuregcm.tests.util.JsonHelpers;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||||
|
|
||||||
class AccountsManagerTest {
|
class AccountsManagerTest {
|
||||||
|
|
||||||
private DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
private Accounts accounts;
|
||||||
private ExperimentEnrollmentManager experimentEnrollmentManager = mock(ExperimentEnrollmentManager.class);
|
private AccountsDynamoDb accountsDynamoDb;
|
||||||
|
private DirectoryQueue directoryQueue;
|
||||||
|
private DynamicConfigurationManager dynamicConfigurationManager;
|
||||||
|
private ExperimentEnrollmentManager experimentEnrollmentManager;
|
||||||
|
|
||||||
|
private RedisAdvancedClusterCommands<String, String> commands;
|
||||||
|
private AccountsManager accountsManager;
|
||||||
|
|
||||||
private static final Answer<?> ACCOUNT_UPDATE_ANSWER = (answer) -> {
|
private static final Answer<?> ACCOUNT_UPDATE_ANSWER = (answer) -> {
|
||||||
// it is implicit in the update() contract is that a successful call will
|
// it is implicit in the update() contract is that a successful call will
|
||||||
|
@ -75,49 +85,57 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
|
accounts = mock(Accounts.class);
|
||||||
|
accountsDynamoDb = mock(AccountsDynamoDb.class);
|
||||||
|
directoryQueue = mock(DirectoryQueue.class);
|
||||||
|
dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||||
|
experimentEnrollmentManager = mock(ExperimentEnrollmentManager.class);
|
||||||
|
|
||||||
DynamicConfiguration dynamicConfiguration = new DynamicConfiguration();
|
//noinspection unchecked
|
||||||
|
commands = mock(RedisAdvancedClusterCommands.class);
|
||||||
|
|
||||||
|
final DynamicConfiguration dynamicConfiguration = new DynamicConfiguration();
|
||||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||||
|
|
||||||
|
accountsManager = new AccountsManager(
|
||||||
|
accounts,
|
||||||
|
accountsDynamoDb,
|
||||||
|
RedisClusterHelper.buildMockRedisCluster(commands),
|
||||||
|
mock(DeletedAccountsManager.class),
|
||||||
|
directoryQueue,
|
||||||
|
mock(KeysDynamoDb.class),
|
||||||
|
mock(MessagesManager.class),
|
||||||
|
mock(UsernamesManager.class),
|
||||||
|
mock(ProfilesManager.class),
|
||||||
|
mock(StoredVerificationCodeManager.class),
|
||||||
|
mock(SecureStorageClient.class),
|
||||||
|
mock(SecureBackupClient.class),
|
||||||
|
experimentEnrollmentManager,
|
||||||
|
dynamicConfigurationManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(booleans = {true, false})
|
@ValueSource(booleans = {true, false})
|
||||||
void testGetAccountByNumberInCache(final boolean dynamoEnabled) {
|
void testGetAccountByNumberInCache(final boolean dynamoEnabled) {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
enableDynamo(dynamoEnabled);
|
enableDynamo(dynamoEnabled);
|
||||||
|
|
||||||
when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(uuid.toString());
|
when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(uuid.toString());
|
||||||
when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
|
when(commands.get(eq("Account3::" + uuid))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
Optional<Account> account = accountsManager.get("+14152222222");
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
Optional<Account> account = accountsManager.get("+14152222222");
|
|
||||||
|
|
||||||
assertTrue(account.isPresent());
|
assertTrue(account.isPresent());
|
||||||
assertEquals(account.get().getNumber(), "+14152222222");
|
assertEquals(account.get().getNumber(), "+14152222222");
|
||||||
assertEquals(account.get().getProfileName(), "test");
|
assertEquals(account.get().getProfileName(), "test");
|
||||||
|
|
||||||
verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
|
verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
|
||||||
verify(commands, times(1)).get(eq("Account3::" + uuid.toString()));
|
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
verifyNoMoreInteractions(accounts);
|
verifyNoMoreInteractions(accounts);
|
||||||
|
|
||||||
verifyZeroInteractions(accountsDynamoDb);
|
verifyNoInteractions(accountsDynamoDb);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableDynamo(boolean dynamoEnabled) {
|
private void enableDynamo(boolean dynamoEnabled) {
|
||||||
|
@ -136,75 +154,46 @@ class AccountsManagerTest {
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(booleans = {true, false})
|
@ValueSource(booleans = {true, false})
|
||||||
void testGetAccountByUuidInCache(boolean dynamoEnabled) {
|
void testGetAccountByUuidInCache(boolean dynamoEnabled) {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
enableDynamo(dynamoEnabled);
|
enableDynamo(dynamoEnabled);
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
|
when(commands.get(eq("Account3::" + uuid))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
Optional<Account> account = accountsManager.get(uuid);
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
Optional<Account> account = accountsManager.get(uuid);
|
|
||||||
|
|
||||||
assertTrue(account.isPresent());
|
assertTrue(account.isPresent());
|
||||||
assertEquals(account.get().getNumber(), "+14152222222");
|
assertEquals(account.get().getNumber(), "+14152222222");
|
||||||
assertEquals(account.get().getUuid(), uuid);
|
assertEquals(account.get().getUuid(), uuid);
|
||||||
assertEquals(account.get().getProfileName(), "test");
|
assertEquals(account.get().getProfileName(), "test");
|
||||||
|
|
||||||
verify(commands, times(1)).get(eq("Account3::" + uuid.toString()));
|
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
verifyNoMoreInteractions(accounts);
|
verifyNoMoreInteractions(accounts);
|
||||||
|
|
||||||
verifyZeroInteractions(accountsDynamoDb);
|
verifyNoInteractions(accountsDynamoDb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(booleans = {true, false})
|
@ValueSource(booleans = {true, false})
|
||||||
void testGetAccountByNumberNotInCache(boolean dynamoEnabled) {
|
void testGetAccountByNumberNotInCache(boolean dynamoEnabled) {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
UUID uuid = UUID.randomUUID();
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
|
||||||
|
|
||||||
enableDynamo(dynamoEnabled);
|
enableDynamo(dynamoEnabled);
|
||||||
|
|
||||||
when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null);
|
when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null);
|
||||||
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
|
||||||
|
|
||||||
assertTrue(retrieved.isPresent());
|
assertTrue(retrieved.isPresent());
|
||||||
assertSame(retrieved.get(), account);
|
assertSame(retrieved.get(), account);
|
||||||
|
|
||||||
verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
|
verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
|
||||||
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accounts, times(1)).get(eq("+14152222222"));
|
verify(accounts, times(1)).get(eq("+14152222222"));
|
||||||
|
@ -218,36 +207,22 @@ class AccountsManagerTest {
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(booleans = {true, false})
|
@ValueSource(booleans = {true, false})
|
||||||
void testGetAccountByUuidNotInCache(boolean dynamoEnabled) {
|
void testGetAccountByUuidNotInCache(boolean dynamoEnabled) {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
UUID uuid = UUID.randomUUID();
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
|
||||||
|
|
||||||
enableDynamo(dynamoEnabled);
|
enableDynamo(dynamoEnabled);
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
||||||
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
|
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
Optional<Account> retrieved = accountsManager.get(uuid);
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
Optional<Account> retrieved = accountsManager.get(uuid);
|
|
||||||
|
|
||||||
assertTrue(retrieved.isPresent());
|
assertTrue(retrieved.isPresent());
|
||||||
assertSame(retrieved.get(), account);
|
assertSame(retrieved.get(), account);
|
||||||
|
|
||||||
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
||||||
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accounts, times(1)).get(eq(uuid));
|
verify(accounts, times(1)).get(eq(uuid));
|
||||||
|
@ -260,18 +235,6 @@ class AccountsManagerTest {
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(booleans = {true, false})
|
@ValueSource(booleans = {true, false})
|
||||||
void testGetAccountByNumberBrokenCache(boolean dynamoEnabled) {
|
void testGetAccountByNumberBrokenCache(boolean dynamoEnabled) {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
|
@ -280,8 +243,6 @@ class AccountsManagerTest {
|
||||||
when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!"));
|
when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!"));
|
||||||
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
||||||
|
|
||||||
assertTrue(retrieved.isPresent());
|
assertTrue(retrieved.isPresent());
|
||||||
|
@ -289,7 +250,7 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
|
verify(commands, times(1)).get(eq("AccountMap::+14152222222"));
|
||||||
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accounts, times(1)).get(eq("+14152222222"));
|
verify(accounts, times(1)).get(eq("+14152222222"));
|
||||||
|
@ -302,18 +263,6 @@ class AccountsManagerTest {
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(booleans = {true, false})
|
@ValueSource(booleans = {true, false})
|
||||||
void testGetAccountByUuidBrokenCache(boolean dynamoEnabled) {
|
void testGetAccountByUuidBrokenCache(boolean dynamoEnabled) {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
|
@ -322,8 +271,6 @@ class AccountsManagerTest {
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!"));
|
when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!"));
|
||||||
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
|
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
Optional<Account> retrieved = accountsManager.get(uuid);
|
Optional<Account> retrieved = accountsManager.get(uuid);
|
||||||
|
|
||||||
assertTrue(retrieved.isPresent());
|
assertTrue(retrieved.isPresent());
|
||||||
|
@ -331,7 +278,7 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
||||||
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
verify(commands, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid.toString()), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accounts, times(1)).get(eq(uuid));
|
verify(accounts, times(1)).get(eq(uuid));
|
||||||
|
@ -344,18 +291,6 @@ class AccountsManagerTest {
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(booleans = {true, false})
|
@ValueSource(booleans = {true, false})
|
||||||
void testUpdate_dynamoDbMigration(boolean dynamoEnabled) throws IOException {
|
void testUpdate_dynamoDbMigration(boolean dynamoEnabled) throws IOException {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
|
@ -367,9 +302,6 @@ class AccountsManagerTest {
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
when(accountsDynamoDb.get(uuid)).thenReturn(Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
||||||
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accounts).update(any(Account.class));
|
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accounts).update(any(Account.class));
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccountsManager,
|
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
|
|
||||||
Account updatedAccount = accountsManager.update(account, a -> a.setProfileName("name"));
|
Account updatedAccount = accountsManager.update(account, a -> a.setProfileName("name"));
|
||||||
|
|
||||||
assertThrows(AssertionError.class, account::getProfileName, "Account passed to update() should be stale");
|
assertThrows(AssertionError.class, account::getProfileName, "Account passed to update() should be stale");
|
||||||
|
@ -405,18 +337,6 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate_dynamoMissing() {
|
void testUpdate_dynamoMissing() {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
|
@ -427,9 +347,6 @@ class AccountsManagerTest {
|
||||||
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accounts).update(any());
|
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accounts).update(any());
|
||||||
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accountsDynamoDb).update(any());
|
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accountsDynamoDb).update(any());
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
|
|
||||||
Account updatedAccount = accountsManager.update(account, a -> {});
|
Account updatedAccount = accountsManager.update(account, a -> {});
|
||||||
|
|
||||||
verify(accounts, times(1)).update(account);
|
verify(accounts, times(1)).update(account);
|
||||||
|
@ -444,18 +361,6 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate_optimisticLockingFailure() {
|
void testUpdate_optimisticLockingFailure() {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
|
@ -473,8 +378,6 @@ class AccountsManagerTest {
|
||||||
.doAnswer(ACCOUNT_UPDATE_ANSWER)
|
.doAnswer(ACCOUNT_UPDATE_ANSWER)
|
||||||
.when(accountsDynamoDb).update(any());
|
.when(accountsDynamoDb).update(any());
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
|
|
||||||
account = accountsManager.update(account, a -> a.setProfileName("name"));
|
account = accountsManager.update(account, a -> a.setProfileName("name"));
|
||||||
|
|
||||||
assertEquals(1, account.getVersion());
|
assertEquals(1, account.getVersion());
|
||||||
|
@ -492,18 +395,6 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate_dynamoOptimisticLockingFailureDuringCreate() {
|
void testUpdate_dynamoOptimisticLockingFailureDuringCreate() {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
|
@ -514,8 +405,6 @@ class AccountsManagerTest {
|
||||||
.thenReturn(Optional.of(account));
|
.thenReturn(Optional.of(account));
|
||||||
when(accountsDynamoDb.create(any())).thenThrow(ContestedOptimisticLockException.class);
|
when(accountsDynamoDb.create(any())).thenThrow(ContestedOptimisticLockException.class);
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
|
|
||||||
accountsManager.update(account, a -> {});
|
accountsManager.update(account, a -> {});
|
||||||
|
|
||||||
verify(accounts, times(1)).update(account);
|
verify(accounts, times(1)).update(account);
|
||||||
|
@ -526,22 +415,7 @@ class AccountsManagerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdateDevice() throws Exception {
|
void testUpdateDevice() {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.empty(), Optional.empty()));
|
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.empty(), Optional.empty()));
|
||||||
|
|
||||||
final UUID uuid = UUID.randomUUID();
|
final UUID uuid = UUID.randomUUID();
|
||||||
|
@ -565,7 +439,7 @@ class AccountsManagerTest {
|
||||||
account = accountsManager.updateDevice(account, deviceId, deviceUpdater);
|
account = accountsManager.updateDevice(account, deviceId, deviceUpdater);
|
||||||
account = accountsManager.updateDevice(account, deviceId, d -> d.setName("deviceName"));
|
account = accountsManager.updateDevice(account, deviceId, d -> d.setName("deviceName"));
|
||||||
|
|
||||||
assertEquals("deviceName", account.getDevice(deviceId).get().getName());
|
assertEquals("deviceName", account.getDevice(deviceId).orElseThrow().getName());
|
||||||
|
|
||||||
verify(deviceUpdater, times(1)).accept(any(Device.class));
|
verify(deviceUpdater, times(1)).accept(any(Device.class));
|
||||||
|
|
||||||
|
@ -574,25 +448,8 @@ class AccountsManagerTest {
|
||||||
verify(unknownDeviceUpdater, never()).accept(any(Device.class));
|
verify(unknownDeviceUpdater, never()).accept(any(Device.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCompareAccounts() throws Exception {
|
void testCompareAccounts() throws Exception {
|
||||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
|
||||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
|
||||||
Accounts accounts = mock(Accounts.class);
|
|
||||||
AccountsDynamoDb accountsDynamoDb = mock(AccountsDynamoDb.class);
|
|
||||||
DeletedAccountsManager deletedAccounts = mock(DeletedAccountsManager.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);
|
|
||||||
SecureBackupClient secureBackupClient = mock(SecureBackupClient.class);
|
|
||||||
SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
|
||||||
|
|
||||||
AccountsManager accountsManager = new AccountsManager(accounts, accountsDynamoDb, cacheCluster, deletedAccounts,
|
|
||||||
directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager, secureStorageClient, secureBackupClient, experimentEnrollmentManager, dynamicConfigurationManager);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.empty(), Optional.empty()));
|
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.empty(), Optional.empty()));
|
||||||
|
|
||||||
final UUID uuidA = UUID.randomUUID();
|
final UUID uuidA = UUID.randomUUID();
|
||||||
|
@ -663,4 +520,50 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
assertEquals(Optional.of("profileName"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
assertEquals(Optional.of("profileName"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(booleans = {true, false})
|
||||||
|
void testCreateWithDiscoverability(final boolean discoverable) {
|
||||||
|
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, null, discoverable, null);
|
||||||
|
final Account account = accountsManager.create("+18005550123", "password", null, attributes);
|
||||||
|
|
||||||
|
assertEquals(discoverable, account.isDiscoverableByPhoneNumber());
|
||||||
|
|
||||||
|
if (!discoverable) {
|
||||||
|
verify(directoryQueue).deleteAccount(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(booleans = {true, false})
|
||||||
|
void testCreateWithStorageCapability(final boolean hasStorage) {
|
||||||
|
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, null, true,
|
||||||
|
new DeviceCapabilities(false, false, false, hasStorage, false, false, false, false));
|
||||||
|
|
||||||
|
final Account account = accountsManager.create("+18005550123", "password", null, attributes);
|
||||||
|
|
||||||
|
assertEquals(hasStorage, account.isStorageSupported());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource
|
||||||
|
void testUpdateDirectoryQueue(final boolean discoverableBeforeUpdate, final boolean discoverableAfterUpdate, final boolean expectRefresh) {
|
||||||
|
final Account account = new Account("+14152222222", UUID.randomUUID(), new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
|
when(directoryQueue.isDiscoverable(any()))
|
||||||
|
.thenReturn(discoverableBeforeUpdate)
|
||||||
|
.thenReturn(discoverableAfterUpdate);
|
||||||
|
|
||||||
|
final Account updatedAccount = accountsManager.update(account, a -> a.setProfileName("Hello I am a unit test"));
|
||||||
|
|
||||||
|
verify(directoryQueue, times(expectRefresh ? 1 : 0)).refreshAccount(updatedAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> testUpdateDirectoryQueue() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(false, false, false),
|
||||||
|
Arguments.of(true, true, false),
|
||||||
|
Arguments.of(false, true, true),
|
||||||
|
Arguments.of(true, false, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.tests.storage;
|
package org.whispersystems.textsecuregcm.tests.storage;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.anyBoolean;
|
import static org.mockito.Mockito.anyBoolean;
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
|
@ -13,7 +12,7 @@ import static org.mockito.Mockito.isNull;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.whispersystems.textsecuregcm.tests.util.AccountsHelper.eqUuid;
|
import static org.whispersystems.textsecuregcm.tests.util.AccountsHelper.eqUuid;
|
||||||
|
|
||||||
|
@ -23,11 +22,8 @@ import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
|
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
@ -39,7 +35,6 @@ import org.whispersystems.textsecuregcm.util.Util;
|
||||||
class PushFeedbackProcessorTest {
|
class PushFeedbackProcessorTest {
|
||||||
|
|
||||||
private AccountsManager accountsManager = mock(AccountsManager.class);
|
private AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
private DirectoryQueue directoryQueue = mock(DirectoryQueue.class);
|
|
||||||
|
|
||||||
private Account uninstalledAccount = mock(Account.class);
|
private Account uninstalledAccount = mock(Account.class);
|
||||||
private Account mixedAccount = mock(Account.class);
|
private Account mixedAccount = mock(Account.class);
|
||||||
|
@ -105,16 +100,15 @@ class PushFeedbackProcessorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testEmpty() throws AccountDatabaseCrawlerRestartException {
|
void testEmpty() throws AccountDatabaseCrawlerRestartException {
|
||||||
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, directoryQueue);
|
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager);
|
||||||
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()), Collections.emptyList());
|
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()), Collections.emptyList());
|
||||||
|
|
||||||
verifyZeroInteractions(accountsManager);
|
verifyNoInteractions(accountsManager);
|
||||||
verifyZeroInteractions(directoryQueue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate() throws AccountDatabaseCrawlerRestartException {
|
void testUpdate() throws AccountDatabaseCrawlerRestartException {
|
||||||
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, directoryQueue);
|
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager);
|
||||||
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()), List.of(uninstalledAccount, mixedAccount, stillActiveAccount, freshAccount, cleanAccount, undiscoverableAccount));
|
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()), List.of(uninstalledAccount, mixedAccount, stillActiveAccount, freshAccount, cleanAccount, undiscoverableAccount));
|
||||||
|
|
||||||
verify(uninstalledDevice).setApnId(isNull());
|
verify(uninstalledDevice).setApnId(isNull());
|
||||||
|
@ -151,15 +145,6 @@ class PushFeedbackProcessorTest {
|
||||||
verify(stillActiveDevice, never()).setFetchesMessages(anyBoolean());
|
verify(stillActiveDevice, never()).setFetchesMessages(anyBoolean());
|
||||||
|
|
||||||
verify(accountsManager).update(eqUuid(stillActiveAccount), any());
|
verify(accountsManager).update(eqUuid(stillActiveAccount), any());
|
||||||
|
|
||||||
final ArgumentCaptor<List<Account>> refreshedAccountArgumentCaptor = ArgumentCaptor.forClass(List.class);
|
|
||||||
verify(directoryQueue).refreshRegisteredUsers(refreshedAccountArgumentCaptor.capture());
|
|
||||||
|
|
||||||
final List<UUID> refreshedUuids = refreshedAccountArgumentCaptor.getValue().stream()
|
|
||||||
.map(Account::getUuid)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
assertTrue(refreshedUuids.containsAll(List.of(undiscoverableAccount.getUuid(), uninstalledAccount.getUuid())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue