diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index f6435370d..668fc0d43 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -73,6 +73,7 @@ import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator; import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener; import org.whispersystems.textsecuregcm.badges.ConfiguredProfileBadgeConverter; import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.controllers.AccountController; import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV1; import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV2; @@ -416,7 +417,12 @@ public class WhisperServerService extends Application dynamicConfigurationManager = + new DynamicConfigurationManager<>(config.getAppConfig().getApplication(), + config.getAppConfig().getEnvironment(), + config.getAppConfig().getConfigurationName(), + DynamicConfiguration.class); + dynamicConfigurationManager.start(); ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(dynamicConfigurationManager); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java index 96c7f0ec8..dc209fbdc 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java @@ -50,6 +50,7 @@ import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock; import org.whispersystems.textsecuregcm.auth.StoredVerificationCode; import org.whispersystems.textsecuregcm.auth.TurnToken; import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicSignupCaptchaConfiguration; import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountCreationResult; @@ -115,7 +116,7 @@ public class AccountController { private final AbusiveHostRules abusiveHostRules; private final RateLimiters rateLimiters; private final SmsSender smsSender; - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; private final TurnTokenGenerator turnTokenGenerator; private final Map testDevices; private final RecaptchaClient recaptchaClient; @@ -131,7 +132,7 @@ public class AccountController { AbusiveHostRules abusiveHostRules, RateLimiters rateLimiters, SmsSender smsSenderFactory, - DynamicConfigurationManager dynamicConfigurationManager, + DynamicConfigurationManager dynamicConfigurationManager, TurnTokenGenerator turnTokenGenerator, Map testDevices, RecaptchaClient recaptchaClient, diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java index 4b6dfecbe..e0767700c 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java @@ -59,6 +59,7 @@ import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum; import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter; import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration; import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.entities.CreateProfileRequest; import org.whispersystems.textsecuregcm.entities.Profile; import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes; @@ -90,7 +91,7 @@ public class ProfileController { private final ProfilesManager profilesManager; private final AccountsManager accountsManager; private final UsernamesManager usernamesManager; - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; private final ProfileBadgeConverter profileBadgeConverter; private final Map badgeConfigurationMap; @@ -107,7 +108,7 @@ public class ProfileController { AccountsManager accountsManager, ProfilesManager profilesManager, UsernamesManager usernamesManager, - DynamicConfigurationManager dynamicConfigurationManager, + DynamicConfigurationManager dynamicConfigurationManager, ProfileBadgeConverter profileBadgeConverter, BadgesConfiguration badgesConfiguration, S3Client s3client, diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java index f57fb0c96..e9f09c354 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/experiment/ExperimentEnrollmentManager.java @@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.experiment; import java.util.Optional; import java.util.UUID; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicExperimentEnrollmentConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicPreRegistrationExperimentEnrollmentConfiguration; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; @@ -14,9 +15,9 @@ import org.whispersystems.textsecuregcm.util.Util; public class ExperimentEnrollmentManager { - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; - public ExperimentEnrollmentManager(final DynamicConfigurationManager dynamicConfigurationManager) { + public ExperimentEnrollmentManager(final DynamicConfigurationManager dynamicConfigurationManager) { this.dynamicConfigurationManager = dynamicConfigurationManager; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/filters/RemoteDeprecationFilter.java b/service/src/main/java/org/whispersystems/textsecuregcm/filters/RemoteDeprecationFilter.java index b8494b3f2..be77d3f11 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/filters/RemoteDeprecationFilter.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/filters/RemoteDeprecationFilter.java @@ -19,6 +19,7 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRemoteDeprecationConfiguration; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; import org.whispersystems.textsecuregcm.util.ua.ClientPlatform; @@ -34,7 +35,7 @@ import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil; */ public class RemoteDeprecationFilter implements Filter { - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; private static final String DEPRECATED_CLIENT_COUNTER_NAME = name(RemoteDeprecationFilter.class, "deprecated"); private static final String PENDING_DEPRECATION_COUNTER_NAME = name(RemoteDeprecationFilter.class, "pendingDeprecation"); @@ -44,7 +45,7 @@ public class RemoteDeprecationFilter implements Filter { private static final String BLOCKED_CLIENT_REASON = "blocked"; private static final String UNRECOGNIZED_UA_REASON = "unrecognized_user_agent"; - public RemoteDeprecationFilter(final DynamicConfigurationManager dynamicConfigurationManager) { + public RemoteDeprecationFilter(final DynamicConfigurationManager dynamicConfigurationManager) { this.dynamicConfigurationManager = dynamicConfigurationManager; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/PreKeyRateLimiter.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/PreKeyRateLimiter.java index 9486561dc..0765bc57d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/PreKeyRateLimiter.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/PreKeyRateLimiter.java @@ -9,6 +9,7 @@ import static com.codahale.metrics.MetricRegistry.name; import io.dropwizard.util.Duration; import io.micrometer.core.instrument.Metrics; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; @@ -28,11 +29,11 @@ public class PreKeyRateLimiter { private static final long RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS = Duration.days(1).toSeconds(); private final RateLimiters rateLimiters; - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; private final RateLimitResetMetricsManager metricsManager; public PreKeyRateLimiter(final RateLimiters rateLimiters, - final DynamicConfigurationManager dynamicConfigurationManager, + final DynamicConfigurationManager dynamicConfigurationManager, final RateLimitResetMetricsManager metricsManager) { this.rateLimiters = rateLimiters; this.dynamicConfigurationManager = dynamicConfigurationManager; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimitChallengeManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimitChallengeManager.java index b8707ee69..b95ef48cb 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimitChallengeManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimitChallengeManager.java @@ -7,6 +7,7 @@ import io.micrometer.core.instrument.Metrics; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; import org.whispersystems.textsecuregcm.push.NotPushRegisteredException; import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient; @@ -26,7 +27,7 @@ public class RateLimitChallengeManager { private final UnsealedSenderRateLimiter unsealedSenderRateLimiter; private final RateLimiters rateLimiters; - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; public static final String OPTION_RECAPTCHA = "recaptcha"; public static final String OPTION_PUSH_CHALLENGE = "pushChallenge"; @@ -43,7 +44,7 @@ public class RateLimitChallengeManager { final PreKeyRateLimiter preKeyRateLimiter, final UnsealedSenderRateLimiter unsealedSenderRateLimiter, final RateLimiters rateLimiters, - final DynamicConfigurationManager dynamicConfigurationManager) { + final DynamicConfigurationManager dynamicConfigurationManager) { this.pushChallengeManager = pushChallengeManager; this.recaptchaClient = recaptchaClient; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java index 22be62425..b789b6c62 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java @@ -10,6 +10,7 @@ import java.util.function.BiFunction; import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration; import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.CardinalityRateLimitConfiguration; import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.RateLimitConfiguration; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; @@ -48,9 +49,9 @@ public class RateLimiters { private final AtomicReference dailyPreKeysLimiter; private final FaultTolerantRedisCluster cacheCluster; - private final DynamicConfigurationManager dynamicConfig; + private final DynamicConfigurationManager dynamicConfig; - public RateLimiters(RateLimitsConfiguration config, DynamicConfigurationManager dynamicConfig, FaultTolerantRedisCluster cacheCluster) { + public RateLimiters(RateLimitsConfiguration config, DynamicConfigurationManager dynamicConfig, FaultTolerantRedisCluster cacheCluster) { this.cacheCluster = cacheCluster; this.dynamicConfig = dynamicConfig; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/UnsealedSenderRateLimiter.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/UnsealedSenderRateLimiter.java index cde5b4a99..bdffe436f 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/UnsealedSenderRateLimiter.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/UnsealedSenderRateLimiter.java @@ -10,6 +10,7 @@ import static com.codahale.metrics.MetricRegistry.name; import io.dropwizard.util.Duration; import io.lettuce.core.SetArgs; import io.micrometer.core.instrument.Metrics; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitsConfiguration; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; @@ -21,7 +22,7 @@ public class UnsealedSenderRateLimiter { private final RateLimiters rateLimiters; private final FaultTolerantRedisCluster rateLimitCluster; - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; private final RateLimitResetMetricsManager metricsManager; private static final String RATE_LIMIT_RESET_COUNTER_NAME = name(UnsealedSenderRateLimiter.class, "reset"); @@ -38,7 +39,7 @@ public class UnsealedSenderRateLimiter { public UnsealedSenderRateLimiter(final RateLimiters rateLimiters, final FaultTolerantRedisCluster rateLimitCluster, - final DynamicConfigurationManager dynamicConfigurationManager, + final DynamicConfigurationManager dynamicConfigurationManager, final RateLimitResetMetricsManager metricsManager) { this.rateLimiters = rateLimiters; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java b/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java index 9a8807e59..5e79713d2 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java @@ -38,6 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration; import org.whispersystems.textsecuregcm.configuration.TwilioVerificationTextConfiguration; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient; import org.whispersystems.textsecuregcm.http.FormDataBodyPublisher; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; @@ -74,12 +75,16 @@ public class TwilioSmsSender { private final URI smsUri; private final URI voxUri; - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; private final TwilioVerifySender twilioVerifySender; @VisibleForTesting - public TwilioSmsSender(String baseUri, String baseVerifyUri, TwilioConfiguration twilioConfiguration, DynamicConfigurationManager dynamicConfigurationManager) { + public TwilioSmsSender(String baseUri, + String baseVerifyUri, + TwilioConfiguration twilioConfiguration, + DynamicConfigurationManager dynamicConfigurationManager) { + Executor executor = ExecutorUtils.newFixedThreadBoundedQueueExecutor(10, 100); this.accountId = twilioConfiguration.getAccountId(); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java index 45ea66abd..5d645e9d5 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java @@ -15,6 +15,7 @@ import java.util.function.Function; import javax.ws.rs.ProcessingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationRequest; import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationResponse; import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationResponse.Status; @@ -26,10 +27,10 @@ public class DirectoryReconciler extends AccountDatabaseCrawlerListener { private final String replicationName; private final DirectoryReconciliationClient reconciliationClient; - private final DynamicConfigurationManager dynamicConfigurationManager; + private final DynamicConfigurationManager dynamicConfigurationManager; public DirectoryReconciler(String replicationName, DirectoryReconciliationClient reconciliationClient, - DynamicConfigurationManager dynamicConfigurationManager) { + DynamicConfigurationManager dynamicConfigurationManager) { this.reconciliationClient = reconciliationClient; this.replicationName = replicationName; this.dynamicConfigurationManager = dynamicConfigurationManager; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManager.java index ba4ff1e02..4fb9217dd 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManager.java @@ -18,22 +18,23 @@ import javax.validation.Validator; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.util.Util; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.services.appconfig.AppConfigClient; import software.amazon.awssdk.services.appconfig.model.GetConfigurationRequest; import software.amazon.awssdk.services.appconfig.model.GetConfigurationResponse; -public class DynamicConfigurationManager { +public class DynamicConfigurationManager { - private final String application; - private final String environment; - private final String configurationName; - private final String clientId; + private final String application; + private final String environment; + private final String configurationName; + private final String clientId; private final AppConfigClient appConfigClient; - private final AtomicReference configuration = new AtomicReference<>(); + private final Class configurationClass; + + private final AtomicReference configuration = new AtomicReference<>(); private GetConfigurationResponse lastConfigResult; @@ -47,31 +48,32 @@ public class DynamicConfigurationManager { private static final Logger logger = LoggerFactory.getLogger(DynamicConfigurationManager.class); - public DynamicConfigurationManager(String application, String environment, String configurationName) { + public DynamicConfigurationManager(String application, String environment, String configurationName, + Class configurationClass) { this(AppConfigClient.builder() .overrideConfiguration(ClientOverrideConfiguration.builder() .apiCallTimeout(Duration.ofMillis(10000)) .apiCallAttemptTimeout(Duration.ofMillis(10000)).build()) - /* To specify specific credential provider: - https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials.html - */ .build(), - application, environment, configurationName, UUID.randomUUID().toString()); + application, environment, configurationName, UUID.randomUUID().toString(), configurationClass); } @VisibleForTesting public DynamicConfigurationManager(AppConfigClient appConfigClient, String application, String environment, - String configurationName, String clientId) { - this.appConfigClient = appConfigClient; - this.application = application; - this.environment = environment; + String configurationName, String clientId, Class configurationClass) { + this.appConfigClient = appConfigClient; + this.application = application; + this.environment = environment; this.configurationName = configurationName; - this.clientId = clientId; + this.clientId = clientId; + this.configurationClass = configurationClass; } - public DynamicConfiguration getConfiguration() { + public T getConfiguration() { synchronized (this) { - while (!initialized) Util.wait(this); + while (!initialized) { + Util.wait(this); + } } return configuration.get(); @@ -101,25 +103,26 @@ public class DynamicConfigurationManager { workerThread.start(); } - private Optional retrieveDynamicConfiguration() throws JsonProcessingException { + private Optional retrieveDynamicConfiguration() throws JsonProcessingException { final String previousVersion = lastConfigResult != null ? lastConfigResult.configurationVersion() : null; lastConfigResult = appConfigClient.getConfiguration(GetConfigurationRequest.builder() - .application(application) - .environment(environment) - .configuration(configurationName) - .clientId(clientId) - .clientConfigurationVersion(previousVersion) - .build()); + .application(application) + .environment(environment) + .configuration(configurationName) + .clientId(clientId) + .clientConfigurationVersion(previousVersion) + .build()); - final Optional maybeDynamicConfiguration; + final Optional maybeDynamicConfiguration; if (!StringUtils.equals(lastConfigResult.configurationVersion(), previousVersion)) { logger.info("Received new config version: {}", lastConfigResult.configurationVersion()); maybeDynamicConfiguration = parseConfiguration( - StandardCharsets.UTF_8.decode(lastConfigResult.content().asByteBuffer().asReadOnlyBuffer()).toString()); + StandardCharsets.UTF_8.decode(lastConfigResult.content().asByteBuffer().asReadOnlyBuffer()).toString(), + configurationClass); } else { // No change since last version maybeDynamicConfiguration = Optional.empty(); @@ -129,12 +132,12 @@ public class DynamicConfigurationManager { } @VisibleForTesting - public static Optional parseConfiguration(final String configurationYaml) + public static Optional parseConfiguration(final String configurationYaml, final Class configurationClass) throws JsonProcessingException { - final DynamicConfiguration configuration = OBJECT_MAPPER.readValue(configurationYaml, DynamicConfiguration.class); - final Set> violations = VALIDATOR.validate(configuration); + final T configuration = OBJECT_MAPPER.readValue(configurationYaml, configurationClass); + final Set> violations = VALIDATOR.validate(configuration); - final Optional maybeDynamicConfiguration; + final Optional maybeDynamicConfiguration; if (violations.isEmpty()) { maybeDynamicConfiguration = Optional.of(configuration); @@ -146,10 +149,10 @@ public class DynamicConfigurationManager { return maybeDynamicConfiguration; } - private DynamicConfiguration retrieveInitialDynamicConfiguration() { + private T retrieveInitialDynamicConfiguration() { for (;;) { try { - final Optional maybeDynamicConfiguration = retrieveDynamicConfiguration(); + final Optional maybeDynamicConfiguration = retrieveDynamicConfiguration(); if (maybeDynamicConfiguration.isPresent()) { return maybeDynamicConfiguration.get(); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagePersister.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagePersister.java index 45a4ad8f4..ef874d5cc 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagePersister.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagePersister.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.entities.MessageProtos; import org.whispersystems.textsecuregcm.util.Constants; import org.whispersystems.textsecuregcm.util.Util; @@ -53,7 +54,7 @@ public class MessagePersister implements Managed { private static final Logger logger = LoggerFactory.getLogger(MessagePersister.class); - public MessagePersister(final MessagesCache messagesCache, final MessagesManager messagesManager, final AccountsManager accountsManager, final DynamicConfigurationManager dynamicConfigurationManager, final Duration persistDelay) { + public MessagePersister(final MessagesCache messagesCache, final MessagesManager messagesManager, final AccountsManager accountsManager, final DynamicConfigurationManager dynamicConfigurationManager, final Duration persistDelay) { this.messagesCache = messagesCache; this.messagesManager = messagesManager; this.accountsManager = accountsManager; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CheckDynamicConfigurationCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CheckDynamicConfigurationCommand.java index 4ce104756..32e8fb260 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CheckDynamicConfigurationCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CheckDynamicConfigurationCommand.java @@ -11,6 +11,7 @@ import java.nio.file.Files; import java.nio.file.Path; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; public class CheckDynamicConfigurationCommand extends Command { @@ -31,7 +32,7 @@ public class CheckDynamicConfigurationCommand extends Command { public void run(final Bootstrap bootstrap, final Namespace namespace) throws Exception { final Path path = Path.of(namespace.getString("file")); - if (DynamicConfigurationManager.parseConfiguration(Files.readString(path)).isPresent()) { + if (DynamicConfigurationManager.parseConfiguration(Files.readString(path), DynamicConfiguration.class).isPresent()) { System.out.println("Dynamic configuration file at " + path + " is valid"); } else { System.err.println("Dynamic configuration file at " + path + " is not valid"); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java index 7b36f93ae..f896212d9 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java @@ -28,6 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.metrics.PushLatencyManager; import org.whispersystems.textsecuregcm.push.ClientPresenceManager; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; @@ -127,9 +128,9 @@ public class DeleteUserCommand extends EnvironmentCommand dynamicConfigurationManager = new DynamicConfigurationManager<>( configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(), - configuration.getAppConfig().getConfigurationName()); + configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class); dynamicConfigurationManager.start(); DynamoDbClient pendingAccountsDynamoDbClient = DynamoDbFromConfig.client(configuration.getPendingAccountsDynamoDbConfiguration(), diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/SetUserDiscoverabilityCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/SetUserDiscoverabilityCommand.java index b84923c4a..03517adb2 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/SetUserDiscoverabilityCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/SetUserDiscoverabilityCommand.java @@ -27,6 +27,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.jdbi.v3.core.Jdbi; import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.metrics.PushLatencyManager; import org.whispersystems.textsecuregcm.push.ClientPresenceManager; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; @@ -131,9 +132,9 @@ public class SetUserDiscoverabilityCommand extends EnvironmentCommand dynamicConfigurationManager = new DynamicConfigurationManager<>( configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(), - configuration.getAppConfig().getConfigurationName()); + configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class); dynamicConfigurationManager.start(); DynamoDbClient pendingAccountsDynamoDbClient = DynamoDbFromConfig diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java index 10ffa0957..74e593d5d 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java @@ -32,7 +32,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertFalse(emptyConfig.getExperimentEnrollmentConfiguration("test").isPresent()); } @@ -52,7 +52,7 @@ class DynamicConfigurationTest { " - 71618739-114c-4b1f-bb0d-6478a44eb600"; final DynamicConfiguration config = - DynamicConfigurationManager.parseConfiguration(experimentConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(experimentConfigYaml, DynamicConfiguration.class).orElseThrow(); assertFalse(config.getExperimentEnrollmentConfiguration("unconfigured").isPresent()); @@ -80,7 +80,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertFalse(emptyConfig.getPreRegistrationEnrollmentConfiguration("test").isPresent()); } @@ -109,7 +109,7 @@ class DynamicConfigurationTest { " - 47"; final DynamicConfiguration config = - DynamicConfigurationManager.parseConfiguration(experimentConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(experimentConfigYaml, DynamicConfiguration.class).orElseThrow(); assertFalse(config.getPreRegistrationEnrollmentConfiguration("unconfigured").isPresent()); @@ -161,7 +161,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertNotNull(emptyConfig.getRemoteDeprecationConfiguration()); } @@ -181,7 +181,7 @@ class DynamicConfigurationTest { " - 1.4.0-beta.2"; final DynamicConfiguration config = - DynamicConfigurationManager.parseConfiguration(remoteDeprecationConfig).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(remoteDeprecationConfig, DynamicConfiguration.class).orElseThrow(); final DynamicRemoteDeprecationConfiguration remoteDeprecationConfiguration = config .getRemoteDeprecationConfiguration(); @@ -201,7 +201,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertFalse(emptyConfig.getMessageRateConfiguration().isEnforceUnsealedSenderRateLimit()); } @@ -212,7 +212,7 @@ class DynamicConfigurationTest { " enforceUnsealedSenderRateLimit: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(messageRateConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(messageRateConfigYaml, DynamicConfiguration.class).orElseThrow(); assertTrue(emptyConfig.getMessageRateConfiguration().isEnforceUnsealedSenderRateLimit()); } @@ -223,7 +223,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertTrue(emptyConfig.getActiveFeatureFlags().isEmpty()); } @@ -234,7 +234,7 @@ class DynamicConfigurationTest { + " - testFlag"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(featureFlagYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(featureFlagYaml, DynamicConfiguration.class).orElseThrow(); assertTrue(emptyConfig.getActiveFeatureFlags().contains("testFlag")); } @@ -245,7 +245,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertTrue(emptyConfig.getTwilioConfiguration().getNumbers().isEmpty()); } @@ -258,7 +258,7 @@ class DynamicConfigurationTest { + " - 2135551313"; final DynamicTwilioConfiguration config = - DynamicConfigurationManager.parseConfiguration(twilioConfigYaml).orElseThrow() + DynamicConfigurationManager.parseConfiguration(twilioConfigYaml, DynamicConfiguration.class).orElseThrow() .getTwilioConfiguration(); assertEquals(List.of("2135551212", "2135551313"), config.getNumbers()); @@ -270,7 +270,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertTrue(emptyConfig.getPaymentsConfiguration().getAllowedCountryCodes().isEmpty()); } @@ -282,7 +282,7 @@ class DynamicConfigurationTest { + " - 44"; final DynamicPaymentsConfiguration config = - DynamicConfigurationManager.parseConfiguration(paymentsConfigYaml).orElseThrow() + DynamicConfigurationManager.parseConfiguration(paymentsConfigYaml, DynamicConfiguration.class).orElseThrow() .getPaymentsConfiguration(); assertEquals(Set.of("44"), config.getAllowedCountryCodes()); @@ -294,7 +294,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertTrue(emptyConfig.getSignupCaptchaConfiguration().getCountryCodes().isEmpty()); } @@ -306,7 +306,7 @@ class DynamicConfigurationTest { + " - 1"; final DynamicSignupCaptchaConfiguration config = - DynamicConfigurationManager.parseConfiguration(signupCaptchaConfig).orElseThrow() + DynamicConfigurationManager.parseConfiguration(signupCaptchaConfig, DynamicConfiguration.class).orElseThrow() .getSignupCaptchaConfiguration(); assertEquals(Set.of("1"), config.getCountryCodes()); @@ -318,7 +318,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertThat(emptyConfig.getLimits().getUnsealedSenderNumber().getMaxCardinality()).isEqualTo(100); assertThat(emptyConfig.getLimits().getUnsealedSenderNumber().getTtl()).isEqualTo(Duration.ofDays(1)); @@ -332,7 +332,7 @@ class DynamicConfigurationTest { + " ttl: PT23H"; final CardinalityRateLimitConfiguration unsealedSenderNumber = - DynamicConfigurationManager.parseConfiguration(limitsConfig).orElseThrow() + DynamicConfigurationManager.parseConfiguration(limitsConfig, DynamicConfiguration.class).orElseThrow() .getLimits().getUnsealedSenderNumber(); assertThat(unsealedSenderNumber.getMaxCardinality()).isEqualTo(99); @@ -345,7 +345,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertThat(emptyConfig.getRateLimitChallengeConfiguration().getClientSupportedVersions()).isEmpty(); assertThat(emptyConfig.getRateLimitChallengeConfiguration().isPreKeyLimitEnforced()).isFalse(); @@ -362,7 +362,7 @@ class DynamicConfigurationTest { + " DESKTOP: 5.0.0"; DynamicRateLimitChallengeConfiguration rateLimitChallengeConfiguration = - DynamicConfigurationManager.parseConfiguration(rateLimitChallengeConfig).orElseThrow() + DynamicConfigurationManager.parseConfiguration(rateLimitChallengeConfig, DynamicConfiguration.class).orElseThrow() .getRateLimitChallengeConfiguration(); final Map clientSupportedVersions = rateLimitChallengeConfiguration.getClientSupportedVersions(); @@ -380,7 +380,7 @@ class DynamicConfigurationTest { { final String emptyConfigYaml = "test: true"; final DynamicConfiguration emptyConfig = - DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow(); + DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow(); assertThat(emptyConfig.getDirectoryReconcilerConfiguration().isEnabled()).isTrue(); } @@ -391,7 +391,7 @@ class DynamicConfigurationTest { + " enabled: false"; DynamicDirectoryReconcilerConfiguration directoryReconcilerConfiguration = - DynamicConfigurationManager.parseConfiguration(directoryReconcilerConfig).orElseThrow() + DynamicConfigurationManager.parseConfiguration(directoryReconcilerConfig, DynamicConfiguration.class).orElseThrow() .getDirectoryReconcilerConfiguration(); assertThat(directoryReconcilerConfiguration.isEnabled()).isFalse(); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManagerTest.java index 93c25960a..71b2c46e4 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManagerTest.java @@ -7,6 +7,7 @@ import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.appconfig.AppConfigClient; import software.amazon.awssdk.services.appconfig.model.GetConfigurationRequest; @@ -14,13 +15,13 @@ import software.amazon.awssdk.services.appconfig.model.GetConfigurationResponse; public class DynamicConfigurationManagerTest { - private DynamicConfigurationManager dynamicConfigurationManager; + private DynamicConfigurationManager dynamicConfigurationManager; private AppConfigClient appConfig; @Before public void setup() { this.appConfig = mock(AppConfigClient.class); - this.dynamicConfigurationManager = new DynamicConfigurationManager(appConfig, "foo", "bar", "baz", "poof"); + this.dynamicConfigurationManager = new DynamicConfigurationManager<>(appConfig, "foo", "bar", "baz", "poof", DynamicConfiguration.class); } @Test diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/limits/DynamicRateLimitsTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/limits/DynamicRateLimitsTest.java index 4d6ef2b21..c0f744767 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/limits/DynamicRateLimitsTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/limits/DynamicRateLimitsTest.java @@ -21,8 +21,8 @@ import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; class DynamicRateLimitsTest { - private DynamicConfigurationManager dynamicConfig; - private FaultTolerantRedisCluster redisCluster; + private DynamicConfigurationManager dynamicConfig; + private FaultTolerantRedisCluster redisCluster; @BeforeEach void setup() {