Compare commits

...

8 Commits

Author SHA1 Message Date
katherine-signal
fb4ed20ff5 Remove groups v2 capability
* wip removing groups v2 capabilities

* comments

* finish removing groups v2 references

* hardcode gv1migration flag on user capability, remove other references
2022-11-21 09:31:47 -08:00
Jon Chambers
cb50b44d8f Allow the account cleaner to operate on multiple accounts in parallel 2022-11-18 11:15:00 -05:00
Jon Chambers
ae57853ec4 Simplify deletion reason reporting 2022-11-18 11:15:00 -05:00
Jon Chambers
2881c0fd7e Allow the account cleaner to act on all accounts in a crawled chunk 2022-11-18 11:15:00 -05:00
Chris Eager
483fb0968b Use badge name in level configuration for one-time donations 2022-11-18 11:05:23 -05:00
Jon Chambers
4d37418c15 Update to the latest version of the abusive message filter 2022-11-18 10:55:15 -05:00
Jon Chambers
e8ee4b50ff Retire the legacy "abusive hosts" system in favor of newer tools 2022-11-18 10:54:25 -05:00
Chris Eager
4f8aa2eee2 Mark flaky test @Disabled 2022-11-17 13:23:42 -06:00
22 changed files with 114 additions and 691 deletions

View File

@@ -163,7 +163,6 @@ import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
import org.whispersystems.textsecuregcm.storage.AccountCleaner;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
@@ -404,6 +403,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
ExecutorService fcmSenderExecutor = environment.lifecycle().executorService(name(getClass(), "fcmSender-%d")).maxThreads(32).minThreads(32).workQueue(fcmSenderQueue).build();
ExecutorService backupServiceExecutor = environment.lifecycle().executorService(name(getClass(), "backupService-%d")).maxThreads(1).minThreads(1).build();
ExecutorService storageServiceExecutor = environment.lifecycle().executorService(name(getClass(), "storageService-%d")).maxThreads(1).minThreads(1).build();
ExecutorService accountDeletionExecutor = environment.lifecycle().executorService(name(getClass(), "accountCleaner-%d")).maxThreads(16).minThreads(16).build();
// TODO: generally speaking this is a DynamoDB I/O executor for the accounts table; we should eventually have a general executor for speaking to the accounts table, but most of the server is still synchronous so this isn't widely useful yet
ExecutorService batchIdentityCheckExecutor = environment.lifecycle().executorService(name(getClass(), "batchIdentityCheck-%d")).minThreads(32).maxThreads(32).build();
@@ -457,7 +457,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
ExternalServiceCredentialGenerator paymentsCredentialsGenerator = new ExternalServiceCredentialGenerator(
config.getPaymentsServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
AbusiveHostRules abusiveHostRules = new AbusiveHostRules(rateLimitersCluster, dynamicConfigurationManager);
RegistrationServiceClient registrationServiceClient = new RegistrationServiceClient(config.getRegistrationServiceConfiguration().getHost(), config.getRegistrationServiceConfiguration().getPort(), config.getRegistrationServiceConfiguration().getApiKey(), config.getRegistrationServiceConfiguration().getRegistrationCaCertificate(), registrationCallbackExecutor);
SecureBackupClient secureBackupClient = new SecureBackupClient(backupCredentialsGenerator, backupServiceExecutor, config.getSecureBackupServiceConfiguration());
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, storageServiceExecutor, config.getSecureStorageServiceConfiguration());
@@ -554,7 +553,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new AccountDatabaseCrawlerCache(cacheCluster, AccountDatabaseCrawlerCache.ACCOUNT_CLEANER_PREFIX);
AccountDatabaseCrawler accountCleanerAccountDatabaseCrawler = new AccountDatabaseCrawler("Account cleaner crawler",
accountsManager,
accountCleanerAccountDatabaseCrawlerCache, List.of(new AccountCleaner(accountsManager)),
accountCleanerAccountDatabaseCrawlerCache, List.of(new AccountCleaner(accountsManager, accountDeletionExecutor)),
config.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
config.getAccountDatabaseCrawlerConfiguration().getChunkIntervalMs()
);
@@ -653,7 +652,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
// these should be common, but use @Auth DisabledPermittedAccount, which isnt supported yet on websocket
environment.jersey().register(
new AccountController(pendingAccountsManager, accountsManager, abusiveHostRules, rateLimiters,
new AccountController(pendingAccountsManager, accountsManager, rateLimiters,
registrationServiceClient, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
recaptchaClient, pushNotificationManager, changeNumberManager, backupCredentialsGenerator,
clientPresenceManager, clock));

View File

@@ -91,7 +91,6 @@ import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
import org.whispersystems.textsecuregcm.registration.ClientType;
import org.whispersystems.textsecuregcm.registration.MessageTransport;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
@@ -115,8 +114,6 @@ public class AccountController {
private final Logger logger = LoggerFactory.getLogger(AccountController.class);
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
private final Meter blockedHostMeter = metricRegistry.meter(name(AccountController.class, "blocked_host" ));
private final Meter countryFilterApplicable = metricRegistry.meter(name(AccountController.class, "country_filter_applicable"));
private final Meter countryFilteredHostMeter = metricRegistry.meter(name(AccountController.class, "country_limited_host" ));
private final Meter rateLimitedHostMeter = metricRegistry.meter(name(AccountController.class, "rate_limited_host" ));
private final Meter rateLimitedPrefixMeter = metricRegistry.meter(name(AccountController.class, "rate_limited_prefix" ));
@@ -150,7 +147,6 @@ public class AccountController {
private final StoredVerificationCodeManager pendingAccounts;
private final AccountsManager accounts;
private final AbusiveHostRules abusiveHostRules;
private final RateLimiters rateLimiters;
private final RegistrationServiceClient registrationServiceClient;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
@@ -171,7 +167,6 @@ public class AccountController {
public AccountController(
StoredVerificationCodeManager pendingAccounts,
AccountsManager accounts,
AbusiveHostRules abusiveHostRules,
RateLimiters rateLimiters,
RegistrationServiceClient registrationServiceClient,
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
@@ -186,7 +181,6 @@ public class AccountController {
) {
this.pendingAccounts = pendingAccounts;
this.accounts = accounts;
this.abusiveHostRules = abusiveHostRules;
this.rateLimiters = rateLimiters;
this.registrationServiceClient = registrationServiceClient;
this.dynamicConfigurationManager = dynamicConfigurationManager;
@@ -204,7 +198,6 @@ public class AccountController {
public AccountController(
StoredVerificationCodeManager pendingAccounts,
AccountsManager accounts,
AbusiveHostRules abusiveHostRules,
RateLimiters rateLimiters,
RegistrationServiceClient registrationServiceClient,
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
@@ -215,7 +208,7 @@ public class AccountController {
ChangeNumberManager changeNumberManager,
ExternalServiceCredentialGenerator backupServiceCredentialGenerator
) {
this(pendingAccounts, accounts, abusiveHostRules, rateLimiters,
this(pendingAccounts, accounts, rateLimiters,
registrationServiceClient, dynamicConfigurationManager, turnTokenGenerator, testDevices, recaptchaClient,
pushNotificationManager, changeNumberManager,
backupServiceCredentialGenerator, null, Clock.systemUTC());
@@ -886,26 +879,12 @@ public class AccountController {
boolean countryFiltered = captchaConfig.getSignupCountryCodes().contains(countryCode) ||
captchaConfig.getSignupRegions().contains(region);
if (abusiveHostRules.isBlocked(sourceHost)) {
blockedHostMeter.mark();
logger.info("Blocked host: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor);
if (countryFiltered) {
// this host was caught in the abusiveHostRules filter, but
// would be caught by country filter as well
countryFilterApplicable.mark();
}
return true;
}
try {
rateLimiters.getSmsVoiceIpLimiter().validate(sourceHost);
} catch (RateLimitExceededException e) {
logger.info("Rate limit exceeded: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor);
rateLimitedHostMeter.mark();
if (shouldAutoBlock(sourceHost)) {
logger.info("Auto-block: {}", sourceHost);
abusiveHostRules.setBlockedHost(sourceHost);
}
return true;
}
@@ -914,10 +893,7 @@ public class AccountController {
} catch (RateLimitExceededException e) {
logger.info("Prefix rate limit exceeded: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor);
rateLimitedPrefixMeter.mark();
if (shouldAutoBlock(sourceHost)) {
logger.info("Auto-block: {}", sourceHost);
abusiveHostRules.setBlockedHost(sourceHost);
}
return true;
}
@@ -925,6 +901,7 @@ public class AccountController {
countryFilteredHostMeter.mark();
return true;
}
return false;
}
@@ -944,16 +921,6 @@ public class AccountController {
}
}
private boolean shouldAutoBlock(String sourceHost) {
try {
rateLimiters.getAutoBlockLimiter().validate(sourceHost);
} catch (RateLimitExceededException e) {
return true;
}
return false;
}
private String generatePushChallenge() {
SecureRandom random = new SecureRandom();
byte[] challenge = new byte[16];

View File

@@ -188,7 +188,7 @@ public class DeviceController {
}
final DeviceCapabilities capabilities = accountAttributes.getCapabilities();
if (capabilities != null && isCapabilityDowngrade(account.get(), capabilities, userAgent)) {
if (capabilities != null && isCapabilityDowngrade(account.get(), capabilities)) {
throw new WebApplicationException(Response.status(409).build());
}
@@ -236,7 +236,7 @@ public class DeviceController {
return new VerificationCode(randomInt);
}
private boolean isCapabilityDowngrade(Account account, DeviceCapabilities capabilities, String userAgent) {
private boolean isCapabilityDowngrade(Account account, DeviceCapabilities capabilities) {
boolean isDowngrade = false;
isDowngrade |= account.isStoriesSupported() && !capabilities.isStories();
@@ -244,35 +244,8 @@ public class DeviceController {
isDowngrade |= account.isChangeNumberSupported() && !capabilities.isChangeNumber();
isDowngrade |= account.isAnnouncementGroupSupported() && !capabilities.isAnnouncementGroup();
isDowngrade |= account.isSenderKeySupported() && !capabilities.isSenderKey();
isDowngrade |= account.isGv1MigrationSupported() && !capabilities.isGv1Migration();
isDowngrade |= account.isGiftBadgesSupported() && !capabilities.isGiftBadges();
if (account.isGroupsV2Supported()) {
try {
switch (UserAgentUtil.parseUserAgentString(userAgent).getPlatform()) {
case DESKTOP:
case ANDROID: {
if (!capabilities.isGv2_3()) {
isDowngrade = true;
}
break;
}
case IOS: {
if (!capabilities.isGv2_2() && !capabilities.isGv2_3()) {
isDowngrade = true;
}
break;
}
}
} catch (final UnrecognizedUserAgentException e) {
// If we can't parse the UA string, the client is for sure too old to support groups V2
isDowngrade = true;
}
}
return isDowngrade;
}
}

View File

@@ -194,18 +194,22 @@ public class SubscriptionController {
levels.put(String.valueOf(levelId), levelConfiguration);
});
final Badge boostBadge = badgeTranslator.translate(acceptableLanguages,
oneTimeDonationConfiguration.boost().badge());
levels.put(String.valueOf(oneTimeDonationConfiguration.boost().level()),
new LevelConfiguration(
levelTranslator.translate(acceptableLanguages, oneTimeDonationConfiguration.boost().badge()),
boostBadge.getName(),
// NB: the one-time badges are PurchasableBadge, which has a `duration` field
new PurchasableBadge(
badgeTranslator.translate(acceptableLanguages, oneTimeDonationConfiguration.boost().badge()),
boostBadge,
oneTimeDonationConfiguration.boost().expiration())));
final Badge giftBadge = badgeTranslator.translate(acceptableLanguages, oneTimeDonationConfiguration.gift().badge());
levels.put(String.valueOf(oneTimeDonationConfiguration.gift().level()),
new LevelConfiguration(
levelTranslator.translate(acceptableLanguages, oneTimeDonationConfiguration.gift().badge()),
giftBadge.getName(),
new PurchasableBadge(
badgeTranslator.translate(acceptableLanguages, oneTimeDonationConfiguration.gift().badge()),
giftBadge,
oneTimeDonationConfiguration.gift().expiration())));
return new GetSubscriptionConfigurationResponse(currencyConfiguration, levels);

View File

@@ -12,8 +12,7 @@ public class UserCapabilities {
public static UserCapabilities createForAccount(Account account) {
return new UserCapabilities(
account.isGroupsV2Supported(),
account.isGv1MigrationSupported(),
true,
account.isSenderKeySupported(),
account.isAnnouncementGroupSupported(),
account.isChangeNumberSupported(),
@@ -21,9 +20,6 @@ public class UserCapabilities {
account.isGiftBadgesSupported());
}
@JsonProperty
private boolean gv2;
@JsonProperty("gv1-migration")
private boolean gv1Migration;
@@ -45,7 +41,7 @@ public class UserCapabilities {
public UserCapabilities() {
}
public UserCapabilities(final boolean gv2,
public UserCapabilities(
boolean gv1Migration,
final boolean senderKey,
final boolean announcementGroup,
@@ -53,7 +49,6 @@ public class UserCapabilities {
final boolean stories,
final boolean giftBadges) {
this.gv2 = gv2;
this.gv1Migration = gv1Migration;
this.senderKey = senderKey;
this.announcementGroup = announcementGroup;
@@ -62,10 +57,6 @@ public class UserCapabilities {
this.giftBadges = giftBadges;
}
public boolean isGv2() {
return gv2;
}
public boolean isGv1Migration() {
return gv1Migration;
}

View File

@@ -15,7 +15,6 @@ public class RateLimiters {
private final RateLimiter voiceDestinationDailyLimiter;
private final RateLimiter smsVoiceIpLimiter;
private final RateLimiter smsVoicePrefixLimiter;
private final RateLimiter autoBlockLimiter;
private final RateLimiter verifyLimiter;
private final RateLimiter pinLimiter;
@@ -60,10 +59,6 @@ public class RateLimiters {
config.getSmsVoicePrefix().getBucketSize(),
config.getSmsVoicePrefix().getLeakRatePerMinute());
this.autoBlockLimiter = new RateLimiter(cacheCluster, "autoBlock",
config.getAutoBlock().getBucketSize(),
config.getAutoBlock().getLeakRatePerMinute());
this.verifyLimiter = new LockingRateLimiter(cacheCluster, "verify",
config.getVerifyNumber().getBucketSize(),
config.getVerifyNumber().getLeakRatePerMinute());
@@ -158,10 +153,6 @@ public class RateLimiters {
return smsVoicePrefixLimiter;
}
public RateLimiter getAutoBlockLimiter() {
return autoBlockLimiter;
}
public RateLimiter getVoiceDestinationLimiter() {
return voiceDestinationLimiter;
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.storage;
import static com.codahale.metrics.MetricRegistry.name;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
import java.time.Duration;
import com.google.common.annotations.VisibleForTesting;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.util.Constants;
public class AbusiveHostRules {
private static final String KEY_PREFIX = "abusive_hosts::";
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
private final Timer getTimer = metricRegistry.timer(name(AbusiveHostRules.class, "get"));
private final Timer insertTimer = metricRegistry.timer(name(AbusiveHostRules.class, "setBlockedHost"));
private final FaultTolerantRedisCluster redisCluster;
private final DynamicConfigurationManager<DynamicConfiguration> configurationManager;
public AbusiveHostRules(FaultTolerantRedisCluster redisCluster, final DynamicConfigurationManager<DynamicConfiguration> configurationManager) {
this.redisCluster = redisCluster;
this.configurationManager = configurationManager;
}
public boolean isBlocked(String host) {
try (Timer.Context timer = getTimer.time()) {
return this.redisCluster.withCluster(connection -> connection.sync().exists(prefix(host))) > 0;
}
}
public void setBlockedHost(String host) {
Duration expireTime = configurationManager.getConfiguration().getAbusiveHostRules().getExpirationTime();
try (Timer.Context timer = insertTimer.time()) {
this.redisCluster.useCluster(connection -> connection.sync().setex(prefix(host), expireTime.toSeconds(), "1"));
}
}
@VisibleForTesting
public static String prefix(String keyName) {
return KEY_PREFIX + keyName;
}
}

View File

@@ -181,14 +181,6 @@ public class Account {
return devices.stream().filter(device -> device.getId() == deviceId).findFirst();
}
public boolean isGroupsV2Supported() {
requireNotStale();
return devices.stream()
.filter(Device::isEnabled)
.allMatch(Device::isGroupsV2Supported);
}
public boolean isStorageSupported() {
requireNotStale();
@@ -201,10 +193,6 @@ public class Account {
return getMasterDevice().map(Device::getCapabilities).map(Device.DeviceCapabilities::isTransfer).orElse(false);
}
public boolean isGv1MigrationSupported() {
return allEnabledDevicesHaveCapability(DeviceCapabilities::isGv1Migration);
}
public boolean isSenderKeySupported() {
return allEnabledDevicesHaveCapability(DeviceCapabilities::isSenderKey);
}

View File

@@ -4,20 +4,21 @@
*/
package org.whispersystems.textsecuregcm.storage;
import com.google.common.annotations.VisibleForTesting;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
import io.micrometer.core.instrument.Metrics;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.util.Util;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
public class AccountCleaner extends AccountDatabaseCrawlerListener {
private static final Logger log = LoggerFactory.getLogger(AccountCleaner.class);
@@ -25,13 +26,12 @@ public class AccountCleaner extends AccountDatabaseCrawlerListener {
private static final String DELETED_ACCOUNT_COUNTER_NAME = name(AccountCleaner.class, "deletedAccounts");
private static final String DELETION_REASON_TAG_NAME = "reason";
@VisibleForTesting
static final int MAX_ACCOUNT_DELETIONS_PER_CHUNK = 256;
private final AccountsManager accountsManager;
private final Executor deletionExecutor;
public AccountCleaner(AccountsManager accountsManager) {
public AccountCleaner(final AccountsManager accountsManager, final Executor deletionExecutor) {
this.accountsManager = accountsManager;
this.deletionExecutor = deletionExecutor;
}
@Override
@@ -44,30 +44,34 @@ public class AccountCleaner extends AccountDatabaseCrawlerListener {
@Override
protected void onCrawlChunk(Optional<UUID> fromUuid, List<Account> chunkAccounts) {
int accountUpdateCount = 0;
final List<CompletableFuture<Void>> deletionFutures = new ArrayList<>();
for (Account account : chunkAccounts) {
if (isExpired(account) || needsExplicitRemoval(account)) {
final Tag deletionReason;
final String deletionReason = needsExplicitRemoval(account) ? "newlyExpired" : "previouslyExpired";
if (needsExplicitRemoval(account)) {
deletionReason = Tag.of(DELETION_REASON_TAG_NAME, "newlyExpired");
} else {
deletionReason = Tag.of(DELETION_REASON_TAG_NAME, "previouslyExpired");
}
if (accountUpdateCount < MAX_ACCOUNT_DELETIONS_PER_CHUNK) {
try {
accountsManager.delete(account, AccountsManager.DeletionReason.EXPIRED);
accountUpdateCount++;
Metrics.counter(DELETED_ACCOUNT_COUNTER_NAME, Tags.of(deletionReason)).increment();
} catch (final Exception e) {
log.warn("Failed to delete account {}", account.getUuid(), e);
}
}
deletionFutures.add(CompletableFuture.runAsync(() -> {
try {
accountsManager.delete(account, AccountsManager.DeletionReason.EXPIRED);
} catch (final InterruptedException e) {
throw new CompletionException(e);
}
}, deletionExecutor)
.whenComplete((ignored, throwable) -> {
if (throwable != null) {
log.warn("Failed to delete account {}", account.getUuid(), throwable);
} else {
Metrics.counter(DELETED_ACCOUNT_COUNTER_NAME, DELETION_REASON_TAG_NAME, deletionReason).increment();
}
}));
}
}
try {
CompletableFuture.allOf(deletionFutures.toArray(new CompletableFuture[0])).join();
} catch (final Exception e) {
log.debug("Failed to delete one or more accounts in chunk", e);
}
}
private boolean needsExplicitRemoval(Account account) {

View File

@@ -252,39 +252,13 @@ public class Device {
return this.userAgent;
}
public boolean isGroupsV2Supported() {
final boolean groupsV2Supported;
if (this.capabilities != null) {
final boolean ios = this.apnId != null || this.voipApnId != null;
groupsV2Supported = this.capabilities.isGv2_3() || (ios && this.capabilities.isGv2_2());
} else {
groupsV2Supported = false;
}
return groupsV2Supported;
}
public static class DeviceCapabilities {
@JsonProperty
private boolean gv2;
@JsonProperty("gv2-2")
private boolean gv2_2;
@JsonProperty("gv2-3")
private boolean gv2_3;
@JsonProperty
private boolean storage;
@JsonProperty
private boolean transfer;
@JsonProperty("gv1-migration")
private boolean gv1Migration;
@JsonProperty
private boolean senderKey;
@@ -306,15 +280,11 @@ public class Device {
public DeviceCapabilities() {
}
public DeviceCapabilities(boolean gv2, final boolean gv2_2, final boolean gv2_3, boolean storage, boolean transfer,
boolean gv1Migration, final boolean senderKey, final boolean announcementGroup, final boolean changeNumber,
public DeviceCapabilities(boolean storage, boolean transfer,
final boolean senderKey, final boolean announcementGroup, final boolean changeNumber,
final boolean pni, final boolean stories, final boolean giftBadges) {
this.gv2 = gv2;
this.gv2_2 = gv2_2;
this.gv2_3 = gv2_3;
this.storage = storage;
this.transfer = transfer;
this.gv1Migration = gv1Migration;
this.senderKey = senderKey;
this.announcementGroup = announcementGroup;
this.changeNumber = changeNumber;
@@ -323,18 +293,6 @@ public class Device {
this.giftBadges = giftBadges;
}
public boolean isGv2() {
return gv2;
}
public boolean isGv2_2() {
return gv2_2;
}
public boolean isGv2_3() {
return gv2_3;
}
public boolean isStorage() {
return storage;
}
@@ -343,10 +301,6 @@ public class Device {
return transfer;
}
public boolean isGv1Migration() {
return gv1Migration;
}
public boolean isSenderKey() {
return senderKey;
}

View File

@@ -22,7 +22,6 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -100,7 +99,6 @@ import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
import org.whispersystems.textsecuregcm.registration.ClientType;
import org.whispersystems.textsecuregcm.registration.MessageTransport;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
@@ -134,7 +132,6 @@ class AccountControllerTest {
private static final UUID SENDER_TRANSFER_UUID = UUID.randomUUID();
private static final UUID RESERVATION_TOKEN = UUID.randomUUID();
private static final String ABUSIVE_HOST = "192.168.1.1";
private static final String NICE_HOST = "127.0.0.1";
private static final String RATE_LIMITED_IP_HOST = "10.0.0.1";
private static final String RATE_LIMITED_PREFIX_HOST = "10.0.0.2";
@@ -147,7 +144,6 @@ class AccountControllerTest {
private static StoredVerificationCodeManager pendingAccountsManager = mock(StoredVerificationCodeManager.class);
private static AccountsManager accountsManager = mock(AccountsManager.class);
private static AbusiveHostRules abusiveHostRules = mock(AbusiveHostRules.class);
private static RateLimiters rateLimiters = mock(RateLimiters.class);
private static RateLimiter rateLimiter = mock(RateLimiter.class);
private static RateLimiter pinLimiter = mock(RateLimiter.class);
@@ -187,7 +183,6 @@ class AccountControllerTest {
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new AccountController(pendingAccountsManager,
accountsManager,
abusiveHostRules,
rateLimiters,
registrationServiceClient,
dynamicConfigurationManager,
@@ -218,7 +213,6 @@ class AccountControllerTest {
when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter);
when(rateLimiters.getSmsVoiceIpLimiter()).thenReturn(smsVoiceIpLimiter);
when(rateLimiters.getSmsVoicePrefixLimiter()).thenReturn(smsVoicePrefixLimiter);
when(rateLimiters.getAutoBlockLimiter()).thenReturn(autoBlockLimiter);
when(rateLimiters.getUsernameSetLimiter()).thenReturn(usernameSetLimiter);
when(rateLimiters.getUsernameReserveLimiter()).thenReturn(usernameReserveLimiter);
when(rateLimiters.getUsernameLookupLimiter()).thenReturn(usernameLookupLimiter);
@@ -303,9 +297,6 @@ class AccountControllerTest {
when(dynamicConfiguration.getCaptchaConfiguration()).thenReturn(signupCaptchaConfig);
}
when(abusiveHostRules.isBlocked(eq(ABUSIVE_HOST))).thenReturn(true);
when(abusiveHostRules.isBlocked(eq(NICE_HOST))).thenReturn(false);
when(recaptchaClient.verify(eq(INVALID_CAPTCHA_TOKEN), anyString()))
.thenReturn(RecaptchaClient.AssessmentResult.invalid());
when(recaptchaClient.verify(eq(VALID_CAPTCHA_TOKEN), anyString()))
@@ -313,9 +304,6 @@ class AccountControllerTest {
doThrow(new RateLimitExceededException(Duration.ZERO)).when(pinLimiter).validate(eq(SENDER_OVER_PIN));
doThrow(new RateLimitExceededException(Duration.ZERO)).when(autoBlockLimiter).validate(eq(RATE_LIMITED_PREFIX_HOST));
doThrow(new RateLimitExceededException(Duration.ZERO)).when(autoBlockLimiter).validate(eq(RATE_LIMITED_IP_HOST));
doThrow(new RateLimitExceededException(Duration.ZERO)).when(smsVoicePrefixLimiter).validate(SENDER_OVER_PREFIX.substring(0, 4+2));
doThrow(new RateLimitExceededException(Duration.ZERO)).when(smsVoiceIpLimiter).validate(RATE_LIMITED_IP_HOST);
doThrow(new RateLimitExceededException(Duration.ZERO)).when(smsVoiceIpLimiter).validate(RATE_LIMITED_HOST2);
@@ -326,13 +314,11 @@ class AccountControllerTest {
reset(
pendingAccountsManager,
accountsManager,
abusiveHostRules,
rateLimiters,
rateLimiter,
pinLimiter,
smsVoiceIpLimiter,
smsVoicePrefixLimiter,
autoBlockLimiter,
usernameSetLimiter,
usernameReserveLimiter,
usernameLookupLimiter,
@@ -489,7 +475,6 @@ class AccountControllerTest {
final Phonenumber.PhoneNumber expectedPhoneNumber = PhoneNumberUtil.getInstance().parse(SENDER, null);
verify(registrationServiceClient).sendRegistrationCode(expectedPhoneNumber, MessageTransport.SMS, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT);
verify(abusiveHostRules).isBlocked(eq(NICE_HOST));
verify(pendingAccountsManager).store(eq(SENDER), argThat(
storedVerificationCode -> Arrays.equals(storedVerificationCode.sessionId(), sessionId) &&
"1234-push".equals(storedVerificationCode.pushCode())));
@@ -550,7 +535,6 @@ class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(200);
verify(registrationServiceClient).sendRegistrationCode(phoneNumber, MessageTransport.VOICE, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT);
verify(abusiveHostRules).isBlocked(eq(NICE_HOST));
}
@Test
@@ -572,7 +556,6 @@ class AccountControllerTest {
final Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtil.getInstance().parse(SENDER_PREAUTH, null);
verify(registrationServiceClient).sendRegistrationCode(phoneNumber, MessageTransport.SMS, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT);
verify(abusiveHostRules).isBlocked(eq(NICE_HOST));
}
@Test
@@ -588,7 +571,6 @@ class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(403);
verifyNoInteractions(registrationServiceClient);
verifyNoInteractions(abusiveHostRules);
}
@Test
@@ -649,24 +631,7 @@ class AccountControllerTest {
}
@Test
void testSendAbusiveHost() {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
.queryParam("challenge", "1234-push")
.request()
.header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST)
.get();
assertThat(response.getStatus()).isEqualTo(402);
verify(abusiveHostRules).isBlocked(eq(ABUSIVE_HOST));
verifyNoInteractions(registrationServiceClient);
}
@Test
void testSendAbusiveHostWithValidCaptcha() throws NumberParseException {
void testSendWithValidCaptcha() throws NumberParseException {
when(registrationServiceClient.sendRegistrationCode(any(), any(), any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(new byte[16]));
@@ -676,38 +641,36 @@ class AccountControllerTest {
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
.queryParam("captcha", VALID_CAPTCHA_TOKEN)
.request()
.header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST)
.header("X-Forwarded-For", NICE_HOST)
.get();
assertThat(response.getStatus()).isEqualTo(200);
final Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtil.getInstance().parse(SENDER, null);
verifyNoInteractions(abusiveHostRules);
verify(recaptchaClient).verify(eq(VALID_CAPTCHA_TOKEN), eq(ABUSIVE_HOST));
verify(recaptchaClient).verify(eq(VALID_CAPTCHA_TOKEN), eq(NICE_HOST));
verify(registrationServiceClient).sendRegistrationCode(phoneNumber, MessageTransport.SMS, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT);
}
@Test
void testSendAbusiveHostWithInvalidCaptcha() {
void testSendWithInvalidCaptcha() {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
.queryParam("captcha", INVALID_CAPTCHA_TOKEN)
.request()
.header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST)
.header("X-Forwarded-For", NICE_HOST)
.get();
assertThat(response.getStatus()).isEqualTo(402);
verifyNoInteractions(abusiveHostRules);
verify(recaptchaClient).verify(eq(INVALID_CAPTCHA_TOKEN), eq(ABUSIVE_HOST));
verify(recaptchaClient).verify(eq(INVALID_CAPTCHA_TOKEN), eq(NICE_HOST));
verifyNoInteractions(registrationServiceClient);
}
@Test
void testSendRateLimitedHostAutoBlock() {
void testSendRateLimitedHost() {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
@@ -718,10 +681,6 @@ class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(402);
verify(abusiveHostRules).isBlocked(eq(RATE_LIMITED_IP_HOST));
verify(abusiveHostRules).setBlockedHost(eq(RATE_LIMITED_IP_HOST));
verifyNoMoreInteractions(abusiveHostRules);
verifyNoInteractions(recaptchaClient);
verifyNoInteractions(registrationServiceClient);
}
@@ -739,55 +698,10 @@ class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(402);
verify(abusiveHostRules).isBlocked(eq(RATE_LIMITED_PREFIX_HOST));
verify(abusiveHostRules).setBlockedHost(eq(RATE_LIMITED_PREFIX_HOST));
verifyNoMoreInteractions(abusiveHostRules);
verifyNoInteractions(recaptchaClient);
verifyNoInteractions(registrationServiceClient);
}
@Test
void testSendRateLimitedHostNoAutoBlock() {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
.queryParam("challenge", "1234-push")
.request()
.header(HttpHeaders.X_FORWARDED_FOR, RATE_LIMITED_HOST2)
.get();
assertThat(response.getStatus()).isEqualTo(402);
verify(abusiveHostRules).isBlocked(eq(RATE_LIMITED_HOST2));
verifyNoMoreInteractions(abusiveHostRules);
verifyNoInteractions(recaptchaClient);
verifyNoInteractions(registrationServiceClient);
}
@Test
void testSendMultipleHost() {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
.queryParam("challenge", "1234-push")
.request()
.header(HttpHeaders.X_FORWARDED_FOR, NICE_HOST + ", " + ABUSIVE_HOST)
.get();
assertThat(response.getStatus()).isEqualTo(402);
verify(abusiveHostRules, times(1)).isBlocked(eq(ABUSIVE_HOST));
verifyNoMoreInteractions(abusiveHostRules);
verifyNoInteractions(registrationServiceClient);
}
@Test
void testSendRestrictedHostOut() {
@@ -805,7 +719,6 @@ class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(402);
verify(abusiveHostRules).isBlocked(eq(NICE_HOST));
verifyNoInteractions(registrationServiceClient);
}
@@ -884,7 +797,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target(String.format("/v1/accounts/sms/code/%s", TEST_NUMBER))
.request()
.header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST)
.header("X-Forwarded-For", RATE_LIMITED_IP_HOST)
.get();
final ArgumentCaptor<StoredVerificationCode> captor = ArgumentCaptor.forClass(StoredVerificationCode.class);

View File

@@ -193,8 +193,6 @@ class ProfileControllerTest {
when(profileAccount.getUuid()).thenReturn(AuthHelper.VALID_UUID_TWO);
when(profileAccount.getPhoneNumberIdentifier()).thenReturn(AuthHelper.VALID_PNI_TWO);
when(profileAccount.isEnabled()).thenReturn(true);
when(profileAccount.isGroupsV2Supported()).thenReturn(false);
when(profileAccount.isGv1MigrationSupported()).thenReturn(false);
when(profileAccount.isSenderKeySupported()).thenReturn(false);
when(profileAccount.isAnnouncementGroupSupported()).thenReturn(false);
when(profileAccount.isChangeNumberSupported()).thenReturn(false);
@@ -207,8 +205,6 @@ class ProfileControllerTest {
when(capabilitiesAccount.getIdentityKey()).thenReturn(ACCOUNT_IDENTITY_KEY);
when(capabilitiesAccount.getPhoneNumberIdentityKey()).thenReturn(ACCOUNT_PHONE_NUMBER_IDENTITY_KEY);
when(capabilitiesAccount.isEnabled()).thenReturn(true);
when(capabilitiesAccount.isGroupsV2Supported()).thenReturn(true);
when(capabilitiesAccount.isGv1MigrationSupported()).thenReturn(true);
when(capabilitiesAccount.isSenderKeySupported()).thenReturn(true);
when(capabilitiesAccount.isAnnouncementGroupSupported()).thenReturn(true);
when(capabilitiesAccount.isChangeNumberSupported()).thenReturn(true);
@@ -396,7 +392,6 @@ class ProfileControllerTest {
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(BaseProfileResponse.class);
assertThat(profile.getCapabilities().isGv2()).isTrue();
assertThat(profile.getCapabilities().isGv1Migration()).isTrue();
assertThat(profile.getCapabilities().isSenderKey()).isTrue();
assertThat(profile.getCapabilities().isAnnouncementGroup()).isTrue();
@@ -408,8 +403,7 @@ class ProfileControllerTest {
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
.get(BaseProfileResponse.class);
assertThat(profile.getCapabilities().isGv2()).isFalse();
assertThat(profile.getCapabilities().isGv1Migration()).isFalse();
assertThat(profile.getCapabilities().isGv1Migration()).isTrue();
assertThat(profile.getCapabilities().isSenderKey()).isFalse();
assertThat(profile.getCapabilities().isAnnouncementGroup()).isFalse();
}
@@ -753,8 +747,7 @@ class ProfileControllerTest {
assertThat(profile.getAbout()).isEqualTo("about");
assertThat(profile.getAboutEmoji()).isEqualTo("emoji");
assertThat(profile.getAvatar()).isEqualTo("profiles/validavatar");
assertThat(profile.getBaseProfileResponse().getCapabilities().isGv2()).isFalse();
assertThat(profile.getBaseProfileResponse().getCapabilities().isGv1Migration()).isFalse();
assertThat(profile.getBaseProfileResponse().getCapabilities().isGv1Migration()).isTrue();
assertThat(profile.getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID_TWO);
assertThat(profile.getBaseProfileResponse().getBadges()).hasSize(1).element(0).has(new Condition<>(
badge -> "Test Badge".equals(badge.getName()), "has badge with expected name"));

View File

@@ -392,8 +392,6 @@ class SubscriptionControllerTest {
when(LEVEL_TRANSLATOR.translate(any(), eq("B1"))).thenReturn("Z1");
when(LEVEL_TRANSLATOR.translate(any(), eq("B2"))).thenReturn("Z2");
when(LEVEL_TRANSLATOR.translate(any(), eq("B3"))).thenReturn("Z3");
when(LEVEL_TRANSLATOR.translate(any(), eq("BOOST"))).thenReturn("ZBOOST");
when(LEVEL_TRANSLATOR.translate(any(), eq("GIFT"))).thenReturn("ZGIFT");
GetSubscriptionConfigurationResponse response = RESOURCE_EXTENSION.target("/v1/subscription/configuration")
.request()
@@ -445,7 +443,7 @@ class SubscriptionControllerTest {
assertThat(response.levels()).containsKeys("1", "5", "15", "35", "100").satisfies(levelsMap -> {
assertThat(levelsMap).extractingByKey("1").satisfies(level -> {
assertThat(level.name()).isEqualTo("ZBOOST");
assertThat(level.name()).isEqualTo("boost1"); // level name is the same as badge name
assertThat(level).extracting(SubscriptionController.LevelConfiguration::badge).satisfies(badge -> {
assertThat(badge.getId()).isEqualTo("BOOST");
assertThat(badge.getName()).isEqualTo("boost1");
@@ -453,7 +451,7 @@ class SubscriptionControllerTest {
});
assertThat(levelsMap).extractingByKey("100").satisfies(level -> {
assertThat(level.name()).isEqualTo("ZGIFT");
assertThat(level.name()).isEqualTo("gift1"); // level name is the same as badge name
assertThat(level).extracting(SubscriptionController.LevelConfiguration::badge).satisfies(badge -> {
assertThat(badge.getId()).isEqualTo("GIFT");
assertThat(badge.getName()).isEqualTo("gift1");

View File

@@ -8,19 +8,19 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
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 java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.storage.AccountsManager.DeletionReason;
class AccountCleanerTest {
@@ -35,6 +35,8 @@ class AccountCleanerTest {
private final Device undeletedDisabledDevice = mock(Device.class );
private final Device undeletedEnabledDevice = mock(Device.class );
private ExecutorService deletionExecutor;
@BeforeEach
void setup() {
@@ -64,11 +66,19 @@ class AccountCleanerTest {
when(undeletedEnabledAccount.getNumber()).thenReturn("+14153333333");
when(undeletedEnabledAccount.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(364));
when(undeletedEnabledAccount.getUuid()).thenReturn(UUID.randomUUID());
deletionExecutor = Executors.newFixedThreadPool(2);
}
@AfterEach
void tearDown() throws InterruptedException {
deletionExecutor.shutdown();
deletionExecutor.awaitTermination(2, TimeUnit.SECONDS);
}
@Test
void testAccounts() throws AccountDatabaseCrawlerRestartException, InterruptedException {
AccountCleaner accountCleaner = new AccountCleaner(accountsManager);
AccountCleaner accountCleaner = new AccountCleaner(accountsManager, deletionExecutor);
accountCleaner.onCrawlStart();
accountCleaner.timeAndProcessCrawlChunk(Optional.empty(), Arrays.asList(deletedDisabledAccount, undeletedDisabledAccount, undeletedEnabledAccount));
accountCleaner.onCrawlEnd(Optional.empty());
@@ -80,25 +90,4 @@ class AccountCleanerTest {
verifyNoMoreInteractions(accountsManager);
}
@Test
void testMaxAccountUpdates() throws AccountDatabaseCrawlerRestartException, InterruptedException {
List<Account> accounts = new LinkedList<>();
accounts.add(undeletedEnabledAccount);
int activeExpiredAccountCount = AccountCleaner.MAX_ACCOUNT_DELETIONS_PER_CHUNK + 1;
for (int addedAccountCount = 0; addedAccountCount < activeExpiredAccountCount; addedAccountCount++) {
accounts.add(undeletedDisabledAccount);
}
accounts.add(deletedDisabledAccount);
AccountCleaner accountCleaner = new AccountCleaner(accountsManager);
accountCleaner.onCrawlStart();
accountCleaner.timeAndProcessCrawlChunk(Optional.empty(), accounts);
accountCleaner.onCrawlEnd(Optional.empty());
verify(accountsManager, times(AccountCleaner.MAX_ACCOUNT_DELETIONS_PER_CHUNK)).delete(undeletedDisabledAccount, AccountsManager.DeletionReason.EXPIRED);
verifyNoMoreInteractions(accountsManager);
}
}

View File

@@ -603,7 +603,7 @@ class AccountsManagerTest {
@ValueSource(booleans = {true, false})
void testCreateWithStorageCapability(final boolean hasStorage) throws InterruptedException {
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true,
new DeviceCapabilities(false, false, false, hasStorage, false, false, false, false, false, false, false, false));
new DeviceCapabilities(hasStorage, false, false, false, false, false, false, false));
final Account account = accountsManager.create("+18005550123", "password", null, attributes, new ArrayList<>());

View File

@@ -73,66 +73,4 @@ class DeviceTest {
Arguments.of(false, true, null, null, mock(SignedPreKey.class), Duration.ofDays(1), true)
);
}
@ParameterizedTest
@MethodSource("argumentsForTestIsGroupsV2Supported")
void testIsGroupsV2Supported(final boolean master, final String apnId, final boolean gv2Capability,
final boolean gv2_2Capability, final boolean gv2_3Capability, final boolean expectGv2Supported) {
final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability,
gv2_3Capability, false, false, false,
false, false, false, false, false, false);
final Device device = new Device();
device.setId(master ? Device.MASTER_ID : Device.MASTER_ID + 1);
device.setApnId(apnId);
device.setCapabilities(capabilities);
assertEquals(expectGv2Supported, device.isGroupsV2Supported());
}
private static Stream<Arguments> argumentsForTestIsGroupsV2Supported() {
return Stream.of(
// master apnId gv2 gv2-2 gv2-3 capable
// Android master
Arguments.of(true, null, false, false, false, false),
Arguments.of(true, null, true, false, false, false),
Arguments.of(true, null, false, true, false, false),
Arguments.of(true, null, true, true, false, false),
Arguments.of(true, null, false, false, true, true),
Arguments.of(true, null, true, false, true, true),
Arguments.of(true, null, false, true, true, true),
Arguments.of(true, null, true, true, true, true),
// iOS master
Arguments.of(true, "apn-id", false, false, false, false),
Arguments.of(true, "apn-id", true, false, false, false),
Arguments.of(true, "apn-id", false, true, false, true),
Arguments.of(true, "apn-id", true, true, false, true),
Arguments.of(true, "apn-id", false, false, true, true),
Arguments.of(true, "apn-id", true, false, true, true),
Arguments.of(true, "apn-id", false, true, true, true),
Arguments.of(true, "apn-id", true, true, true, true),
// iOS linked
Arguments.of(false, "apn-id", false, false, false, false),
Arguments.of(false, "apn-id", true, false, false, false),
Arguments.of(false, "apn-id", false, true, false, true),
Arguments.of(false, "apn-id", true, true, false, true),
Arguments.of(false, "apn-id", false, false, true, true),
Arguments.of(false, "apn-id", true, false, true, true),
Arguments.of(false, "apn-id", false, true, true, true),
Arguments.of(false, "apn-id", true, true, true, true),
// desktop linked
Arguments.of(false, null, false, false, false, false),
Arguments.of(false, null, true, false, false, false),
Arguments.of(false, null, false, true, false, false),
Arguments.of(false, null, true, true, false, false),
Arguments.of(false, null, false, false, true, true),
Arguments.of(false, null, true, false, true, true),
Arguments.of(false, null, false, true, true, true),
Arguments.of(false, null, true, true, true, true)
);
}
}

View File

@@ -51,6 +51,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -568,6 +569,7 @@ class MessagesCacheTest {
}
@Test
@Disabled("flaky test")
void testGetAllMessagesLimitsAndBackpressure() {
// this test makes sure that we dont fetch and buffer all messages from the cache when the publisher
// is subscribed. Rather, we should be fetching in pages to satisfy downstream requests, so that memory usage

View File

@@ -124,8 +124,6 @@ class DeviceControllerTest {
when(account.getNumber()).thenReturn(AuthHelper.VALID_NUMBER);
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
when(account.isEnabled()).thenReturn(false);
when(account.isGroupsV2Supported()).thenReturn(true);
when(account.isGv1MigrationSupported()).thenReturn(true);
when(account.isSenderKeySupported()).thenReturn(true);
when(account.isAnnouncementGroupSupported()).thenReturn(true);
when(account.isChangeNumberSupported()).thenReturn(true);
@@ -303,78 +301,9 @@ class DeviceControllerTest {
verifyNoMoreInteractions(messagesManager);
}
@ParameterizedTest
@MethodSource
void deviceDowngradeCapabilitiesTest(final String userAgent, final boolean gv2, final boolean gv2_2,
final boolean gv2_3, final int expectedStatus) {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(gv2, gv2_2, gv2_3, true, false, true, true, true,
true, true, true, true);
AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources.getJerseyTest()
.target("/v1/devices/5678901")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, "password1"))
.header(HttpHeaders.USER_AGENT, userAgent)
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(expectedStatus);
if (expectedStatus >= 300) {
verifyNoMoreInteractions(messagesManager);
}
}
private static Stream<Arguments> deviceDowngradeCapabilitiesTest() {
return Stream.of(
// User-Agent gv2 gv2-2 gv2-3 expected
Arguments.of( "Signal-Android/4.68.3 Android/25", false, false, false, 409 ),
Arguments.of( "Signal-Android/4.68.3 Android/25", true, false, false, 409 ),
Arguments.of( "Signal-Android/4.68.3 Android/25", false, true, false, 409 ),
Arguments.of( "Signal-Android/4.68.3 Android/25", false, false, true, 200 ),
Arguments.of( "Signal-iOS/3.9.0", false, false, false, 409 ),
Arguments.of( "Signal-iOS/3.9.0", true, false, false, 409 ),
Arguments.of( "Signal-iOS/3.9.0", false, true, false, 200 ),
Arguments.of( "Signal-iOS/3.9.0", false, false, true, 200 ),
Arguments.of( "Signal-Desktop/1.32.0-beta.3", false, false, false, 409 ),
Arguments.of( "Signal-Desktop/1.32.0-beta.3", true, false, false, 409 ),
Arguments.of( "Signal-Desktop/1.32.0-beta.3", false, true, false, 409 ),
Arguments.of( "Signal-Desktop/1.32.0-beta.3", false, false, true, 200 ),
Arguments.of( "Old client with unparsable UA", false, false, false, 409 ),
Arguments.of( "Old client with unparsable UA", true, false, false, 409 ),
Arguments.of( "Old client with unparsable UA", false, true, false, 409 ),
Arguments.of( "Old client with unparsable UA", false, false, true, 409 )
);
}
@Test
void deviceDowngradeGv1MigrationTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, false, true, true,
true, true, true, true);
AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources.getJerseyTest()
.target("/v1/devices/5678901")
.request()
.header("authorization", AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, "password1"))
.header(HttpHeaders.USER_AGENT, "Signal-Android/4.68.3 Android/25")
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources.getJerseyTest()
.target("/v1/devices/5678901")
.request()
.header("authorization", AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, "password1"))
.header(HttpHeaders.USER_AGENT, "Signal-Android/4.68.3 Android/25")
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
void deviceDowngradeSenderKeyTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, false, true,
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, false, true,
true, true, true, true);
AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
@@ -387,7 +316,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, true);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()
@@ -401,7 +330,7 @@ class DeviceControllerTest {
@Test
void deviceDowngradeAnnouncementGroupTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, false,
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, false,
true, true, true, true);
AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
@@ -414,7 +343,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, true);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()
@@ -428,7 +357,7 @@ class DeviceControllerTest {
@Test
void deviceDowngradeChangeNumberTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true,
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true,
false, true, true, true);
AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
@@ -442,7 +371,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, true);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()
@@ -457,7 +386,7 @@ class DeviceControllerTest {
@Test
void deviceDowngradePniTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true,
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true,
false, true, true);
AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
@@ -470,7 +399,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, true);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()
@@ -485,7 +414,7 @@ class DeviceControllerTest {
@Test
void deviceDowngradeStoriesTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true,
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true,
true, false, true);
AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
@@ -499,7 +428,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, true);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()
@@ -514,7 +443,7 @@ class DeviceControllerTest {
@Test
void deviceDowngradeGiftBadgesTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, false);
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, false);
AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources
.getJerseyTest()
@@ -525,7 +454,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, true);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.tests.storage;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.time.Duration;
import java.time.Instant;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
class AbusiveHostRulesTest {
@RegisterExtension
private static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
private AbusiveHostRules abusiveHostRules;
private DynamicConfigurationManager<DynamicConfiguration> mockDynamicConfigManager;
@BeforeEach
void setup() throws JsonProcessingException {
@SuppressWarnings("unchecked")
DynamicConfigurationManager<DynamicConfiguration> m = mock(DynamicConfigurationManager.class);
this.mockDynamicConfigManager = m;
when(mockDynamicConfigManager.getConfiguration()).thenReturn(generateConfig(Duration.ofHours(1)));
this.abusiveHostRules = new AbusiveHostRules(REDIS_CLUSTER_EXTENSION.getRedisCluster(), mockDynamicConfigManager);
}
DynamicConfiguration generateConfig(Duration expireDuration) throws JsonProcessingException {
final String configString = String.format("""
captcha:
scoreFloor: 1.0
abusiveHostRules:
expirationTime: %s
""", expireDuration);
return DynamicConfigurationManager
.parseConfiguration(configString, DynamicConfiguration.class)
.orElseThrow();
}
@Test
void testBlockedHost() {
REDIS_CLUSTER_EXTENSION.getRedisCluster().useCluster(connection ->
connection.sync().set(AbusiveHostRules.prefix("192.168.1.1"), "1"));
assertThat(abusiveHostRules.isBlocked("192.168.1.1")).isTrue();
}
@Test
void testUnblocked() {
REDIS_CLUSTER_EXTENSION.getRedisCluster().useCluster(connection ->
connection.sync().set(AbusiveHostRules.prefix("192.168.1.1"), "1"));
assertThat(abusiveHostRules.isBlocked("172.17.1.1")).isFalse();
}
@Test
void testInsertBlocked() {
abusiveHostRules.setBlockedHost("172.17.0.1");
assertThat(abusiveHostRules.isBlocked("172.17.0.1")).isTrue();
abusiveHostRules.setBlockedHost("172.17.0.1");
assertThat(abusiveHostRules.isBlocked("172.17.0.1")).isTrue();
}
@Test
void testExpiration() throws Exception {
when(mockDynamicConfigManager.getConfiguration()).thenReturn(generateConfig(Duration.ofSeconds(1)));
abusiveHostRules.setBlockedHost("192.168.1.1");
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
while (true) {
if (!abusiveHostRules.isBlocked("192.168.1.1")) {
break;
}
}
});
}
}

View File

@@ -40,14 +40,6 @@ class AccountTest {
private final Device recentSecondaryDevice = mock(Device.class);
private final Device oldSecondaryDevice = mock(Device.class);
private final Device gv2CapableDevice = mock(Device.class);
private final Device gv2IncapableDevice = mock(Device.class);
private final Device gv2IncapableExpiredDevice = mock(Device.class);
private final Device gv1MigrationCapableDevice = mock(Device.class);
private final Device gv1MigrationIncapableDevice = mock(Device.class);
private final Device gv1MigrationIncapableExpiredDevice = mock(Device.class);
private final Device senderKeyCapableDevice = mock(Device.class);
private final Device senderKeyIncapableDevice = mock(Device.class);
private final Device senderKeyIncapableExpiredDevice = mock(Device.class);
@@ -94,101 +86,77 @@ class AccountTest {
when(oldSecondaryDevice.isEnabled()).thenReturn(false);
when(oldSecondaryDevice.getId()).thenReturn(2L);
when(gv2CapableDevice.isGroupsV2Supported()).thenReturn(true);
when(gv2CapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
when(gv2CapableDevice.isEnabled()).thenReturn(true);
when(gv2IncapableDevice.isGroupsV2Supported()).thenReturn(false);
when(gv2IncapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
when(gv2IncapableDevice.isEnabled()).thenReturn(true);
when(gv2IncapableExpiredDevice.isGroupsV2Supported()).thenReturn(false);
when(gv2IncapableExpiredDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31));
when(gv2IncapableExpiredDevice.isEnabled()).thenReturn(false);
when(gv1MigrationCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false, false, false, false, false));
when(gv1MigrationCapableDevice.isEnabled()).thenReturn(true);
when(gv1MigrationIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, false, false, false, false, false, false, false));
when(gv1MigrationIncapableDevice.isEnabled()).thenReturn(true);
when(gv1MigrationIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, false, false, false, false, false, false, false));
when(gv1MigrationIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(senderKeyCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(senderKeyCapableDevice.isEnabled()).thenReturn(true);
when(senderKeyIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false, false, false, false, false));
new DeviceCapabilities(true, true, false, false, false, false, false, false));
when(senderKeyIncapableDevice.isEnabled()).thenReturn(true);
when(senderKeyIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false, false, false, false, false));
new DeviceCapabilities(true, true, false, false, false, false, false, false));
when(senderKeyIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(announcementGroupCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, true, false, false, false, false));
new DeviceCapabilities(true, true, true, true, false, false, false, false));
when(announcementGroupCapableDevice.isEnabled()).thenReturn(true);
when(announcementGroupIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(announcementGroupIncapableDevice.isEnabled()).thenReturn(true);
when(announcementGroupIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(announcementGroupIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(changeNumberCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, true, false, false, false));
new DeviceCapabilities(true, true, true, false, true, false, false, false));
when(changeNumberCapableDevice.isEnabled()).thenReturn(true);
when(changeNumberIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(changeNumberIncapableDevice.isEnabled()).thenReturn(true);
when(changeNumberIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(changeNumberIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(pniCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, true, false, false));
new DeviceCapabilities(true, true, true, false, false, true, false, false));
when(pniCapableDevice.isEnabled()).thenReturn(true);
when(pniIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(pniIncapableDevice.isEnabled()).thenReturn(true);
when(pniIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(pniIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(storiesCapableDevice.getId()).thenReturn(1L);
when(storiesCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, true, false));
new DeviceCapabilities(true, true, true, false, false, false, true, false));
when(storiesCapableDevice.isEnabled()).thenReturn(true);
when(storiesCapableDevice.getId()).thenReturn(2L);
when(storiesIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(storiesIncapableDevice.isEnabled()).thenReturn(true);
when(storiesCapableDevice.getId()).thenReturn(3L);
when(storiesIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false));
new DeviceCapabilities(true, true, true, false, false, false, false, false));
when(storiesIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(giftBadgesCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, true));
new DeviceCapabilities(true, true, true, true, true, true, true, true));
when(giftBadgesCapableDevice.isEnabled()).thenReturn(true);
when(giftBadgesIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, false));
new DeviceCapabilities(true, true, true, true, true, true, true, false));
when(giftBadgesIncapableDevice.isEnabled()).thenReturn(true);
when(giftBadgesIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, true, true, true, true, false));
new DeviceCapabilities(true, true, true, true, true, true, true, false));
when(giftBadgesIncapableExpiredDevice.isEnabled()).thenReturn(false);
}
@@ -217,17 +185,6 @@ class AccountTest {
assertFalse(AccountsHelper.generateTestAccount("+14151234567", List.of(disabledMasterDevice, disabledLinkedDevice)).isEnabled());
}
@Test
void testCapabilities() {
final Account uuidCapable = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice), "1234".getBytes());
final Account uuidIncapable = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice, gv2IncapableDevice), "1234".getBytes());
final Account uuidCapableWithExpiredIncapable = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice, gv2IncapableExpiredDevice), "1234".getBytes());
assertTrue(uuidCapable.isGroupsV2Supported());
assertFalse(uuidIncapable.isGroupsV2Supported());
assertTrue(uuidCapableWithExpiredIncapable.isGroupsV2Supported());
}
@Test
void testIsTransferSupported() {
final Device transferCapableMasterDevice = mock(Device.class);
@@ -288,31 +245,6 @@ class AccountTest {
assertTrue(account.isDiscoverableByPhoneNumber());
}
@Test
void isGroupsV2Supported() {
assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isGroupsV2Supported());
assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(),
List.of(gv2CapableDevice, gv2IncapableExpiredDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isGroupsV2Supported());
assertFalse(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(),
List.of(gv2CapableDevice, gv2IncapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isGroupsV2Supported());
}
@Test
void isGv1MigrationSupported() {
assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), List.of(gv1MigrationCapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isGv1MigrationSupported());
assertFalse(
AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(),
List.of(gv1MigrationCapableDevice, gv1MigrationIncapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isGv1MigrationSupported());
assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(),
UUID.randomUUID(), List.of(gv1MigrationCapableDevice, gv1MigrationIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8))
.isGv1MigrationSupported());
}
@Test
void isSenderKeySupported() {
assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), List.of(senderKeyCapableDevice),

View File

@@ -120,8 +120,6 @@ public class AccountsHelper {
case "isEnabled" -> when(updatedAccount.isEnabled()).thenAnswer(stubbing);
case "isDiscoverableByPhoneNumber" -> when(updatedAccount.isDiscoverableByPhoneNumber()).thenAnswer(stubbing);
case "getNextDeviceId" -> when(updatedAccount.getNextDeviceId()).thenAnswer(stubbing);
case "isGroupsV2Supported" -> when(updatedAccount.isGroupsV2Supported()).thenAnswer(stubbing);
case "isGv1MigrationSupported" -> when(updatedAccount.isGv1MigrationSupported()).thenAnswer(stubbing);
case "isSenderKeySupported" -> when(updatedAccount.isSenderKeySupported()).thenAnswer(stubbing);
case "isAnnouncementGroupSupported" -> when(updatedAccount.isAnnouncementGroupSupported()).thenAnswer(stubbing);
case "isChangeNumberSupported" -> when(updatedAccount.isChangeNumberSupported()).thenAnswer(stubbing);