mirror of
https://github.com/signalapp/Signal-Server.git
synced 2024-09-19 19:42:18 +02:00
Add tests for WhisperServerService#run
Additionally, `LocalWhisperServerService` may be used for integration testing.
This commit is contained in:
parent
b6f8bca361
commit
0e4be0c85a
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
@ -22,5 +22,8 @@ jobs:
|
|||||||
# work around an issue with actions/runner setting an incorrect HOME in containers, which breaks maven caching
|
# work around an issue with actions/runner setting an incorrect HOME in containers, which breaks maven caching
|
||||||
# https://github.com/actions/setup-java/issues/356
|
# https://github.com/actions/setup-java/issues/356
|
||||||
HOME: /root
|
HOME: /root
|
||||||
|
- name: Install APT packages
|
||||||
|
# ca-certificates: required for AWS CRT client
|
||||||
|
run: apt update && apt install -y ca-certificates
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: ./mvnw -e -B verify
|
run: ./mvnw -e -B verify
|
||||||
|
30
TESTING.md
Normal file
30
TESTING.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Testing
|
||||||
|
|
||||||
|
## Automated tests
|
||||||
|
|
||||||
|
The full suite of automated tests can be run using Maven from the project root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./mvnw verify
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test server
|
||||||
|
|
||||||
|
The service can be run in a feature-limited test mode by running the Maven `integration-test`
|
||||||
|
goal with the `test-server` profile activated:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./mvnw integration-test -Ptest-server [-DskipTests=true]
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs [`LocalWhisperServerService`][lwss] with [test configuration][test.yml] and [secrets][test secrets]. External
|
||||||
|
registration clients are stubbed so that:
|
||||||
|
|
||||||
|
- a captcha requirement can be satisfied with `test.test.registration.test`
|
||||||
|
- any string will be accepted for a phone verification code
|
||||||
|
|
||||||
|
[lwss]: service/src/test/java/org/whispersystems/textsecuregcm/LocalWhisperServerService.java
|
||||||
|
|
||||||
|
[test.yml]: service/src/test/resources/config/test.yml
|
||||||
|
|
||||||
|
[test secrets]: service/src/test/resources/config/test-secrets-bundle.yml
|
@ -15,7 +15,6 @@ import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
|
|||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.VerificationSessions;
|
import org.whispersystems.textsecuregcm.storage.VerificationSessions;
|
||||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
|
||||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
|
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
@ -31,13 +30,9 @@ public class IntegrationTools {
|
|||||||
public static IntegrationTools create(final Config config) {
|
public static IntegrationTools create(final Config config) {
|
||||||
final AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.builder().build();
|
final AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.builder().build();
|
||||||
|
|
||||||
final DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
final DynamoDbAsyncClient dynamoDbAsyncClient = config.dynamoDbClient().buildAsyncClient(credentialsProvider);
|
||||||
config.dynamoDbClientConfiguration(),
|
|
||||||
credentialsProvider);
|
|
||||||
|
|
||||||
final DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
final DynamoDbClient dynamoDbClient = config.dynamoDbClient().buildSyncClient(credentialsProvider);
|
||||||
config.dynamoDbClientConfiguration(),
|
|
||||||
credentialsProvider);
|
|
||||||
|
|
||||||
final RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
final RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
||||||
config.dynamoDbTables().registrationRecovery(), Duration.ofDays(1), dynamoDbClient, dynamoDbAsyncClient);
|
config.dynamoDbTables().registrationRecovery(), Duration.ofDays(1), dynamoDbClient, dynamoDbAsyncClient);
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
package org.signal.integration.config;
|
package org.signal.integration.config;
|
||||||
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory;
|
||||||
|
|
||||||
public record Config(String domain,
|
public record Config(String domain,
|
||||||
String rootCert,
|
String rootCert,
|
||||||
DynamoDbClientConfiguration dynamoDbClientConfiguration,
|
DynamoDbClientFactory dynamoDbClient,
|
||||||
DynamoDbTables dynamoDbTables,
|
DynamoDbTables dynamoDbTables,
|
||||||
String prescribedRegistrationNumber,
|
String prescribedRegistrationNumber,
|
||||||
String prescribedRegistrationCode) {
|
String prescribedRegistrationCode) {
|
||||||
|
@ -67,14 +67,15 @@ braintree:
|
|||||||
supportedCurrenciesByPaymentMethod:
|
supportedCurrenciesByPaymentMethod:
|
||||||
PAYPAL:
|
PAYPAL:
|
||||||
- usd
|
- usd
|
||||||
pubSubProject: example-project
|
pubSubPublisher:
|
||||||
pubSubTopic: example-topic
|
project: example-project
|
||||||
pubSubCredentialConfiguration: |
|
topic: example-topic
|
||||||
{
|
credentialConfiguration: |
|
||||||
"credential": "configuration"
|
{
|
||||||
}
|
"credential": "configuration"
|
||||||
|
}
|
||||||
|
|
||||||
dynamoDbClientConfiguration:
|
dynamoDbClient:
|
||||||
region: us-west-2 # AWS Region
|
region: us-west-2 # AWS Region
|
||||||
|
|
||||||
dynamoDbTables:
|
dynamoDbTables:
|
||||||
@ -138,8 +139,9 @@ cacheCluster: # Redis server configuration for cache cluster
|
|||||||
clientPresenceCluster: # Redis server configuration for client presence cluster
|
clientPresenceCluster: # Redis server configuration for client presence cluster
|
||||||
configurationUri: redis://redis.example.com:6379/
|
configurationUri: redis://redis.example.com:6379/
|
||||||
|
|
||||||
pubsub: # Redis server configuration for pubsub cluster
|
provisioning:
|
||||||
uri: redis://redis.example.com:6379/
|
pubsub: # Redis server configuration for pubsub cluster
|
||||||
|
uri: redis://redis.example.com:6379/
|
||||||
|
|
||||||
pushSchedulerCluster: # Redis server configuration for push scheduler cluster
|
pushSchedulerCluster: # Redis server configuration for push scheduler cluster
|
||||||
configurationUri: redis://redis.example.com:6379/
|
configurationUri: redis://redis.example.com:6379/
|
||||||
@ -218,9 +220,10 @@ metricsCluster:
|
|||||||
configurationUri: redis://redis.example.com:6379/
|
configurationUri: redis://redis.example.com:6379/
|
||||||
|
|
||||||
awsAttachments: # AWS S3 configuration
|
awsAttachments: # AWS S3 configuration
|
||||||
accessKey: secret://awsAttachments.accessKey
|
|
||||||
accessSecret: secret://awsAttachments.accessSecret
|
|
||||||
bucket: aws-attachments
|
bucket: aws-attachments
|
||||||
|
credentials:
|
||||||
|
accessKeyId: secret://awsAttachments.accessKey
|
||||||
|
secretAccessKey: secret://awsAttachments.accessSecret
|
||||||
region: us-west-2
|
region: us-west-2
|
||||||
|
|
||||||
gcpAttachments: # GCP Storage configuration
|
gcpAttachments: # GCP Storage configuration
|
||||||
@ -245,9 +248,10 @@ fcm: # FCM configuration
|
|||||||
credentials: secret://fcm.credentials
|
credentials: secret://fcm.credentials
|
||||||
|
|
||||||
cdn:
|
cdn:
|
||||||
accessKey: secret://cdn.accessKey
|
|
||||||
accessSecret: secret://cdn.accessSecret
|
|
||||||
bucket: cdn # S3 Bucket name
|
bucket: cdn # S3 Bucket name
|
||||||
|
credentials:
|
||||||
|
accessKeyId: secret://cdn.accessKey
|
||||||
|
secretAccessKey: secret://cdn.accessSecret
|
||||||
region: us-west-2 # AWS region
|
region: us-west-2 # AWS region
|
||||||
|
|
||||||
clientCdn:
|
clientCdn:
|
||||||
@ -345,13 +349,14 @@ remoteConfig:
|
|||||||
|
|
||||||
paymentsService:
|
paymentsService:
|
||||||
userAuthenticationTokenSharedSecret: secret://paymentsService.userAuthenticationTokenSharedSecret
|
userAuthenticationTokenSharedSecret: secret://paymentsService.userAuthenticationTokenSharedSecret
|
||||||
fixerApiKey: secret://paymentsService.fixerApiKey
|
|
||||||
coinMarketCapApiKey: secret://paymentsService.coinMarketCapApiKey
|
|
||||||
coinMarketCapCurrencyIds:
|
|
||||||
MOB: 7878
|
|
||||||
paymentCurrencies:
|
paymentCurrencies:
|
||||||
# list of symbols for supported currencies
|
# list of symbols for supported currencies
|
||||||
- MOB
|
- MOB
|
||||||
|
externalClients:
|
||||||
|
fixerApiKey: secret://paymentsService.fixerApiKey
|
||||||
|
coinMarketCapApiKey: secret://paymentsService.coinMarketCapApiKey
|
||||||
|
coinMarketCapCurrencyIds:
|
||||||
|
MOB: 7878
|
||||||
|
|
||||||
artService:
|
artService:
|
||||||
userAuthenticationTokenSharedSecret: secret://artService.userAuthenticationTokenSharedSecret
|
userAuthenticationTokenSharedSecret: secret://artService.userAuthenticationTokenSharedSecret
|
||||||
|
@ -640,6 +640,31 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>test-server</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>start-test-server</id>
|
||||||
|
<phase>integration-test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>java</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>org.whispersystems.textsecuregcm.LocalWhisperServerService</mainClass>
|
||||||
|
<classpathScope>test</classpathScope>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@ -692,15 +717,15 @@
|
|||||||
<goals>
|
<goals>
|
||||||
<goal>java</goal>
|
<goal>java</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>org.whispersystems.textsecuregcm.CheckServiceConfigurations</mainClass>
|
||||||
|
<classpathScope>test</classpathScope>
|
||||||
|
<arguments>
|
||||||
|
<argument>${project.basedir}/config</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
|
||||||
<mainClass>org.whispersystems.textsecuregcm.CheckServiceConfigurations</mainClass>
|
|
||||||
<classpathScope>test</classpathScope>
|
|
||||||
<arguments>
|
|
||||||
<argument>${project.basedir}/config</argument>
|
|
||||||
</arguments>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -15,37 +15,40 @@ import javax.validation.Valid;
|
|||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.whispersystems.textsecuregcm.attachments.TusConfiguration;
|
import org.whispersystems.textsecuregcm.attachments.TusConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.AppConfigConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.BraintreeConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.BraintreeConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.Cdn3StorageManagerConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.Cdn3StorageManagerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ClientCdnConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ClientCdnConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ClientReleaseConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ClientReleaseConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.DefaultAwsCredentialsFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.DogstatsdConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.DogstatsdConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
|
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ExternalRequestFilterConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ExternalRequestFilterConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.GenericZkConfig;
|
import org.whispersystems.textsecuregcm.configuration.GenericZkConfig;
|
||||||
import org.whispersystems.textsecuregcm.configuration.HCaptchaConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.HCaptchaClientFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.LinkDeviceSecretConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.LinkDeviceSecretConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.MessageByteLimitCardinalityEstimatorConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.MessageByteLimitCardinalityEstimatorConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.NoiseWebSocketTunnelConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.NoiseWebSocketTunnelConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ProvisioningConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery3Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery3Configuration;
|
||||||
@ -69,6 +72,11 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private TlsKeyStoreConfiguration tlsKeyStore;
|
private TlsKeyStoreConfiguration tlsKeyStore;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Valid
|
||||||
|
@JsonProperty
|
||||||
|
AwsCredentialsProviderFactory awsCredentialsProvider = new DefaultAwsCredentialsFactory();
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@ -82,7 +90,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private DynamoDbClientConfiguration dynamoDbClientConfiguration;
|
private DynamoDbClientFactory dynamoDbClient;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@ -117,22 +125,22 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private DogstatsdConfiguration dogstatsd = new DogstatsdConfiguration();
|
private DatadogConfiguration dogstatsd = new DogstatsdConfiguration();
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RedisClusterConfiguration cacheCluster;
|
private FaultTolerantRedisClusterFactory cacheCluster;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RedisConfiguration pubsub;
|
private FaultTolerantRedisClusterFactory metricsCluster;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RedisClusterConfiguration metricsCluster;
|
private ProvisioningConfiguration provisioning;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@ -151,12 +159,12 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RedisClusterConfiguration pushSchedulerCluster;
|
private FaultTolerantRedisClusterFactory pushSchedulerCluster;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RedisClusterConfiguration rateLimitersCluster;
|
private FaultTolerantRedisClusterFactory rateLimitersCluster;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@ -166,7 +174,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RedisClusterConfiguration clientPresenceCluster;
|
private FaultTolerantRedisClusterFactory clientPresenceCluster;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -201,7 +209,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private HCaptchaConfiguration hCaptcha;
|
private HCaptchaClientFactory hCaptcha;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -246,7 +254,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private AppConfigConfiguration appConfig;
|
private DynamicConfigurationManagerFactory appConfig;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -270,12 +278,12 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private SpamFilterConfiguration spamFilterConfiguration;
|
private SpamFilterConfiguration spamFilter;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RegistrationServiceConfiguration registrationService;
|
private RegistrationServiceClientFactory registrationService;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -305,28 +313,28 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private VirtualThreadConfiguration virtualThreadConfiguration = new VirtualThreadConfiguration(Duration.ofMillis(1));
|
private VirtualThreadConfiguration virtualThread = new VirtualThreadConfiguration(Duration.ofMillis(1));
|
||||||
|
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private MonitoredS3ObjectConfiguration maxmindCityDatabase;
|
private S3ObjectMonitorFactory maxmindCityDatabase;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private MonitoredS3ObjectConfiguration callingTurnDnsRecords;
|
private S3ObjectMonitorFactory callingTurnDnsRecords;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private MonitoredS3ObjectConfiguration callingTurnPerformanceTable;
|
private S3ObjectMonitorFactory callingTurnPerformanceTable;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private MonitoredS3ObjectConfiguration callingTurnManualTable;
|
private S3ObjectMonitorFactory callingTurnManualTable;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -342,6 +350,10 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
return tlsKeyStore;
|
return tlsKeyStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AwsCredentialsProviderFactory getAwsCredentialsConfiguration() {
|
||||||
|
return awsCredentialsProvider;
|
||||||
|
}
|
||||||
|
|
||||||
public StripeConfiguration getStripe() {
|
public StripeConfiguration getStripe() {
|
||||||
return stripe;
|
return stripe;
|
||||||
}
|
}
|
||||||
@ -350,15 +362,15 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
return braintree;
|
return braintree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamoDbClientConfiguration getDynamoDbClientConfiguration() {
|
public DynamoDbClientFactory getDynamoDbClientConfiguration() {
|
||||||
return dynamoDbClientConfiguration;
|
return dynamoDbClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamoDbTables getDynamoDbTables() {
|
public DynamoDbTables getDynamoDbTables() {
|
||||||
return dynamoDbTables;
|
return dynamoDbTables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HCaptchaConfiguration getHCaptchaConfiguration() {
|
public HCaptchaClientFactory getHCaptchaConfiguration() {
|
||||||
return hCaptcha;
|
return hCaptcha;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,15 +390,15 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
return gcpAttachments;
|
return gcpAttachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisClusterConfiguration getCacheClusterConfiguration() {
|
public FaultTolerantRedisClusterFactory getCacheClusterConfiguration() {
|
||||||
return cacheCluster;
|
return cacheCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisConfiguration getPubsubCacheConfiguration() {
|
public ProvisioningConfiguration getProvisioningConfiguration() {
|
||||||
return pubsub;
|
return provisioning;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisClusterConfiguration getMetricsClusterConfiguration() {
|
public FaultTolerantRedisClusterFactory getMetricsClusterConfiguration() {
|
||||||
return metricsCluster;
|
return metricsCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,15 +422,15 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
return messageCache;
|
return messageCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisClusterConfiguration getClientPresenceClusterConfiguration() {
|
public FaultTolerantRedisClusterFactory getClientPresenceClusterConfiguration() {
|
||||||
return clientPresenceCluster;
|
return clientPresenceCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisClusterConfiguration getPushSchedulerCluster() {
|
public FaultTolerantRedisClusterFactory getPushSchedulerCluster() {
|
||||||
return pushSchedulerCluster;
|
return pushSchedulerCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisClusterConfiguration getRateLimitersCluster() {
|
public FaultTolerantRedisClusterFactory getRateLimitersCluster() {
|
||||||
return rateLimitersCluster;
|
return rateLimitersCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +458,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
return cdn3StorageManager;
|
return cdn3StorageManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DogstatsdConfiguration getDatadogConfiguration() {
|
public DatadogConfiguration getDatadogConfiguration() {
|
||||||
return dogstatsd;
|
return dogstatsd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,7 +501,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
return remoteConfig;
|
return remoteConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppConfigConfiguration getAppConfig() {
|
public DynamicConfigurationManagerFactory getAppConfig() {
|
||||||
return appConfig;
|
return appConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,10 +522,10 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SpamFilterConfiguration getSpamFilterConfiguration() {
|
public SpamFilterConfiguration getSpamFilterConfiguration() {
|
||||||
return spamFilterConfiguration;
|
return spamFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegistrationServiceConfiguration getRegistrationServiceConfiguration() {
|
public RegistrationServiceClientFactory getRegistrationServiceConfiguration() {
|
||||||
return registrationService;
|
return registrationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,22 +550,22 @@ public class WhisperServerConfiguration extends Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public VirtualThreadConfiguration getVirtualThreadConfiguration() {
|
public VirtualThreadConfiguration getVirtualThreadConfiguration() {
|
||||||
return virtualThreadConfiguration;
|
return virtualThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonitoredS3ObjectConfiguration getMaxmindCityDatabase() {
|
public S3ObjectMonitorFactory getMaxmindCityDatabase() {
|
||||||
return maxmindCityDatabase;
|
return maxmindCityDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonitoredS3ObjectConfiguration getCallingTurnDnsRecords() {
|
public S3ObjectMonitorFactory getCallingTurnDnsRecords() {
|
||||||
return callingTurnDnsRecords;
|
return callingTurnDnsRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonitoredS3ObjectConfiguration getCallingTurnPerformanceTable() {
|
public S3ObjectMonitorFactory getCallingTurnPerformanceTable() {
|
||||||
return callingTurnPerformanceTable;
|
return callingTurnPerformanceTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonitoredS3ObjectConfiguration getCallingTurnManualTable() {
|
public S3ObjectMonitorFactory getCallingTurnManualTable() {
|
||||||
return callingTurnManualTable;
|
return callingTurnManualTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,14 +7,7 @@ package org.whispersystems.textsecuregcm;
|
|||||||
import static com.codahale.metrics.MetricRegistry.name;
|
import static com.codahale.metrics.MetricRegistry.name;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
import com.google.api.gax.batching.BatchingSettings;
|
|
||||||
import com.google.api.gax.batching.FlowControlSettings;
|
|
||||||
import com.google.api.gax.batching.FlowController;
|
|
||||||
import com.google.api.gax.core.FixedCredentialsProvider;
|
|
||||||
import com.google.auth.oauth2.ExternalAccountCredentials;
|
|
||||||
import com.google.cloud.pubsub.v1.Publisher;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.pubsub.v1.TopicName;
|
|
||||||
import io.dropwizard.auth.AuthDynamicFeature;
|
import io.dropwizard.auth.AuthDynamicFeature;
|
||||||
import io.dropwizard.auth.AuthFilter;
|
import io.dropwizard.auth.AuthFilter;
|
||||||
import io.dropwizard.auth.AuthValueFactoryProvider;
|
import io.dropwizard.auth.AuthValueFactoryProvider;
|
||||||
@ -27,7 +20,6 @@ import io.dropwizard.core.server.DefaultServerFactory;
|
|||||||
import io.dropwizard.core.setup.Bootstrap;
|
import io.dropwizard.core.setup.Bootstrap;
|
||||||
import io.dropwizard.core.setup.Environment;
|
import io.dropwizard.core.setup.Environment;
|
||||||
import io.dropwizard.jetty.HttpsConnectorFactory;
|
import io.dropwizard.jetty.HttpsConnectorFactory;
|
||||||
import io.dropwizard.lifecycle.Managed;
|
|
||||||
import io.grpc.ServerBuilder;
|
import io.grpc.ServerBuilder;
|
||||||
import io.lettuce.core.metrics.MicrometerCommandLatencyRecorder;
|
import io.lettuce.core.metrics.MicrometerCommandLatencyRecorder;
|
||||||
import io.lettuce.core.metrics.MicrometerOptions;
|
import io.lettuce.core.metrics.MicrometerOptions;
|
||||||
@ -36,9 +28,7 @@ import io.micrometer.core.instrument.Metrics;
|
|||||||
import io.micrometer.core.instrument.binder.grpc.MetricCollectingServerInterceptor;
|
import io.micrometer.core.instrument.binder.grpc.MetricCollectingServerInterceptor;
|
||||||
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
|
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
|
||||||
import io.netty.channel.local.LocalAddress;
|
import io.netty.channel.local.LocalAddress;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -232,7 +222,6 @@ import org.whispersystems.textsecuregcm.subscriptions.BankMandateTranslator;
|
|||||||
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
|
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
|
||||||
import org.whispersystems.textsecuregcm.subscriptions.StripeManager;
|
import org.whispersystems.textsecuregcm.subscriptions.StripeManager;
|
||||||
import org.whispersystems.textsecuregcm.util.BufferingInterceptor;
|
import org.whispersystems.textsecuregcm.util.BufferingInterceptor;
|
||||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
|
||||||
import org.whispersystems.textsecuregcm.util.ManagedAwsCrt;
|
import org.whispersystems.textsecuregcm.util.ManagedAwsCrt;
|
||||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.UsernameHashZkProofVerifier;
|
import org.whispersystems.textsecuregcm.util.UsernameHashZkProofVerifier;
|
||||||
@ -263,9 +252,7 @@ import org.whispersystems.websocket.WebSocketResourceProviderFactory;
|
|||||||
import org.whispersystems.websocket.setup.WebSocketEnvironment;
|
import org.whispersystems.websocket.setup.WebSocketEnvironment;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
|
||||||
import software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider;
|
|
||||||
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
|
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
|
||||||
import software.amazon.awssdk.regions.Region;
|
import software.amazon.awssdk.regions.Region;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
@ -279,9 +266,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
|
|
||||||
public static final String SECRETS_BUNDLE_FILE_NAME_PROPERTY = "secrets.bundle.filename";
|
public static final String SECRETS_BUNDLE_FILE_NAME_PROPERTY = "secrets.bundle.filename";
|
||||||
|
|
||||||
public static final software.amazon.awssdk.auth.credentials.AwsCredentialsProvider AWSSDK_CREDENTIALS_PROVIDER =
|
|
||||||
WebIdentityTokenFileCredentialsProvider.create();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(final Bootstrap<WhisperServerConfiguration> bootstrap) {
|
public void initialize(final Bootstrap<WhisperServerConfiguration> bootstrap) {
|
||||||
// `SecretStore` needs to be initialized before Dropwizard reads the main application config file.
|
// `SecretStore` needs to be initialized before Dropwizard reads the main application config file.
|
||||||
@ -328,16 +312,15 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
final Clock clock = Clock.systemUTC();
|
final Clock clock = Clock.systemUTC();
|
||||||
final int availableProcessors = Runtime.getRuntime().availableProcessors();
|
final int availableProcessors = Runtime.getRuntime().availableProcessors();
|
||||||
|
|
||||||
|
final AwsCredentialsProvider awsCredentialsProvider = config.getAwsCredentialsConfiguration().build();
|
||||||
|
|
||||||
UncaughtExceptionHandler.register();
|
UncaughtExceptionHandler.register();
|
||||||
|
|
||||||
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
||||||
.scheduledExecutorService(name(getClass(), "dynamicConfiguration-%d")).threads(1).build();
|
.scheduledExecutorService(name(getClass(), "dynamicConfiguration-%d")).threads(1).build();
|
||||||
|
|
||||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = config.getAppConfig()
|
||||||
new DynamicConfigurationManager<>(config.getAppConfig().getApplication(),
|
.build(DynamicConfiguration.class, dynamicConfigurationExecutor, awsCredentialsProvider);
|
||||||
config.getAppConfig().getEnvironment(),
|
|
||||||
config.getAppConfig().getConfigurationName(),
|
|
||||||
DynamicConfiguration.class, dynamicConfigurationExecutor);
|
|
||||||
dynamicConfigurationManager.start();
|
dynamicConfigurationManager.start();
|
||||||
|
|
||||||
MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager);
|
MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager);
|
||||||
@ -362,11 +345,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
BankMandateTranslator bankMandateTranslator = new BankMandateTranslator(headerControlledResourceBundleLookup);
|
BankMandateTranslator bankMandateTranslator = new BankMandateTranslator(headerControlledResourceBundleLookup);
|
||||||
|
|
||||||
environment.lifecycle().manage(new ManagedAwsCrt());
|
environment.lifecycle().manage(new ManagedAwsCrt());
|
||||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(config.getDynamoDbClientConfiguration(),
|
DynamoDbAsyncClient dynamoDbAsyncClient = config.getDynamoDbClientConfiguration()
|
||||||
AWSSDK_CREDENTIALS_PROVIDER);
|
.buildAsyncClient(awsCredentialsProvider);
|
||||||
|
|
||||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(config.getDynamoDbClientConfiguration(),
|
DynamoDbClient dynamoDbClient = config.getDynamoDbClientConfiguration().buildSyncClient(awsCredentialsProvider);
|
||||||
AWSSDK_CREDENTIALS_PROVIDER);
|
|
||||||
|
|
||||||
BlockingQueue<Runnable> messageDeletionQueue = new LinkedBlockingQueue<>();
|
BlockingQueue<Runnable> messageDeletionQueue = new LinkedBlockingQueue<>();
|
||||||
Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(),
|
Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(),
|
||||||
@ -428,18 +410,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
.build();
|
.build();
|
||||||
ConnectionEventLogger.logConnectionEvents(sharedClientResources);
|
ConnectionEventLogger.logConnectionEvents(sharedClientResources);
|
||||||
|
|
||||||
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache",
|
FaultTolerantRedisCluster cacheCluster = config.getCacheClusterConfiguration()
|
||||||
config.getCacheClusterConfiguration(), sharedClientResources.mutate());
|
.build("main_cache", sharedClientResources.mutate());
|
||||||
FaultTolerantRedisCluster messagesCluster = new FaultTolerantRedisCluster("messages",
|
FaultTolerantRedisCluster messagesCluster =
|
||||||
config.getMessageCacheConfiguration().getRedisClusterConfiguration(), sharedClientResources.mutate());
|
config.getMessageCacheConfiguration().getRedisClusterConfiguration()
|
||||||
FaultTolerantRedisCluster clientPresenceCluster = new FaultTolerantRedisCluster("client_presence",
|
.build("messages", sharedClientResources.mutate());
|
||||||
config.getClientPresenceClusterConfiguration(), sharedClientResources.mutate());
|
FaultTolerantRedisCluster clientPresenceCluster = config.getClientPresenceClusterConfiguration()
|
||||||
FaultTolerantRedisCluster metricsCluster = new FaultTolerantRedisCluster("metrics",
|
.build("client_presence", sharedClientResources.mutate());
|
||||||
config.getMetricsClusterConfiguration(), sharedClientResources.mutate());
|
FaultTolerantRedisCluster metricsCluster = config.getMetricsClusterConfiguration()
|
||||||
FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler",
|
.build("metrics", sharedClientResources.mutate());
|
||||||
config.getPushSchedulerCluster(), sharedClientResources.mutate());
|
FaultTolerantRedisCluster pushSchedulerCluster = config.getPushSchedulerCluster().build("push_scheduler",
|
||||||
FaultTolerantRedisCluster rateLimitersCluster = new FaultTolerantRedisCluster("rate_limiters",
|
sharedClientResources.mutate());
|
||||||
config.getRateLimitersCluster(), sharedClientResources.mutate());
|
FaultTolerantRedisCluster rateLimitersCluster = config.getRateLimitersCluster().build("rate_limiters",
|
||||||
|
sharedClientResources.mutate());
|
||||||
|
|
||||||
final BlockingQueue<Runnable> keyspaceNotificationDispatchQueue = new ArrayBlockingQueue<>(100_000);
|
final BlockingQueue<Runnable> keyspaceNotificationDispatchQueue = new ArrayBlockingQueue<>(100_000);
|
||||||
Metrics.gaugeCollectionSize(name(getClass(), "keyspaceNotificationDispatchQueueSize"), Collections.emptyList(),
|
Metrics.gaugeCollectionSize(name(getClass(), "keyspaceNotificationDispatchQueueSize"), Collections.emptyList(),
|
||||||
@ -551,14 +534,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
registrationRecoveryPasswords);
|
registrationRecoveryPasswords);
|
||||||
UsernameHashZkProofVerifier usernameHashZkProofVerifier = new UsernameHashZkProofVerifier();
|
UsernameHashZkProofVerifier usernameHashZkProofVerifier = new UsernameHashZkProofVerifier();
|
||||||
|
|
||||||
RegistrationServiceClient registrationServiceClient = new RegistrationServiceClient(
|
RegistrationServiceClient registrationServiceClient = config.getRegistrationServiceConfiguration()
|
||||||
config.getRegistrationServiceConfiguration().host(),
|
.build(environment, registrationCallbackExecutor, registrationIdentityTokenRefreshExecutor);
|
||||||
config.getRegistrationServiceConfiguration().port(),
|
|
||||||
config.getRegistrationServiceConfiguration().credentialConfigurationJson(),
|
|
||||||
config.getRegistrationServiceConfiguration().identityTokenAudience(),
|
|
||||||
config.getRegistrationServiceConfiguration().registrationCaCertificate(),
|
|
||||||
registrationCallbackExecutor,
|
|
||||||
registrationIdentityTokenRefreshExecutor);
|
|
||||||
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(svr2CredentialsGenerator,
|
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(svr2CredentialsGenerator,
|
||||||
secureValueRecoveryServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr2Configuration());
|
secureValueRecoveryServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr2Configuration());
|
||||||
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator,
|
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator,
|
||||||
@ -595,9 +572,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
apnPushNotificationScheduler, pushLatencyManager);
|
apnPushNotificationScheduler, pushLatencyManager);
|
||||||
RateLimiters rateLimiters = RateLimiters.createAndValidate(config.getLimitsConfiguration(),
|
RateLimiters rateLimiters = RateLimiters.createAndValidate(config.getLimitsConfiguration(),
|
||||||
dynamicConfigurationManager, rateLimitersCluster);
|
dynamicConfigurationManager, rateLimitersCluster);
|
||||||
ProvisioningManager provisioningManager = new ProvisioningManager(config.getPubsubCacheConfiguration().getUri(),
|
ProvisioningManager provisioningManager = new ProvisioningManager(
|
||||||
sharedClientResources, config.getPubsubCacheConfiguration().getTimeout(),
|
config.getProvisioningConfiguration().pubsub().build(sharedClientResources),
|
||||||
config.getPubsubCacheConfiguration().getCircuitBreakerConfiguration());
|
config.getProvisioningConfiguration().circuitBreaker());
|
||||||
IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(
|
IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(
|
||||||
config.getDynamoDbTables().getIssuedReceipts().getTableName(),
|
config.getDynamoDbTables().getIssuedReceipts().getTableName(),
|
||||||
config.getDynamoDbTables().getIssuedReceipts().getExpiration(),
|
config.getDynamoDbTables().getIssuedReceipts().getExpiration(),
|
||||||
@ -635,12 +612,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
"message_byte_limit",
|
"message_byte_limit",
|
||||||
config.getMessageByteLimitCardinalityEstimator().period());
|
config.getMessageByteLimitCardinalityEstimator().period());
|
||||||
|
|
||||||
HCaptchaClient hCaptchaClient = new HCaptchaClient(
|
HCaptchaClient hCaptchaClient = config.getHCaptchaConfiguration()
|
||||||
config.getHCaptchaConfiguration().getApiKey().value(),
|
.build(hcaptchaRetryExecutor, dynamicConfigurationManager);
|
||||||
hcaptchaRetryExecutor,
|
|
||||||
config.getHCaptchaConfiguration().getCircuitBreaker(),
|
|
||||||
config.getHCaptchaConfiguration().getRetry(),
|
|
||||||
dynamicConfigurationManager);
|
|
||||||
HttpClient shortCodeRetrieverHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2)
|
HttpClient shortCodeRetrieverHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2)
|
||||||
.connectTimeout(Duration.ofSeconds(10)).build();
|
.connectTimeout(Duration.ofSeconds(10)).build();
|
||||||
ShortCodeExpander shortCodeRetriever = new ShortCodeExpander(shortCodeRetrieverHttpClient, config.getShortCodeRetrieverConfiguration().baseUrl());
|
ShortCodeExpander shortCodeRetriever = new ShortCodeExpander(shortCodeRetrieverHttpClient, config.getShortCodeRetrieverConfiguration().baseUrl());
|
||||||
@ -652,8 +625,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
ChangeNumberManager changeNumberManager = new ChangeNumberManager(messageSender, accountsManager);
|
ChangeNumberManager changeNumberManager = new ChangeNumberManager(messageSender, accountsManager);
|
||||||
|
|
||||||
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().fixerApiKey().value());
|
FixerClient fixerClient = config.getPaymentsServiceConfiguration().externalClients()
|
||||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().coinMarketCapApiKey().value(), config.getPaymentsServiceConfiguration().coinMarketCapCurrencyIds());
|
.buildFixerClient(currencyClient);
|
||||||
|
CoinMarketCapClient coinMarketCapClient = config.getPaymentsServiceConfiguration().externalClients()
|
||||||
|
.buildCoinMarketCapClient(currencyClient);
|
||||||
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, coinMarketCapClient,
|
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, coinMarketCapClient,
|
||||||
cacheCluster, config.getPaymentsServiceConfiguration().paymentCurrencies(), recurringJobExecutor, Clock.systemUTC());
|
cacheCluster, config.getPaymentsServiceConfiguration().paymentCurrencies(), recurringJobExecutor, Clock.systemUTC());
|
||||||
VirtualThreadPinEventMonitor virtualThreadPinEventMonitor = new VirtualThreadPinEventMonitor(
|
VirtualThreadPinEventMonitor virtualThreadPinEventMonitor = new VirtualThreadPinEventMonitor(
|
||||||
@ -661,39 +636,14 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
() -> dynamicConfigurationManager.getConfiguration().getVirtualThreads().allowedPinEvents(),
|
() -> dynamicConfigurationManager.getConfiguration().getVirtualThreads().allowedPinEvents(),
|
||||||
config.getVirtualThreadConfiguration().pinEventThreshold());
|
config.getVirtualThreadConfiguration().pinEventThreshold());
|
||||||
|
|
||||||
final Publisher pubSubPublisher;
|
|
||||||
{
|
|
||||||
final FlowControlSettings flowControlSettings = FlowControlSettings.newBuilder()
|
|
||||||
.setLimitExceededBehavior(FlowController.LimitExceededBehavior.ThrowException)
|
|
||||||
.setMaxOutstandingElementCount(100L)
|
|
||||||
.setMaxOutstandingRequestBytes(16 * 1024 * 1024L) // 16MB
|
|
||||||
.build();
|
|
||||||
|
|
||||||
final BatchingSettings batchingSettings = BatchingSettings.newBuilder()
|
|
||||||
.setFlowControlSettings(flowControlSettings)
|
|
||||||
.setDelayThreshold(org.threeten.bp.Duration.ofMillis(10))
|
|
||||||
// These thresholds are actually the default, setting them explicitly since creating a custom batchingSettings resets them
|
|
||||||
.setElementCountThreshold(100L)
|
|
||||||
.setRequestByteThreshold(5000L)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try (final ByteArrayInputStream credentialConfigInputStream =
|
|
||||||
new ByteArrayInputStream(config.getBraintree().pubSubCredentialConfiguration().getBytes(StandardCharsets.UTF_8))) {
|
|
||||||
|
|
||||||
pubSubPublisher = Publisher.newBuilder(TopicName.of(config.getBraintree().pubSubProject(), config.getBraintree().pubSubTopic()))
|
|
||||||
.setCredentialsProvider(FixedCredentialsProvider.create(ExternalAccountCredentials.fromStream(credentialConfigInputStream)))
|
|
||||||
.setBatchingSettings(batchingSettings)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey().value(), subscriptionProcessorExecutor,
|
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey().value(), subscriptionProcessorExecutor,
|
||||||
config.getStripe().idempotencyKeyGenerator().value(), config.getStripe().boostDescription(), config.getStripe().supportedCurrenciesByPaymentMethod());
|
config.getStripe().idempotencyKeyGenerator().value(), config.getStripe().boostDescription(), config.getStripe().supportedCurrenciesByPaymentMethod());
|
||||||
BraintreeManager braintreeManager = new BraintreeManager(config.getBraintree().merchantId(),
|
BraintreeManager braintreeManager = new BraintreeManager(config.getBraintree().merchantId(),
|
||||||
config.getBraintree().publicKey(), config.getBraintree().privateKey().value(),
|
config.getBraintree().publicKey(), config.getBraintree().privateKey().value(),
|
||||||
config.getBraintree().environment(),
|
config.getBraintree().environment(),
|
||||||
config.getBraintree().supportedCurrenciesByPaymentMethod(), config.getBraintree().merchantAccounts(),
|
config.getBraintree().supportedCurrenciesByPaymentMethod(), config.getBraintree().merchantAccounts(),
|
||||||
config.getBraintree().graphqlUrl(), currencyManager, pubSubPublisher, config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor,
|
config.getBraintree().graphqlUrl(), currencyManager, config.getBraintree().pubSubPublisher().build(),
|
||||||
|
config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor,
|
||||||
subscriptionProcessorRetryExecutor);
|
subscriptionProcessorRetryExecutor);
|
||||||
|
|
||||||
environment.lifecycle().manage(apnSender);
|
environment.lifecycle().manage(apnSender);
|
||||||
@ -708,10 +658,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
|
|
||||||
final RegistrationCaptchaManager registrationCaptchaManager = new RegistrationCaptchaManager(captchaChecker);
|
final RegistrationCaptchaManager registrationCaptchaManager = new RegistrationCaptchaManager(captchaChecker);
|
||||||
|
|
||||||
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
|
AwsCredentialsProvider cdnCredentialsProvider = config.getCdnConfiguration().credentials().build();
|
||||||
.create(AwsBasicCredentials.create(
|
|
||||||
config.getCdnConfiguration().accessKey().value(),
|
|
||||||
config.getCdnConfiguration().accessSecret().value()));
|
|
||||||
S3Client cdnS3Client = S3Client.builder()
|
S3Client cdnS3Client = S3Client.builder()
|
||||||
.credentialsProvider(cdnCredentialsProvider)
|
.credentialsProvider(cdnCredentialsProvider)
|
||||||
.region(Region.of(config.getCdnConfiguration().region()))
|
.region(Region.of(config.getCdnConfiguration().region()))
|
||||||
@ -730,8 +677,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
config.getGcpAttachmentsConfiguration().rsaSigningKey().value());
|
config.getGcpAttachmentsConfiguration().rsaSigningKey().value());
|
||||||
|
|
||||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().region(),
|
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().region(),
|
||||||
config.getCdnConfiguration().bucket(), config.getCdnConfiguration().accessKey().value());
|
config.getCdnConfiguration().bucket(), config.getCdnConfiguration().credentials().accessKeyId().value());
|
||||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().accessSecret().value(),
|
PolicySigner profileCdnPolicySigner = new PolicySigner(
|
||||||
|
config.getCdnConfiguration().credentials().secretAccessKey().value(),
|
||||||
config.getCdnConfiguration().region());
|
config.getCdnConfiguration().region());
|
||||||
|
|
||||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().serverSecret().value());
|
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().serverSecret().value());
|
||||||
@ -768,23 +716,27 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
|
|
||||||
MaxMindDatabaseManager geoIpCityDatabaseManager = new MaxMindDatabaseManager(
|
MaxMindDatabaseManager geoIpCityDatabaseManager = new MaxMindDatabaseManager(
|
||||||
recurringConfigSyncExecutor,
|
recurringConfigSyncExecutor,
|
||||||
|
awsCredentialsProvider,
|
||||||
config.getMaxmindCityDatabase(),
|
config.getMaxmindCityDatabase(),
|
||||||
"city"
|
"city"
|
||||||
);
|
);
|
||||||
environment.lifecycle().manage(geoIpCityDatabaseManager);
|
environment.lifecycle().manage(geoIpCityDatabaseManager);
|
||||||
CallDnsRecordsManager callDnsRecordsManager = new CallDnsRecordsManager(
|
CallDnsRecordsManager callDnsRecordsManager = new CallDnsRecordsManager(
|
||||||
recurringConfigSyncExecutor,
|
recurringConfigSyncExecutor,
|
||||||
|
awsCredentialsProvider,
|
||||||
config.getCallingTurnDnsRecords()
|
config.getCallingTurnDnsRecords()
|
||||||
);
|
);
|
||||||
environment.lifecycle().manage(callDnsRecordsManager);
|
environment.lifecycle().manage(callDnsRecordsManager);
|
||||||
CallRoutingTableManager callRoutingTableManager = new CallRoutingTableManager(
|
CallRoutingTableManager callRoutingTableManager = new CallRoutingTableManager(
|
||||||
recurringConfigSyncExecutor,
|
recurringConfigSyncExecutor,
|
||||||
|
awsCredentialsProvider,
|
||||||
config.getCallingTurnPerformanceTable(),
|
config.getCallingTurnPerformanceTable(),
|
||||||
"Performance"
|
"Performance"
|
||||||
);
|
);
|
||||||
environment.lifecycle().manage(callRoutingTableManager);
|
environment.lifecycle().manage(callRoutingTableManager);
|
||||||
CallRoutingTableManager manualCallRoutingTableManager = new CallRoutingTableManager(
|
CallRoutingTableManager manualCallRoutingTableManager = new CallRoutingTableManager(
|
||||||
recurringConfigSyncExecutor,
|
recurringConfigSyncExecutor,
|
||||||
|
awsCredentialsProvider,
|
||||||
config.getCallingTurnManualTable(),
|
config.getCallingTurnManualTable(),
|
||||||
"Manual"
|
"Manual"
|
||||||
);
|
);
|
||||||
@ -978,8 +930,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
|
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
|
||||||
registrationLockVerificationManager, rateLimiters),
|
registrationLockVerificationManager, rateLimiters),
|
||||||
new ArtController(rateLimiters, artCredentialsGenerator),
|
new ArtController(rateLimiters, artCredentialsGenerator),
|
||||||
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().accessKey().value(),
|
new AttachmentControllerV2(rateLimiters,
|
||||||
config.getAwsAttachmentsConfiguration().accessSecret().value(),
|
config.getAwsAttachmentsConfiguration().credentials().accessKeyId().value(),
|
||||||
|
config.getAwsAttachmentsConfiguration().credentials().secretAccessKey().value(),
|
||||||
config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
|
config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
|
||||||
new AttachmentControllerV3(rateLimiters, gcsAttachmentGenerator),
|
new AttachmentControllerV3(rateLimiters, gcsAttachmentGenerator),
|
||||||
new AttachmentControllerV4(rateLimiters, gcsAttachmentGenerator, tusAttachmentGenerator,
|
new AttachmentControllerV4(rateLimiters, gcsAttachmentGenerator, tusAttachmentGenerator,
|
||||||
@ -1012,8 +965,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||||||
new SecureStorageController(storageCredentialsGenerator),
|
new SecureStorageController(storageCredentialsGenerator),
|
||||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
|
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
|
||||||
new SecureValueRecovery3Controller(svr3CredentialsGenerator, accountsManager),
|
new SecureValueRecovery3Controller(svr3CredentialsGenerator, accountsManager),
|
||||||
new StickerController(rateLimiters, config.getCdnConfiguration().accessKey().value(),
|
new StickerController(rateLimiters, config.getCdnConfiguration().credentials().accessKeyId().value(),
|
||||||
config.getCdnConfiguration().accessSecret().value(), config.getCdnConfiguration().region(),
|
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
|
||||||
config.getCdnConfiguration().bucket()),
|
config.getCdnConfiguration().bucket()),
|
||||||
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
||||||
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
||||||
|
@ -11,18 +11,18 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
|
|||||||
import io.dropwizard.lifecycle.Managed;
|
import io.dropwizard.lifecycle.Managed;
|
||||||
import io.micrometer.core.instrument.Metrics;
|
import io.micrometer.core.instrument.Metrics;
|
||||||
import io.micrometer.core.instrument.Timer;
|
import io.micrometer.core.instrument.Timer;
|
||||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
|
||||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||||
|
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
|
||||||
public class CallDnsRecordsManager implements Supplier<CallDnsRecords>, Managed {
|
public class CallDnsRecordsManager implements Supplier<CallDnsRecords>, Managed {
|
||||||
|
|
||||||
@ -38,20 +38,10 @@ public class CallDnsRecordsManager implements Supplier<CallDnsRecords>, Managed
|
|||||||
.enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
|
.enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public CallDnsRecordsManager(
|
public CallDnsRecordsManager(final ScheduledExecutorService executorService,
|
||||||
@Nonnull final ScheduledExecutorService executorService,
|
final AwsCredentialsProvider awsCredentialsProvider, final S3ObjectMonitorFactory configuration) {
|
||||||
@Nonnull final MonitoredS3ObjectConfiguration configuration
|
|
||||||
){
|
|
||||||
this.objectMonitor = new S3ObjectMonitor(
|
|
||||||
configuration.s3Region(),
|
|
||||||
configuration.s3Bucket(),
|
|
||||||
configuration.objectKey(),
|
|
||||||
configuration.maxSize(),
|
|
||||||
executorService,
|
|
||||||
configuration.refreshInterval(),
|
|
||||||
this::handleDatabaseChanged
|
|
||||||
);
|
|
||||||
|
|
||||||
|
this.objectMonitor = configuration.build(awsCredentialsProvider, executorService);
|
||||||
this.callDnsRecords.set(CallDnsRecords.empty());
|
this.callDnsRecords.set(CallDnsRecords.empty());
|
||||||
this.refreshTimer = Metrics.timer(MetricsUtil.name(CallDnsRecordsManager.class, "refresh"));
|
this.refreshTimer = Metrics.timer(MetricsUtil.name(CallDnsRecordsManager.class, "refresh"));
|
||||||
}
|
}
|
||||||
@ -74,14 +64,12 @@ public class CallDnsRecordsManager implements Supplier<CallDnsRecords>, Managed
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
Managed.super.start();
|
objectMonitor.start(this::handleDatabaseChanged);
|
||||||
objectMonitor.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws Exception {
|
public void stop() throws Exception {
|
||||||
objectMonitor.stop();
|
objectMonitor.stop();
|
||||||
Managed.super.stop();
|
|
||||||
callDnsRecords.getAndSet(null);
|
callDnsRecords.getAndSet(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,18 +8,18 @@ package org.whispersystems.textsecuregcm.calls.routing;
|
|||||||
import io.dropwizard.lifecycle.Managed;
|
import io.dropwizard.lifecycle.Managed;
|
||||||
import io.micrometer.core.instrument.Metrics;
|
import io.micrometer.core.instrument.Metrics;
|
||||||
import io.micrometer.core.instrument.Timer;
|
import io.micrometer.core.instrument.Timer;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
|
||||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||||
|
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
|
||||||
public class CallRoutingTableManager implements Supplier<CallRoutingTable>, Managed {
|
public class CallRoutingTableManager implements Supplier<CallRoutingTable>, Managed {
|
||||||
|
|
||||||
@ -33,21 +33,11 @@ public class CallRoutingTableManager implements Supplier<CallRoutingTable>, Mana
|
|||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(CallRoutingTableManager.class);
|
private static final Logger log = LoggerFactory.getLogger(CallRoutingTableManager.class);
|
||||||
|
|
||||||
public CallRoutingTableManager(
|
public CallRoutingTableManager(final ScheduledExecutorService executorService,
|
||||||
@Nonnull final ScheduledExecutorService executorService,
|
final AwsCredentialsProvider awsCredentialsProvider, final S3ObjectMonitorFactory configuration,
|
||||||
@Nonnull final MonitoredS3ObjectConfiguration configuration,
|
final String tableTag) {
|
||||||
@Nonnull final String tableTag
|
|
||||||
){
|
|
||||||
this.objectMonitor = new S3ObjectMonitor(
|
|
||||||
configuration.s3Region(),
|
|
||||||
configuration.s3Bucket(),
|
|
||||||
configuration.objectKey(),
|
|
||||||
configuration.maxSize(),
|
|
||||||
executorService,
|
|
||||||
configuration.refreshInterval(),
|
|
||||||
this::handleDatabaseChanged
|
|
||||||
);
|
|
||||||
|
|
||||||
|
this.objectMonitor = configuration.build(awsCredentialsProvider, executorService);
|
||||||
this.tableTag = tableTag;
|
this.tableTag = tableTag;
|
||||||
this.routingTable.set(CallRoutingTable.empty());
|
this.routingTable.set(CallRoutingTable.empty());
|
||||||
this.refreshTimer = Metrics.timer(MetricsUtil.name(CallRoutingTableManager.class, tableTag));
|
this.refreshTimer = Metrics.timer(MetricsUtil.name(CallRoutingTableManager.class, tableTag));
|
||||||
@ -67,13 +57,11 @@ public class CallRoutingTableManager implements Supplier<CallRoutingTable>, Mana
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
Managed.super.start();
|
objectMonitor.start(this::handleDatabaseChanged);
|
||||||
objectMonitor.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws Exception {
|
public void stop() throws Exception {
|
||||||
Managed.super.stop();
|
|
||||||
objectMonitor.stop();
|
objectMonitor.stop();
|
||||||
routingTable.getAndSet(null);
|
routingTable.getAndSet(null);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
|
||||||
public class AppConfigConfiguration {
|
@JsonTypeName("default")
|
||||||
|
public class AppConfigConfiguration implements DynamicConfigurationManagerFactory {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
@ -29,4 +33,11 @@ public class AppConfigConfiguration {
|
|||||||
public String getConfigurationName() {
|
public String getConfigurationName() {
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> DynamicConfigurationManager<T> build(Class<T> klazz, ScheduledExecutorService scheduledExecutorService,
|
||||||
|
AwsCredentialsProvider awsCredentialsProvider) {
|
||||||
|
return new DynamicConfigurationManager<>(application, environment, configuration, awsCredentialsProvider, klazz,
|
||||||
|
scheduledExecutorService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
|
||||||
|
|
||||||
public record AwsAttachmentsConfiguration(@NotNull SecretString accessKey,
|
public record AwsAttachmentsConfiguration(@NotNull @Valid StaticAwsCredentialsFactory credentials,
|
||||||
@NotNull SecretString accessSecret,
|
|
||||||
@NotBlank String bucket,
|
@NotBlank String bucket,
|
||||||
@NotBlank String region) {
|
@NotBlank String region) {
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultAwsCredentialsFactory.class)
|
||||||
|
public interface AwsCredentialsProviderFactory extends Discoverable {
|
||||||
|
|
||||||
|
AwsCredentialsProvider build();
|
||||||
|
}
|
@ -32,9 +32,7 @@ public record BraintreeConfiguration(@NotBlank String merchantId,
|
|||||||
@NotBlank String graphqlUrl,
|
@NotBlank String graphqlUrl,
|
||||||
@NotEmpty Map<String, String> merchantAccounts,
|
@NotEmpty Map<String, String> merchantAccounts,
|
||||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||||
@NotBlank String pubSubProject,
|
@Valid @NotNull PubSubPublisherFactory pubSubPublisher) {
|
||||||
@NotBlank String pubSubTopic,
|
|
||||||
@NotBlank String pubSubCredentialConfiguration) {
|
|
||||||
|
|
||||||
public BraintreeConfiguration {
|
public BraintreeConfiguration {
|
||||||
if (circuitBreaker == null) {
|
if (circuitBreaker == null) {
|
||||||
|
@ -5,12 +5,11 @@
|
|||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
|
||||||
|
|
||||||
public record CdnConfiguration(@NotNull SecretString accessKey,
|
public record CdnConfiguration(@NotNull @Valid StaticAwsCredentialsFactory credentials,
|
||||||
@NotNull SecretString accessSecret,
|
|
||||||
@NotBlank String bucket,
|
@NotBlank String bucket,
|
||||||
@NotBlank String region) {
|
@NotBlank String region) {
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import io.micrometer.statsd.StatsdConfig;
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DogstatsdConfiguration.class)
|
||||||
|
public interface DatadogConfiguration extends StatsdConfig, Discoverable {
|
||||||
|
|
||||||
|
String getEnvironment();
|
||||||
|
|
||||||
|
Duration getShutdownWaitDuration();
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider;
|
||||||
|
|
||||||
|
@JsonTypeName("default")
|
||||||
|
public record DefaultAwsCredentialsFactory() implements AwsCredentialsProviderFactory {
|
||||||
|
|
||||||
|
public AwsCredentialsProvider build() {
|
||||||
|
return WebIdentityTokenFileCredentialsProvider.create();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import com.google.api.gax.batching.BatchingSettings;
|
||||||
|
import com.google.api.gax.batching.FlowControlSettings;
|
||||||
|
import com.google.api.gax.batching.FlowController;
|
||||||
|
import com.google.api.gax.core.FixedCredentialsProvider;
|
||||||
|
import com.google.auth.oauth2.ExternalAccountCredentials;
|
||||||
|
import com.google.cloud.pubsub.v1.Publisher;
|
||||||
|
import com.google.cloud.pubsub.v1.PublisherInterface;
|
||||||
|
import com.google.pubsub.v1.TopicName;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@JsonTypeName("default")
|
||||||
|
public record DefaultPubSubPublisherFactory(@NotBlank String project,
|
||||||
|
@NotBlank String topic,
|
||||||
|
@NotBlank String credentialConfiguration) implements
|
||||||
|
PubSubPublisherFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublisherInterface build() throws IOException {
|
||||||
|
|
||||||
|
final FlowControlSettings flowControlSettings = FlowControlSettings.newBuilder()
|
||||||
|
.setLimitExceededBehavior(FlowController.LimitExceededBehavior.ThrowException)
|
||||||
|
.setMaxOutstandingElementCount(100L)
|
||||||
|
.setMaxOutstandingRequestBytes(16 * 1024 * 1024L) // 16MB
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final BatchingSettings batchingSettings = BatchingSettings.newBuilder()
|
||||||
|
.setFlowControlSettings(flowControlSettings)
|
||||||
|
.setDelayThreshold(org.threeten.bp.Duration.ofMillis(10))
|
||||||
|
// These thresholds are actually the default, setting them explicitly since creating a custom batchingSettings resets them
|
||||||
|
.setElementCountThreshold(100L)
|
||||||
|
.setRequestByteThreshold(5000L)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (final ByteArrayInputStream credentialConfigInputStream =
|
||||||
|
new ByteArrayInputStream(credentialConfiguration.getBytes(StandardCharsets.UTF_8))) {
|
||||||
|
|
||||||
|
return Publisher.newBuilder(
|
||||||
|
TopicName.of(project, topic))
|
||||||
|
.setCredentialsProvider(
|
||||||
|
FixedCredentialsProvider.create(ExternalAccountCredentials.fromStream(credentialConfigInputStream)))
|
||||||
|
.setBatchingSettings(batchingSettings)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,13 +6,14 @@
|
|||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import io.micrometer.statsd.StatsdConfig;
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
import io.micrometer.statsd.StatsdFlavor;
|
import io.micrometer.statsd.StatsdFlavor;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
public class DogstatsdConfiguration implements StatsdConfig {
|
@JsonTypeName("default")
|
||||||
|
public class DogstatsdConfiguration implements DatadogConfiguration {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -31,6 +32,7 @@ public class DogstatsdConfiguration implements StatsdConfig {
|
|||||||
return step;
|
return step;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getEnvironment() {
|
public String getEnvironment() {
|
||||||
return environment;
|
return environment;
|
||||||
}
|
}
|
||||||
@ -50,4 +52,9 @@ public class DogstatsdConfiguration implements StatsdConfig {
|
|||||||
public String host() {
|
public String host() {
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Duration getShutdownWaitDuration() {
|
||||||
|
return step().plus(step.dividedBy(2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = AppConfigConfiguration.class)
|
||||||
|
public interface DynamicConfigurationManagerFactory extends Discoverable {
|
||||||
|
|
||||||
|
<T> DynamicConfigurationManager<T> build(Class<T> configurationClass,
|
||||||
|
ScheduledExecutorService scheduledExecutorService, AwsCredentialsProvider awsCredentialsProvider);
|
||||||
|
}
|
@ -9,11 +9,20 @@ import java.time.Duration;
|
|||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Positive;
|
import javax.validation.constraints.Positive;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
||||||
|
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
|
||||||
|
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
|
||||||
|
import software.amazon.awssdk.regions.Region;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
|
||||||
|
@JsonTypeName("default")
|
||||||
public record DynamoDbClientConfiguration(@NotBlank String region,
|
public record DynamoDbClientConfiguration(@NotBlank String region,
|
||||||
@NotNull Duration clientExecutionTimeout,
|
@NotNull Duration clientExecutionTimeout,
|
||||||
@NotNull Duration clientRequestTimeout,
|
@NotNull Duration clientRequestTimeout,
|
||||||
@Positive int maxConnections) {
|
@Positive int maxConnections) implements DynamoDbClientFactory {
|
||||||
|
|
||||||
public DynamoDbClientConfiguration {
|
public DynamoDbClientConfiguration {
|
||||||
if (clientExecutionTimeout == null) {
|
if (clientExecutionTimeout == null) {
|
||||||
@ -28,4 +37,32 @@ public record DynamoDbClientConfiguration(@NotBlank String region,
|
|||||||
maxConnections = 50;
|
maxConnections = 50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamoDbClient buildSyncClient(final AwsCredentialsProvider credentialsProvider) {
|
||||||
|
return DynamoDbClient.builder()
|
||||||
|
.region(Region.of(region()))
|
||||||
|
.credentialsProvider(credentialsProvider)
|
||||||
|
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||||
|
.apiCallTimeout(clientExecutionTimeout())
|
||||||
|
.apiCallAttemptTimeout(clientRequestTimeout())
|
||||||
|
.build())
|
||||||
|
.httpClientBuilder(AwsCrtHttpClient.builder()
|
||||||
|
.maxConcurrency(maxConnections()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamoDbAsyncClient buildAsyncClient(final AwsCredentialsProvider credentialsProvider) {
|
||||||
|
return DynamoDbAsyncClient.builder()
|
||||||
|
.region(Region.of(region()))
|
||||||
|
.credentialsProvider(credentialsProvider)
|
||||||
|
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||||
|
.apiCallTimeout(clientExecutionTimeout())
|
||||||
|
.apiCallAttemptTimeout(clientRequestTimeout())
|
||||||
|
.build())
|
||||||
|
.httpClientBuilder(NettyNioAsyncHttpClient.builder()
|
||||||
|
.maxConcurrency(maxConnections()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DynamoDbClientConfiguration.class)
|
||||||
|
public interface DynamoDbClientFactory extends Discoverable {
|
||||||
|
|
||||||
|
DynamoDbClient buildSyncClient(AwsCredentialsProvider awsCredentialsProvider);
|
||||||
|
|
||||||
|
DynamoDbAsyncClient buildAsyncClient(AwsCredentialsProvider awsCredentialsProvider);
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import io.lettuce.core.resource.ClientResources;
|
||||||
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RedisClusterConfiguration.class)
|
||||||
|
public interface FaultTolerantRedisClusterFactory extends Discoverable {
|
||||||
|
|
||||||
|
FaultTolerantRedisCluster build(String name, ClientResources.Builder clientResourcesBuilder);
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = HCaptchaConfiguration.class)
|
||||||
|
public interface HCaptchaClientFactory extends Discoverable {
|
||||||
|
|
||||||
|
HCaptchaClient build(ScheduledExecutorService retryExecutor,
|
||||||
|
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager);
|
||||||
|
}
|
@ -7,9 +7,15 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
public class HCaptchaConfiguration {
|
@JsonTypeName("default")
|
||||||
|
public class HCaptchaConfiguration implements HCaptchaClientFactory {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -36,4 +42,14 @@ public class HCaptchaConfiguration {
|
|||||||
return retry;
|
return retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HCaptchaClient build(final ScheduledExecutorService retryExecutor,
|
||||||
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
|
return new HCaptchaClient(
|
||||||
|
apiKey.value(),
|
||||||
|
retryExecutor,
|
||||||
|
circuitBreaker,
|
||||||
|
retry,
|
||||||
|
dynamicConfigurationManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,12 @@ public class MessageCacheConfiguration {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
private RedisClusterConfiguration cluster;
|
private FaultTolerantRedisClusterFactory cluster;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private int persistDelayMinutes = 10;
|
private int persistDelayMinutes = 10;
|
||||||
|
|
||||||
public RedisClusterConfiguration getRedisClusterConfiguration() {
|
public FaultTolerantRedisClusterFactory getRedisClusterConfiguration() {
|
||||||
return cluster;
|
return cluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,18 +5,23 @@
|
|||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@JsonTypeName("default")
|
||||||
public record MonitoredS3ObjectConfiguration(
|
public record MonitoredS3ObjectConfiguration(
|
||||||
@NotBlank String s3Region,
|
@NotBlank String s3Region,
|
||||||
@NotBlank String s3Bucket,
|
@NotBlank String s3Bucket,
|
||||||
@NotBlank String objectKey,
|
@NotBlank String objectKey,
|
||||||
Long maxSize,
|
Long maxSize,
|
||||||
Duration refreshInterval) {
|
Duration refreshInterval) implements S3ObjectMonitorFactory {
|
||||||
|
|
||||||
private static long DEFAULT_MAXSIZE = 16*1024*1024;
|
private static final long DEFAULT_MAXSIZE = 16 * 1024 * 1024;
|
||||||
private static Duration DEFAULT_REFRESH_INTERVAL = Duration.ofMinutes(5);
|
private static final Duration DEFAULT_REFRESH_INTERVAL = Duration.ofMinutes(5);
|
||||||
|
|
||||||
public MonitoredS3ObjectConfiguration {
|
public MonitoredS3ObjectConfiguration {
|
||||||
if (maxSize == null) {
|
if (maxSize == null) {
|
||||||
@ -26,4 +31,12 @@ public record MonitoredS3ObjectConfiguration(
|
|||||||
refreshInterval = DEFAULT_REFRESH_INTERVAL;
|
refreshInterval = DEFAULT_REFRESH_INTERVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public S3ObjectMonitor build(final AwsCredentialsProvider awsCredentialsProvider,
|
||||||
|
final ScheduledExecutorService refreshExecutorService) {
|
||||||
|
|
||||||
|
return new S3ObjectMonitor(awsCredentialsProvider, s3Region, s3Bucket, objectKey, maxSize, refreshExecutorService,
|
||||||
|
refreshInterval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
|
import org.whispersystems.textsecuregcm.currency.CoinMarketCapClient;
|
||||||
|
import org.whispersystems.textsecuregcm.currency.FixerClient;
|
||||||
|
|
||||||
|
@JsonTypeName("default")
|
||||||
|
public record PaymentsServiceClientsConfiguration(@NotNull SecretString coinMarketCapApiKey,
|
||||||
|
@NotNull SecretString fixerApiKey,
|
||||||
|
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds) implements
|
||||||
|
PaymentsServiceClientsFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FixerClient buildFixerClient(final HttpClient httpClient) {
|
||||||
|
return new FixerClient(httpClient, fixerApiKey.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CoinMarketCapClient buildCoinMarketCapClient(final HttpClient httpClient) {
|
||||||
|
return new CoinMarketCapClient(httpClient, coinMarketCapApiKey.value(), coinMarketCapCurrencyIds);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import org.whispersystems.textsecuregcm.currency.CoinMarketCapClient;
|
||||||
|
import org.whispersystems.textsecuregcm.currency.FixerClient;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = PaymentsServiceClientsConfiguration.class)
|
||||||
|
public interface PaymentsServiceClientsFactory extends Discoverable {
|
||||||
|
|
||||||
|
FixerClient buildFixerClient(final HttpClient httpClient);
|
||||||
|
|
||||||
|
CoinMarketCapClient buildCoinMarketCapClient(HttpClient httpClient);
|
||||||
|
}
|
@ -5,17 +5,15 @@
|
|||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import java.util.List;
|
import javax.validation.Valid;
|
||||||
import java.util.Map;
|
|
||||||
import javax.validation.constraints.NotBlank;
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
import java.util.List;
|
||||||
|
|
||||||
public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||||
@NotNull SecretString coinMarketCapApiKey,
|
@NotEmpty List<String> paymentCurrencies,
|
||||||
@NotNull SecretString fixerApiKey,
|
@NotNull @Valid PaymentsServiceClientsFactory externalClients) {
|
||||||
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds,
|
|
||||||
@NotEmpty List<String> paymentCurrencies) {
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public record ProvisioningConfiguration(@Valid @NotNull SingletonRedisClientFactory pubsub,
|
||||||
|
@Valid @NotNull CircuitBreakerConfiguration circuitBreaker) {
|
||||||
|
|
||||||
|
public ProvisioningConfiguration {
|
||||||
|
if (circuitBreaker == null) {
|
||||||
|
circuitBreaker = new CircuitBreakerConfiguration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.google.cloud.pubsub.v1.PublisherInterface;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultPubSubPublisherFactory.class)
|
||||||
|
public interface PubSubPublisherFactory extends Discoverable {
|
||||||
|
|
||||||
|
PublisherInterface build() throws IOException;
|
||||||
|
}
|
@ -6,13 +6,17 @@
|
|||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import io.lettuce.core.resource.ClientResources;
|
||||||
|
import java.time.Duration;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.time.Duration;
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
|
|
||||||
public class RedisClusterConfiguration {
|
@JsonTypeName("default")
|
||||||
|
public class RedisClusterConfiguration implements FaultTolerantRedisClusterFactory {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
@ -32,6 +36,11 @@ public class RedisClusterConfiguration {
|
|||||||
@Valid
|
@Valid
|
||||||
private RetryConfiguration retry = new RetryConfiguration();
|
private RetryConfiguration retry = new RetryConfiguration();
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setConfigurationUri(final String configurationUri) {
|
||||||
|
this.configurationUri = configurationUri;
|
||||||
|
}
|
||||||
|
|
||||||
public String getConfigurationUri() {
|
public String getConfigurationUri() {
|
||||||
return configurationUri;
|
return configurationUri;
|
||||||
}
|
}
|
||||||
@ -47,4 +56,9 @@ public class RedisClusterConfiguration {
|
|||||||
public RetryConfiguration getRetryConfiguration() {
|
public RetryConfiguration getRetryConfiguration() {
|
||||||
return retry;
|
return retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FaultTolerantRedisCluster build(final String name, final ClientResources.Builder clientResourcesBuilder) {
|
||||||
|
return new FaultTolerantRedisCluster(name, this, clientResourcesBuilder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,16 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||||||
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import io.lettuce.core.RedisClient;
|
||||||
|
import io.lettuce.core.resource.ClientResources;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.redis.RedisUriUtil;
|
||||||
|
|
||||||
public class RedisConfiguration {
|
@JsonTypeName("default")
|
||||||
|
public class RedisConfiguration implements SingletonRedisClientFactory {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
@ -22,11 +25,6 @@ public class RedisConfiguration {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private Duration timeout = Duration.ofSeconds(1);
|
private Duration timeout = Duration.ofSeconds(1);
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@NotNull
|
|
||||||
@Valid
|
|
||||||
private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
|
|
||||||
|
|
||||||
public String getUri() {
|
public String getUri() {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
@ -35,7 +33,12 @@ public class RedisConfiguration {
|
|||||||
return timeout;
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CircuitBreakerConfiguration getCircuitBreakerConfiguration() {
|
@Override
|
||||||
return circuitBreaker;
|
public RedisClient build(final ClientResources clientResources) {
|
||||||
|
final RedisClient redisClient = RedisClient.create(clientResources,
|
||||||
|
RedisUriUtil.createRedisUriWithTimeout(uri, timeout));
|
||||||
|
redisClient.setDefaultTimeout(timeout);
|
||||||
|
|
||||||
|
return redisClient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.core.setup.Environment;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RegistrationServiceConfiguration.class)
|
||||||
|
public interface RegistrationServiceClientFactory extends Discoverable {
|
||||||
|
|
||||||
|
RegistrationServiceClient build(Environment environment, Executor callbackExecutor,
|
||||||
|
ScheduledExecutorService identityRefreshExecutor);
|
||||||
|
}
|
@ -1,10 +1,35 @@
|
|||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import io.dropwizard.core.setup.Environment;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import org.whispersystems.textsecuregcm.registration.IdentityTokenCallCredentials;
|
||||||
|
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||||
|
|
||||||
|
@JsonTypeName("default")
|
||||||
public record RegistrationServiceConfiguration(@NotBlank String host,
|
public record RegistrationServiceConfiguration(@NotBlank String host,
|
||||||
int port,
|
int port,
|
||||||
@NotBlank String credentialConfigurationJson,
|
@NotBlank String credentialConfigurationJson,
|
||||||
@NotBlank String identityTokenAudience,
|
@NotBlank String identityTokenAudience,
|
||||||
@NotBlank String registrationCaCertificate) {
|
@NotBlank String registrationCaCertificate) implements
|
||||||
|
RegistrationServiceClientFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegistrationServiceClient build(final Environment environment, final Executor callbackExecutor,
|
||||||
|
final ScheduledExecutorService identityRefreshExecutor) {
|
||||||
|
try {
|
||||||
|
final IdentityTokenCallCredentials callCredentials = IdentityTokenCallCredentials.fromCredentialConfig(
|
||||||
|
credentialConfigurationJson, identityTokenAudience, identityRefreshExecutor);
|
||||||
|
|
||||||
|
environment.lifecycle().manage(callCredentials);
|
||||||
|
|
||||||
|
return new RegistrationServiceClient(host, port, callCredentials, registrationCaCertificate,
|
||||||
|
identityRefreshExecutor);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = MonitoredS3ObjectConfiguration.class)
|
||||||
|
public interface S3ObjectMonitorFactory extends Discoverable {
|
||||||
|
|
||||||
|
S3ObjectMonitor build(AwsCredentialsProvider awsCredentialsProvider,
|
||||||
|
ScheduledExecutorService refreshExecutorService);
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.dropwizard.jackson.Discoverable;
|
||||||
|
import io.lettuce.core.RedisClient;
|
||||||
|
import io.lettuce.core.resource.ClientResources;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RedisConfiguration.class)
|
||||||
|
public interface SingletonRedisClientFactory extends Discoverable {
|
||||||
|
|
||||||
|
RedisClient build(ClientResources clientResources);
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = StaticAwsCredentialsFactory.class)
|
||||||
|
@JsonTypeName("static")
|
||||||
|
public record StaticAwsCredentialsFactory(@NotNull SecretString accessKeyId,
|
||||||
|
@NotNull SecretString secretAccessKey) implements
|
||||||
|
AwsCredentialsProviderFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AwsCredentialsProvider build() {
|
||||||
|
return StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKeyId.value(), secretAccessKey.value()));
|
||||||
|
}
|
||||||
|
}
|
@ -7,25 +7,24 @@ package org.whispersystems.textsecuregcm.geo;
|
|||||||
|
|
||||||
import com.maxmind.db.CHMCache;
|
import com.maxmind.db.CHMCache;
|
||||||
import com.maxmind.geoip2.DatabaseReader;
|
import com.maxmind.geoip2.DatabaseReader;
|
||||||
import com.maxmind.geoip2.GeoIp2Provider;
|
|
||||||
import io.dropwizard.lifecycle.Managed;
|
import io.dropwizard.lifecycle.Managed;
|
||||||
import io.micrometer.core.instrument.Metrics;
|
import io.micrometer.core.instrument.Metrics;
|
||||||
import io.micrometer.core.instrument.Timer;
|
import io.micrometer.core.instrument.Timer;
|
||||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
|
||||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
|
||||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
|
||||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||||
|
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||||
|
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||||
|
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
|
||||||
public class MaxMindDatabaseManager implements Supplier<DatabaseReader>, Managed {
|
public class MaxMindDatabaseManager implements Supplier<DatabaseReader>, Managed {
|
||||||
|
|
||||||
@ -39,21 +38,11 @@ public class MaxMindDatabaseManager implements Supplier<DatabaseReader>, Managed
|
|||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(MaxMindDatabaseManager.class);
|
private static final Logger log = LoggerFactory.getLogger(MaxMindDatabaseManager.class);
|
||||||
|
|
||||||
public MaxMindDatabaseManager(
|
public MaxMindDatabaseManager(final ScheduledExecutorService executorService,
|
||||||
@Nonnull final ScheduledExecutorService executorService,
|
final AwsCredentialsProvider awsCredentialsProvider, final S3ObjectMonitorFactory configuration,
|
||||||
@Nonnull final MonitoredS3ObjectConfiguration configuration,
|
final String databaseTag) {
|
||||||
@Nonnull final String databaseTag
|
|
||||||
){
|
|
||||||
this.databaseMonitor = new S3ObjectMonitor(
|
|
||||||
configuration.s3Region(),
|
|
||||||
configuration.s3Bucket(),
|
|
||||||
configuration.objectKey(),
|
|
||||||
configuration.maxSize(),
|
|
||||||
executorService,
|
|
||||||
configuration.refreshInterval(),
|
|
||||||
this::handleDatabaseChanged
|
|
||||||
);
|
|
||||||
|
|
||||||
|
this.databaseMonitor = configuration.build(awsCredentialsProvider, executorService);
|
||||||
this.databaseTag = databaseTag;
|
this.databaseTag = databaseTag;
|
||||||
this.refreshTimer = Metrics.timer(MetricsUtil.name(MaxMindDatabaseManager.class, "refresh"), "db", databaseTag);
|
this.refreshTimer = Metrics.timer(MetricsUtil.name(MaxMindDatabaseManager.class, "refresh"), "db", databaseTag);
|
||||||
}
|
}
|
||||||
@ -93,17 +82,15 @@ public class MaxMindDatabaseManager implements Supplier<DatabaseReader>, Managed
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
Managed.super.start();
|
databaseMonitor.start(this::handleDatabaseChanged);
|
||||||
databaseMonitor.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws Exception {
|
public void stop() throws Exception {
|
||||||
Managed.super.stop();
|
|
||||||
databaseMonitor.stop();
|
databaseMonitor.stop();
|
||||||
|
|
||||||
final DatabaseReader reader = databaseReader.getAndSet(null);
|
final DatabaseReader reader = databaseReader.getAndSet(null);
|
||||||
if(reader != null) {
|
if (reader != null) {
|
||||||
reader.close();
|
reader.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,8 @@ public class MetricsUtil {
|
|||||||
|
|
||||||
environment.lifecycle().addEventListener(new ApplicationShutdownMonitor(Metrics.globalRegistry));
|
environment.lifecycle().addEventListener(new ApplicationShutdownMonitor(Metrics.globalRegistry));
|
||||||
environment.lifecycle().addEventListener(
|
environment.lifecycle().addEventListener(
|
||||||
new MicrometerRegistryManager(Metrics.globalRegistry, config.getDatadogConfiguration().pollingFrequency()));
|
new MicrometerRegistryManager(Metrics.globalRegistry,
|
||||||
|
config.getDatadogConfiguration().getShutdownWaitDuration()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -14,14 +14,13 @@ import org.slf4j.LoggerFactory;
|
|||||||
public class MicrometerRegistryManager implements LifeCycle.Listener {
|
public class MicrometerRegistryManager implements LifeCycle.Listener {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MicrometerRegistryManager.class);
|
private static final Logger logger = LoggerFactory.getLogger(MicrometerRegistryManager.class);
|
||||||
private static final Duration BUFFER = Duration.ofSeconds(5);
|
|
||||||
|
|
||||||
private final MeterRegistry meterRegistry;
|
private final MeterRegistry meterRegistry;
|
||||||
private final Duration waitDuration;
|
private final Duration waitDuration;
|
||||||
|
|
||||||
public MicrometerRegistryManager(final MeterRegistry meterRegistry, final Duration pollingFrequency) {
|
public MicrometerRegistryManager(final MeterRegistry meterRegistry, final Duration waitDuration) {
|
||||||
this.meterRegistry = meterRegistry;
|
this.meterRegistry = meterRegistry;
|
||||||
this.waitDuration = pollingFrequency.plus(BUFFER);
|
this.waitDuration = waitDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.push;
|
|||||||
|
|
||||||
import static com.codahale.metrics.MetricRegistry.name;
|
import static com.codahale.metrics.MetricRegistry.name;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import io.dropwizard.lifecycle.Managed;
|
import io.dropwizard.lifecycle.Managed;
|
||||||
@ -17,11 +16,9 @@ import io.lettuce.core.api.StatefulRedisConnection;
|
|||||||
import io.lettuce.core.codec.ByteArrayCodec;
|
import io.lettuce.core.codec.ByteArrayCodec;
|
||||||
import io.lettuce.core.pubsub.RedisPubSubAdapter;
|
import io.lettuce.core.pubsub.RedisPubSubAdapter;
|
||||||
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||||
import io.lettuce.core.resource.ClientResources;
|
|
||||||
import io.micrometer.core.instrument.Metrics;
|
import io.micrometer.core.instrument.Metrics;
|
||||||
import io.micrometer.core.instrument.Tags;
|
import io.micrometer.core.instrument.Tags;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -29,7 +26,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.redis.RedisOperation;
|
import org.whispersystems.textsecuregcm.redis.RedisOperation;
|
||||||
import org.whispersystems.textsecuregcm.redis.RedisUriUtil;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.PubSubProtos;
|
import org.whispersystems.textsecuregcm.storage.PubSubProtos;
|
||||||
import org.whispersystems.textsecuregcm.util.CircuitBreakerUtil;
|
import org.whispersystems.textsecuregcm.util.CircuitBreakerUtil;
|
||||||
import org.whispersystems.textsecuregcm.websocket.InvalidWebsocketAddressException;
|
import org.whispersystems.textsecuregcm.websocket.InvalidWebsocketAddressException;
|
||||||
@ -56,22 +52,10 @@ public class ProvisioningManager extends RedisPubSubAdapter<byte[], byte[]> impl
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ProvisioningManager.class);
|
private static final Logger logger = LoggerFactory.getLogger(ProvisioningManager.class);
|
||||||
|
|
||||||
public ProvisioningManager(final String redisUri,
|
public ProvisioningManager(final RedisClient redisClient,
|
||||||
final ClientResources clientResources,
|
|
||||||
final Duration timeout,
|
|
||||||
final CircuitBreakerConfiguration circuitBreakerConfiguration) {
|
|
||||||
|
|
||||||
this(RedisClient.create(clientResources, RedisUriUtil.createRedisUriWithTimeout(redisUri, timeout)), timeout,
|
|
||||||
circuitBreakerConfiguration);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
ProvisioningManager(final RedisClient redisClient,
|
|
||||||
final Duration timeout,
|
|
||||||
final CircuitBreakerConfiguration circuitBreakerConfiguration) {
|
final CircuitBreakerConfiguration circuitBreakerConfiguration) {
|
||||||
|
|
||||||
this.redisClient = redisClient;
|
this.redisClient = redisClient;
|
||||||
this.redisClient.setDefaultTimeout(timeout);
|
|
||||||
|
|
||||||
this.subscriptionConnection = redisClient.connectPubSub(new ByteArrayCodec());
|
this.subscriptionConnection = redisClient.connectPubSub(new ByteArrayCodec());
|
||||||
this.publicationConnection = redisClient.connect(new ByteArrayCodec());
|
this.publicationConnection = redisClient.connect(new ByteArrayCodec());
|
||||||
|
@ -7,13 +7,13 @@ package org.whispersystems.textsecuregcm.registration;
|
|||||||
import com.google.auth.oauth2.ExternalAccountCredentials;
|
import com.google.auth.oauth2.ExternalAccountCredentials;
|
||||||
import com.google.auth.oauth2.ImpersonatedCredentials;
|
import com.google.auth.oauth2.ImpersonatedCredentials;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import io.dropwizard.lifecycle.Managed;
|
||||||
import io.github.resilience4j.core.IntervalFunction;
|
import io.github.resilience4j.core.IntervalFunction;
|
||||||
import io.github.resilience4j.retry.Retry;
|
import io.github.resilience4j.retry.Retry;
|
||||||
import io.github.resilience4j.retry.RetryConfig;
|
import io.github.resilience4j.retry.RetryConfig;
|
||||||
import io.grpc.CallCredentials;
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.Metadata;
|
import io.grpc.Metadata;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
@ -28,7 +28,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
class IdentityTokenCallCredentials extends CallCredentials implements Closeable {
|
public class IdentityTokenCallCredentials extends CallCredentials implements Managed {
|
||||||
private static final Duration IDENTITY_TOKEN_LIFETIME = Duration.ofHours(1);
|
private static final Duration IDENTITY_TOKEN_LIFETIME = Duration.ofHours(1);
|
||||||
private static final Duration IDENTITY_TOKEN_REFRESH_BUFFER = Duration.ofMinutes(10);
|
private static final Duration IDENTITY_TOKEN_REFRESH_BUFFER = Duration.ofMinutes(10);
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class IdentityTokenCallCredentials extends CallCredentials implements Closeable
|
|||||||
TimeUnit.MILLISECONDS);
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static IdentityTokenCallCredentials fromCredentialConfig(
|
public static IdentityTokenCallCredentials fromCredentialConfig(
|
||||||
final String credentialConfigJson,
|
final String credentialConfigJson,
|
||||||
final String audience,
|
final String audience,
|
||||||
final ScheduledExecutorService scheduledExecutorService) throws IOException {
|
final ScheduledExecutorService scheduledExecutorService) throws IOException {
|
||||||
@ -129,7 +129,7 @@ class IdentityTokenCallCredentials extends CallCredentials implements Closeable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void stop() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!scheduledFuture.isDone()) {
|
if (!scheduledFuture.isDone()) {
|
||||||
scheduledFuture.cancel(true);
|
scheduledFuture.cancel(true);
|
||||||
|
@ -8,6 +8,7 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
|||||||
import com.google.i18n.phonenumbers.Phonenumber;
|
import com.google.i18n.phonenumbers.Phonenumber;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import io.dropwizard.lifecycle.Managed;
|
import io.dropwizard.lifecycle.Managed;
|
||||||
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
import io.grpc.Deadline;
|
import io.grpc.Deadline;
|
||||||
import io.grpc.Grpc;
|
import io.grpc.Grpc;
|
||||||
@ -21,7 +22,6 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
@ -38,7 +38,6 @@ import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
|||||||
public class RegistrationServiceClient implements Managed {
|
public class RegistrationServiceClient implements Managed {
|
||||||
|
|
||||||
private final ManagedChannel channel;
|
private final ManagedChannel channel;
|
||||||
private final IdentityTokenCallCredentials identityTokenCallCredentials;
|
|
||||||
private final RegistrationServiceGrpc.RegistrationServiceFutureStub stub;
|
private final RegistrationServiceGrpc.RegistrationServiceFutureStub stub;
|
||||||
private final Executor callbackExecutor;
|
private final Executor callbackExecutor;
|
||||||
|
|
||||||
@ -61,11 +60,9 @@ public class RegistrationServiceClient implements Managed {
|
|||||||
|
|
||||||
public RegistrationServiceClient(final String host,
|
public RegistrationServiceClient(final String host,
|
||||||
final int port,
|
final int port,
|
||||||
final String credentialConfigJson,
|
final CallCredentials callCredentials,
|
||||||
final String identityTokenAudience,
|
|
||||||
final String caCertificatePem,
|
final String caCertificatePem,
|
||||||
final Executor callbackExecutor,
|
final Executor callbackExecutor) throws IOException {
|
||||||
final ScheduledExecutorService identityRefreshExecutor) throws IOException {
|
|
||||||
|
|
||||||
try (final ByteArrayInputStream certificateInputStream = new ByteArrayInputStream(caCertificatePem.getBytes(StandardCharsets.UTF_8))) {
|
try (final ByteArrayInputStream certificateInputStream = new ByteArrayInputStream(caCertificatePem.getBytes(StandardCharsets.UTF_8))) {
|
||||||
final ChannelCredentials tlsChannelCredentials = TlsChannelCredentials.newBuilder()
|
final ChannelCredentials tlsChannelCredentials = TlsChannelCredentials.newBuilder()
|
||||||
@ -77,10 +74,7 @@ public class RegistrationServiceClient implements Managed {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.identityTokenCallCredentials = IdentityTokenCallCredentials.fromCredentialConfig(
|
this.stub = RegistrationServiceGrpc.newFutureStub(channel).withCallCredentials(callCredentials);
|
||||||
credentialConfigJson, identityTokenAudience, identityRefreshExecutor);
|
|
||||||
|
|
||||||
this.stub = RegistrationServiceGrpc.newFutureStub(channel).withCallCredentials(identityTokenCallCredentials);
|
|
||||||
|
|
||||||
this.callbackExecutor = callbackExecutor;
|
this.callbackExecutor = callbackExecutor;
|
||||||
}
|
}
|
||||||
@ -284,6 +278,5 @@ public class RegistrationServiceClient implements Managed {
|
|||||||
if (channel != null) {
|
if (channel != null) {
|
||||||
channel.shutdown();
|
channel.shutdown();
|
||||||
}
|
}
|
||||||
this.identityTokenCallCredentials.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2021 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.s3;
|
|
||||||
|
|
||||||
import io.dropwizard.lifecycle.Managed;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public interface ManagedSupplier<T> extends Supplier<T>, Managed {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void start() throws Exception {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void stop() throws Exception {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2021 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.s3;
|
|
||||||
|
|
||||||
import static com.codahale.metrics.MetricRegistry.name;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import io.micrometer.core.instrument.Counter;
|
|
||||||
import io.micrometer.core.instrument.Metrics;
|
|
||||||
import io.micrometer.core.instrument.Timer;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
|
||||||
|
|
||||||
public class S3MonitoringSupplier<T> implements ManagedSupplier<T> {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private final Timer refreshTimer;
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private final Counter refreshErrors;
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private final AtomicReference<T> holder;
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private final S3ObjectMonitor monitor;
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private final Function<InputStream, T> parser;
|
|
||||||
|
|
||||||
|
|
||||||
public S3MonitoringSupplier(
|
|
||||||
@Nonnull final ScheduledExecutorService executor,
|
|
||||||
@Nonnull final MonitoredS3ObjectConfiguration cfg,
|
|
||||||
@Nonnull final Function<InputStream, T> parser,
|
|
||||||
@Nonnull final T initial,
|
|
||||||
@Nonnull final String name) {
|
|
||||||
this.refreshTimer = Metrics.timer(name(name, "refresh"));
|
|
||||||
this.refreshErrors = Metrics.counter(name(name, "refreshErrors"));
|
|
||||||
this.holder = new AtomicReference<>(initial);
|
|
||||||
this.parser = requireNonNull(parser);
|
|
||||||
this.monitor = new S3ObjectMonitor(
|
|
||||||
cfg.s3Region(),
|
|
||||||
cfg.s3Bucket(),
|
|
||||||
cfg.objectKey(),
|
|
||||||
cfg.maxSize(),
|
|
||||||
executor,
|
|
||||||
cfg.refreshInterval(),
|
|
||||||
this::handleObjectChange
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nonnull
|
|
||||||
public T get() {
|
|
||||||
return requireNonNull(holder.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() throws Exception {
|
|
||||||
monitor.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() throws Exception {
|
|
||||||
monitor.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleObjectChange(@Nonnull final InputStream inputStream) {
|
|
||||||
refreshTimer.record(() -> {
|
|
||||||
// parser function is supposed to close the input stream
|
|
||||||
try {
|
|
||||||
holder.set(parser.apply(inputStream));
|
|
||||||
} catch (final Exception e) {
|
|
||||||
log.error("failed to update internal state from the monitored object", e);
|
|
||||||
refreshErrors.increment();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ import java.util.function.Consumer;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerService;
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import software.amazon.awssdk.core.ResponseInputStream;
|
import software.amazon.awssdk.core.ResponseInputStream;
|
||||||
import software.amazon.awssdk.regions.Region;
|
import software.amazon.awssdk.regions.Region;
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
@ -39,8 +39,6 @@ public class S3ObjectMonitor {
|
|||||||
private final Duration refreshInterval;
|
private final Duration refreshInterval;
|
||||||
private ScheduledFuture<?> refreshFuture;
|
private ScheduledFuture<?> refreshFuture;
|
||||||
|
|
||||||
private final Consumer<InputStream> changeListener;
|
|
||||||
|
|
||||||
private final AtomicReference<String> lastETag = new AtomicReference<>();
|
private final AtomicReference<String> lastETag = new AtomicReference<>();
|
||||||
|
|
||||||
private final S3Client s3Client;
|
private final S3Client s3Client;
|
||||||
@ -48,24 +46,23 @@ public class S3ObjectMonitor {
|
|||||||
private static final Logger log = LoggerFactory.getLogger(S3ObjectMonitor.class);
|
private static final Logger log = LoggerFactory.getLogger(S3ObjectMonitor.class);
|
||||||
|
|
||||||
public S3ObjectMonitor(
|
public S3ObjectMonitor(
|
||||||
|
final AwsCredentialsProvider awsCredentialsProvider,
|
||||||
final String s3Region,
|
final String s3Region,
|
||||||
final String s3Bucket,
|
final String s3Bucket,
|
||||||
final String objectKey,
|
final String objectKey,
|
||||||
final long maxObjectSize,
|
final long maxObjectSize,
|
||||||
final ScheduledExecutorService refreshExecutorService,
|
final ScheduledExecutorService refreshExecutorService,
|
||||||
final Duration refreshInterval,
|
final Duration refreshInterval) {
|
||||||
final Consumer<InputStream> changeListener) {
|
|
||||||
|
|
||||||
this(S3Client.builder()
|
this(S3Client.builder()
|
||||||
.region(Region.of(s3Region))
|
.region(Region.of(s3Region))
|
||||||
.credentialsProvider(WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER)
|
.credentialsProvider(awsCredentialsProvider)
|
||||||
.build(),
|
.build(),
|
||||||
s3Bucket,
|
s3Bucket,
|
||||||
objectKey,
|
objectKey,
|
||||||
maxObjectSize,
|
maxObjectSize,
|
||||||
refreshExecutorService,
|
refreshExecutorService,
|
||||||
refreshInterval,
|
refreshInterval);
|
||||||
changeListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@ -75,8 +72,7 @@ public class S3ObjectMonitor {
|
|||||||
final String objectKey,
|
final String objectKey,
|
||||||
final long maxObjectSize,
|
final long maxObjectSize,
|
||||||
final ScheduledExecutorService refreshExecutorService,
|
final ScheduledExecutorService refreshExecutorService,
|
||||||
final Duration refreshInterval,
|
final Duration refreshInterval) {
|
||||||
final Consumer<InputStream> changeListener) {
|
|
||||||
|
|
||||||
this.s3Client = s3Client;
|
this.s3Client = s3Client;
|
||||||
this.s3Bucket = s3Bucket;
|
this.s3Bucket = s3Bucket;
|
||||||
@ -85,21 +81,19 @@ public class S3ObjectMonitor {
|
|||||||
|
|
||||||
this.refreshExecutorService = refreshExecutorService;
|
this.refreshExecutorService = refreshExecutorService;
|
||||||
this.refreshInterval = refreshInterval;
|
this.refreshInterval = refreshInterval;
|
||||||
|
|
||||||
this.changeListener = changeListener;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void start() {
|
public synchronized void start(final Consumer<InputStream> changeListener) {
|
||||||
if (refreshFuture != null) {
|
if (refreshFuture != null) {
|
||||||
throw new RuntimeException("S3 object manager already started");
|
throw new RuntimeException("S3 object manager already started");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the first request immediately/blocking, then start subsequent calls.
|
// Run the first request immediately/blocking, then start subsequent calls.
|
||||||
log.info("Initial request for s3://{}/{}", s3Bucket, objectKey);
|
log.info("Initial request for s3://{}/{}", s3Bucket, objectKey);
|
||||||
refresh();
|
refresh(changeListener);
|
||||||
|
|
||||||
refreshFuture = refreshExecutorService
|
refreshFuture = refreshExecutorService
|
||||||
.scheduleAtFixedRate(this::refresh, refreshInterval.toMillis(), refreshInterval.toMillis(),
|
.scheduleAtFixedRate(() -> refresh(changeListener), refreshInterval.toMillis(), refreshInterval.toMillis(),
|
||||||
TimeUnit.MILLISECONDS);
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +133,7 @@ public class S3ObjectMonitor {
|
|||||||
* changed since the last call to {@link #getObject()} or {@code refresh()}.
|
* changed since the last call to {@link #getObject()} or {@code refresh()}.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void refresh() {
|
void refresh(final Consumer<InputStream> changeListener) {
|
||||||
try {
|
try {
|
||||||
final HeadObjectResponse objectMetadata = s3Client.headObject(HeadObjectRequest.builder()
|
final HeadObjectResponse objectMetadata = s3Client.headObject(HeadObjectRequest.builder()
|
||||||
.bucket(s3Bucket)
|
.bucket(s3Bucket)
|
||||||
|
@ -24,6 +24,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
||||||
import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient;
|
import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient;
|
||||||
import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest;
|
import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest;
|
||||||
@ -54,9 +55,11 @@ public class DynamicConfigurationManager<T> {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(DynamicConfigurationManager.class);
|
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<T> configurationClass, ScheduledExecutorService scheduledExecutorService) {
|
AwsCredentialsProvider awsCredentialsProvider, Class<T> configurationClass,
|
||||||
|
ScheduledExecutorService scheduledExecutorService) {
|
||||||
this(AppConfigDataClient
|
this(AppConfigDataClient
|
||||||
.builder()
|
.builder()
|
||||||
|
.credentialsProvider(awsCredentialsProvider)
|
||||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||||
.apiCallTimeout(Duration.ofSeconds(10))
|
.apiCallTimeout(Duration.ofSeconds(10))
|
||||||
.apiCallAttemptTimeout(Duration.ofSeconds(10)).build())
|
.apiCallAttemptTimeout(Duration.ofSeconds(10)).build())
|
||||||
@ -86,6 +89,9 @@ public class DynamicConfigurationManager<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
if (initialized.getCount() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
configuration.set(retrieveInitialDynamicConfiguration());
|
configuration.set(retrieveInitialDynamicConfiguration());
|
||||||
initialized.countDown();
|
initialized.countDown();
|
||||||
|
|
||||||
|
@ -19,8 +19,10 @@ import com.braintreegateway.TransactionSearchRequest;
|
|||||||
import com.braintreegateway.exceptions.BraintreeException;
|
import com.braintreegateway.exceptions.BraintreeException;
|
||||||
import com.braintreegateway.exceptions.NotFoundException;
|
import com.braintreegateway.exceptions.NotFoundException;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.google.cloud.pubsub.v1.Publisher;
|
import com.google.cloud.pubsub.v1.PublisherInterface;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.pubsub.v1.PubsubMessage;
|
||||||
|
import io.micrometer.core.instrument.Metrics;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -40,8 +42,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.ws.rs.ClientErrorException;
|
import javax.ws.rs.ClientErrorException;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import com.google.pubsub.v1.PubsubMessage;
|
|
||||||
import io.micrometer.core.instrument.Metrics;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||||
@ -65,7 +65,7 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
|||||||
private final BraintreeGateway braintreeGateway;
|
private final BraintreeGateway braintreeGateway;
|
||||||
private final BraintreeGraphqlClient braintreeGraphqlClient;
|
private final BraintreeGraphqlClient braintreeGraphqlClient;
|
||||||
private final CurrencyConversionManager currencyConversionManager;
|
private final CurrencyConversionManager currencyConversionManager;
|
||||||
private final Publisher pubsubPublisher;
|
private final PublisherInterface pubsubPublisher;
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
private final Map<PaymentMethod, Set<String>> supportedCurrenciesByPaymentMethod;
|
private final Map<PaymentMethod, Set<String>> supportedCurrenciesByPaymentMethod;
|
||||||
private final Map<String, String> currenciesToMerchantAccounts;
|
private final Map<String, String> currenciesToMerchantAccounts;
|
||||||
@ -79,7 +79,7 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
|||||||
final Map<String, String> currenciesToMerchantAccounts,
|
final Map<String, String> currenciesToMerchantAccounts,
|
||||||
final String graphqlUri,
|
final String graphqlUri,
|
||||||
final CurrencyConversionManager currencyConversionManager,
|
final CurrencyConversionManager currencyConversionManager,
|
||||||
final Publisher pubsubPublisher,
|
final PublisherInterface pubsubPublisher,
|
||||||
final CircuitBreakerConfiguration circuitBreakerConfiguration,
|
final CircuitBreakerConfiguration circuitBreakerConfiguration,
|
||||||
final Executor executor,
|
final Executor executor,
|
||||||
final ScheduledExecutorService retryExecutor) {
|
final ScheduledExecutorService retryExecutor) {
|
||||||
@ -107,7 +107,7 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
|||||||
BraintreeManager(final BraintreeGateway braintreeGateway,
|
BraintreeManager(final BraintreeGateway braintreeGateway,
|
||||||
final Map<PaymentMethod, Set<String>> supportedCurrenciesByPaymentMethod,
|
final Map<PaymentMethod, Set<String>> supportedCurrenciesByPaymentMethod,
|
||||||
final Map<String, String> currenciesToMerchantAccounts, final BraintreeGraphqlClient braintreeGraphqlClient,
|
final Map<String, String> currenciesToMerchantAccounts, final BraintreeGraphqlClient braintreeGraphqlClient,
|
||||||
final CurrencyConversionManager currencyConversionManager, final Publisher pubsubPublisher,
|
final CurrencyConversionManager currencyConversionManager, final PublisherInterface pubsubPublisher,
|
||||||
final Executor executor) {
|
final Executor executor) {
|
||||||
this.braintreeGateway = braintreeGateway;
|
this.braintreeGateway = braintreeGateway;
|
||||||
this.supportedCurrenciesByPaymentMethod = supportedCurrenciesByPaymentMethod;
|
this.supportedCurrenciesByPaymentMethod = supportedCurrenciesByPaymentMethod;
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
package org.whispersystems.textsecuregcm.util;
|
|
||||||
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
|
|
||||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
|
||||||
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
|
||||||
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
|
|
||||||
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
|
|
||||||
import software.amazon.awssdk.regions.Region;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
|
||||||
|
|
||||||
public class DynamoDbFromConfig {
|
|
||||||
|
|
||||||
public static DynamoDbClient client(DynamoDbClientConfiguration config, AwsCredentialsProvider credentialsProvider) {
|
|
||||||
return DynamoDbClient.builder()
|
|
||||||
.region(Region.of(config.region()))
|
|
||||||
.credentialsProvider(credentialsProvider)
|
|
||||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
|
||||||
.apiCallTimeout(config.clientExecutionTimeout())
|
|
||||||
.apiCallAttemptTimeout(config.clientRequestTimeout())
|
|
||||||
.build())
|
|
||||||
.httpClientBuilder(AwsCrtHttpClient
|
|
||||||
.builder()
|
|
||||||
.maxConcurrency(config.maxConnections()))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DynamoDbAsyncClient asyncClient(
|
|
||||||
DynamoDbClientConfiguration config,
|
|
||||||
AwsCredentialsProvider credentialsProvider) {
|
|
||||||
return DynamoDbAsyncClient.builder()
|
|
||||||
.region(Region.of(config.region()))
|
|
||||||
.credentialsProvider(credentialsProvider)
|
|
||||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
|
||||||
.apiCallTimeout(config.clientExecutionTimeout())
|
|
||||||
.apiCallAttemptTimeout(config.clientRequestTimeout())
|
|
||||||
.build())
|
|
||||||
.httpClientBuilder(NettyNioAsyncHttpClient.builder()
|
|
||||||
.maxConcurrency(config.maxConnections()))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,7 +23,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerService;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||||
@ -50,10 +49,10 @@ import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
|||||||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.UsernameHashNotAvailableException;
|
import org.whispersystems.textsecuregcm.storage.UsernameHashNotAvailableException;
|
||||||
import org.whispersystems.textsecuregcm.storage.UsernameReservationNotFoundException;
|
import org.whispersystems.textsecuregcm.storage.UsernameReservationNotFoundException;
|
||||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
|
||||||
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
|
||||||
@ -97,18 +96,19 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
|
||||||
|
final AwsCredentialsProvider awsCredentialsProvider = configuration.getAwsCredentialsConfiguration().build();
|
||||||
|
|
||||||
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
||||||
.scheduledExecutorService(name(getClass(), "dynamicConfiguration-%d")).threads(1).build();
|
.scheduledExecutorService(name(getClass(), "dynamicConfiguration-%d")).threads(1).build();
|
||||||
|
|
||||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = configuration.getAppConfig().build(
|
||||||
configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(),
|
DynamicConfiguration.class, dynamicConfigurationExecutor, awsCredentialsProvider);
|
||||||
configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class, dynamicConfigurationExecutor);
|
|
||||||
dynamicConfigurationManager.start();
|
dynamicConfigurationManager.start();
|
||||||
|
|
||||||
final ClientResources.Builder redisClientResourcesBuilder = ClientResources.builder();
|
final ClientResources.Builder redisClientResourcesBuilder = ClientResources.builder();
|
||||||
|
|
||||||
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache_cluster",
|
FaultTolerantRedisCluster cacheCluster = configuration.getCacheClusterConfiguration().build("main_cache_cluster",
|
||||||
configuration.getCacheClusterConfiguration(), redisClientResourcesBuilder);
|
redisClientResourcesBuilder);
|
||||||
|
|
||||||
Scheduler messageDeliveryScheduler = Schedulers.fromExecutorService(
|
Scheduler messageDeliveryScheduler = Schedulers.fromExecutorService(
|
||||||
environment.lifecycle().executorService("messageDelivery-%d").maxThreads(4)
|
environment.lifecycle().executorService("messageDelivery-%d").maxThreads(4)
|
||||||
@ -135,11 +135,11 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi
|
|||||||
ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
||||||
configuration.getSvr2Configuration());
|
configuration.getSvr2Configuration());
|
||||||
|
|
||||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
DynamoDbAsyncClient dynamoDbAsyncClient = configuration.getDynamoDbClientConfiguration()
|
||||||
configuration.getDynamoDbClientConfiguration(), WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
.buildAsyncClient(awsCredentialsProvider);
|
||||||
|
|
||||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(configuration.getDynamoDbClientConfiguration(),
|
DynamoDbClient dynamoDbClient = configuration.getDynamoDbClientConfiguration()
|
||||||
WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
.buildSyncClient(awsCredentialsProvider);
|
||||||
|
|
||||||
RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
||||||
configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(),
|
configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(),
|
||||||
@ -173,12 +173,12 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi
|
|||||||
configuration.getDynamoDbTables().getMessages().getTableName(),
|
configuration.getDynamoDbTables().getMessages().getTableName(),
|
||||||
configuration.getDynamoDbTables().getMessages().getExpiration(),
|
configuration.getDynamoDbTables().getMessages().getExpiration(),
|
||||||
messageDeletionExecutor);
|
messageDeletionExecutor);
|
||||||
FaultTolerantRedisCluster messagesCluster = new FaultTolerantRedisCluster("messages",
|
FaultTolerantRedisCluster messagesCluster = configuration.getMessageCacheConfiguration()
|
||||||
configuration.getMessageCacheConfiguration().getRedisClusterConfiguration(), redisClientResourcesBuilder);
|
.getRedisClusterConfiguration().build("messages", redisClientResourcesBuilder);
|
||||||
FaultTolerantRedisCluster clientPresenceCluster = new FaultTolerantRedisCluster("client_presence",
|
FaultTolerantRedisCluster clientPresenceCluster = configuration.getClientPresenceClusterConfiguration()
|
||||||
configuration.getClientPresenceClusterConfiguration(), redisClientResourcesBuilder);
|
.build("client_presence", redisClientResourcesBuilder);
|
||||||
FaultTolerantRedisCluster rateLimitersCluster = new FaultTolerantRedisCluster("rate_limiters",
|
FaultTolerantRedisCluster rateLimitersCluster = configuration.getRateLimitersCluster().build("rate_limiters",
|
||||||
configuration.getRateLimitersCluster(), redisClientResourcesBuilder);
|
redisClientResourcesBuilder);
|
||||||
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(
|
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(
|
||||||
secureValueRecoveryCredentialsGenerator, secureValueRecoveryExecutor, secureValueRecoveryServiceRetryExecutor,
|
secureValueRecoveryCredentialsGenerator, secureValueRecoveryExecutor, secureValueRecoveryServiceRetryExecutor,
|
||||||
configuration.getSvr2Configuration());
|
configuration.getSvr2Configuration());
|
||||||
|
@ -18,7 +18,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||||||
import org.signal.libsignal.zkgroup.GenericServerSecretParams;
|
import org.signal.libsignal.zkgroup.GenericServerSecretParams;
|
||||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerService;
|
|
||||||
import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
|
import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.backup.BackupManager;
|
import org.whispersystems.textsecuregcm.backup.BackupManager;
|
||||||
@ -49,10 +48,10 @@ import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
|
|||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
||||||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
|
||||||
import org.whispersystems.textsecuregcm.util.ManagedAwsCrt;
|
import org.whispersystems.textsecuregcm.util.ManagedAwsCrt;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
|
||||||
@ -80,20 +79,21 @@ record CommandDependencies(
|
|||||||
|
|
||||||
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
|
||||||
|
final AwsCredentialsProvider awsCredentialsProvider = configuration.getAwsCredentialsConfiguration().build();
|
||||||
|
|
||||||
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
||||||
.scheduledExecutorService(name(name, "dynamicConfiguration-%d")).threads(1).build();
|
.scheduledExecutorService(name(name, "dynamicConfiguration-%d")).threads(1).build();
|
||||||
|
|
||||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = configuration.getAppConfig().build(
|
||||||
configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(),
|
DynamicConfiguration.class, dynamicConfigurationExecutor, awsCredentialsProvider);
|
||||||
configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class, dynamicConfigurationExecutor);
|
|
||||||
dynamicConfigurationManager.start();
|
dynamicConfigurationManager.start();
|
||||||
|
|
||||||
MetricsUtil.configureRegistries(configuration, environment, dynamicConfigurationManager);
|
MetricsUtil.configureRegistries(configuration, environment, dynamicConfigurationManager);
|
||||||
|
|
||||||
final ClientResources.Builder redisClientResourcesBuilder = ClientResources.builder();
|
final ClientResources.Builder redisClientResourcesBuilder = ClientResources.builder();
|
||||||
|
|
||||||
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache",
|
FaultTolerantRedisCluster cacheCluster = configuration.getCacheClusterConfiguration().build("main_cache",
|
||||||
configuration.getCacheClusterConfiguration(), redisClientResourcesBuilder);
|
redisClientResourcesBuilder);
|
||||||
|
|
||||||
ScheduledExecutorService recurringJobExecutor = environment.lifecycle()
|
ScheduledExecutorService recurringJobExecutor = environment.lifecycle()
|
||||||
.scheduledExecutorService(name(name, "recurringJob-%d")).threads(2).build();
|
.scheduledExecutorService(name(name, "recurringJob-%d")).threads(2).build();
|
||||||
@ -124,11 +124,11 @@ record CommandDependencies(
|
|||||||
ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
||||||
configuration.getSvr2Configuration());
|
configuration.getSvr2Configuration());
|
||||||
|
|
||||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
DynamoDbAsyncClient dynamoDbAsyncClient = configuration.getDynamoDbClientConfiguration()
|
||||||
configuration.getDynamoDbClientConfiguration(), WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
.buildAsyncClient(awsCredentialsProvider);
|
||||||
|
|
||||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
DynamoDbClient dynamoDbClient = configuration.getDynamoDbClientConfiguration()
|
||||||
configuration.getDynamoDbClientConfiguration(), WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
.buildSyncClient(awsCredentialsProvider);
|
||||||
|
|
||||||
RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
||||||
configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(),
|
configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(),
|
||||||
@ -163,12 +163,12 @@ record CommandDependencies(
|
|||||||
configuration.getDynamoDbTables().getMessages().getTableName(),
|
configuration.getDynamoDbTables().getMessages().getTableName(),
|
||||||
configuration.getDynamoDbTables().getMessages().getExpiration(),
|
configuration.getDynamoDbTables().getMessages().getExpiration(),
|
||||||
messageDeletionExecutor);
|
messageDeletionExecutor);
|
||||||
FaultTolerantRedisCluster messagesCluster = new FaultTolerantRedisCluster("messages",
|
FaultTolerantRedisCluster messagesCluster = configuration.getMessageCacheConfiguration()
|
||||||
configuration.getMessageCacheConfiguration().getRedisClusterConfiguration(), redisClientResourcesBuilder);
|
.getRedisClusterConfiguration().build("messages", redisClientResourcesBuilder);
|
||||||
FaultTolerantRedisCluster clientPresenceCluster = new FaultTolerantRedisCluster("client_presence",
|
FaultTolerantRedisCluster clientPresenceCluster = configuration.getClientPresenceClusterConfiguration()
|
||||||
configuration.getClientPresenceClusterConfiguration(), redisClientResourcesBuilder);
|
.build("client_presence", redisClientResourcesBuilder);
|
||||||
FaultTolerantRedisCluster rateLimitersCluster = new FaultTolerantRedisCluster("rate_limiters",
|
FaultTolerantRedisCluster rateLimitersCluster = configuration.getRateLimitersCluster().build("rate_limiters",
|
||||||
configuration.getRateLimitersCluster(), redisClientResourcesBuilder);
|
redisClientResourcesBuilder);
|
||||||
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(
|
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(
|
||||||
secureValueRecoveryCredentialsGenerator, secureValueRecoveryServiceExecutor,
|
secureValueRecoveryCredentialsGenerator, secureValueRecoveryServiceExecutor,
|
||||||
secureValueRecoveryServiceRetryExecutor,
|
secureValueRecoveryServiceRetryExecutor,
|
||||||
|
@ -63,8 +63,8 @@ public class ScheduledApnPushNotificationSenderServiceCommand extends ServerComm
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler",
|
final FaultTolerantRedisCluster pushSchedulerCluster = configuration.getPushSchedulerCluster()
|
||||||
configuration.getPushSchedulerCluster(), deps.redisClusterClientResourcesBuilder());
|
.build("push_scheduler", deps.redisClusterClientResourcesBuilder());
|
||||||
|
|
||||||
final ExecutorService apnSenderExecutor = environment.lifecycle().executorService(name(getClass(), "apnSender-%d"))
|
final ExecutorService apnSenderExecutor = environment.lifecycle().executorService(name(getClass(), "apnSender-%d"))
|
||||||
.maxThreads(1).minThreads(1).build();
|
.maxThreads(1).minThreads(1).build();
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.DatadogConfiguration
|
||||||
|
org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.HCaptchaClientFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.PaymentsServiceClientsFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.PubSubPublisherFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory
|
||||||
|
org.whispersystems.textsecuregcm.configuration.SingletonRedisClientFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.StaticAwsCredentialsFactory
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm;
|
||||||
|
|
||||||
|
import io.dropwizard.util.Resources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class may be run directly from a correctly configured IDE, or using the command line:
|
||||||
|
* <p>
|
||||||
|
* <code>./mvnw clean integration-test -DskipTests=true -Ptest-server</code>
|
||||||
|
* <p>
|
||||||
|
* <strong>NOTE: many features are non-functional, especially those that depend on external services</strong>
|
||||||
|
*/
|
||||||
|
public class LocalWhisperServerService {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
System.setProperty("secrets.bundle.filename",
|
||||||
|
Resources.getResource("config/test-secrets-bundle.yml").getPath());
|
||||||
|
System.setProperty("sqlite.dir", "service/target/lib");
|
||||||
|
System.setProperty("aws.region", "local-test-region");
|
||||||
|
|
||||||
|
new WhisperServerService().run("server", Resources.getResource("config/test.yml").getPath());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import io.dropwizard.testing.junit5.DropwizardAppExtension;
|
||||||
|
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||||
|
import io.dropwizard.util.Resources;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.ws.rs.client.Client;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema;
|
||||||
|
import org.whispersystems.textsecuregcm.tests.util.TestWebsocketListener;
|
||||||
|
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||||
|
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
||||||
|
|
||||||
|
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||||
|
class WhisperServerServiceTest {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("secrets.bundle.filename",
|
||||||
|
Resources.getResource("config/test-secrets-bundle.yml").getPath());
|
||||||
|
// needed for AppConfigDataClient initialization
|
||||||
|
System.setProperty("aws.region", "local-test-region");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final DropwizardAppExtension<WhisperServerConfiguration> EXTENSION = new DropwizardAppExtension<>(
|
||||||
|
WhisperServerService.class, Resources.getResource("config/test.yml").getPath());
|
||||||
|
|
||||||
|
private WebSocketClient webSocketClient;
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void teardown() {
|
||||||
|
System.clearProperty("secrets.bundle.filename");
|
||||||
|
System.clearProperty("aws.region");
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
webSocketClient = new WebSocketClient();
|
||||||
|
webSocketClient.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() throws Exception {
|
||||||
|
webSocketClient.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void start() throws Exception {
|
||||||
|
// make sure the service nominally starts and responds to health checks
|
||||||
|
|
||||||
|
Client client = EXTENSION.client();
|
||||||
|
|
||||||
|
final Response ping = client.target(
|
||||||
|
String.format("http://localhost:%d%s", EXTENSION.getAdminPort(), "/ping"))
|
||||||
|
.request("application/json")
|
||||||
|
.get();
|
||||||
|
|
||||||
|
assertEquals(200, ping.getStatus());
|
||||||
|
|
||||||
|
final Response healthCheck = client.target(
|
||||||
|
String.format("http://localhost:%d%s", EXTENSION.getLocalPort(), "/health-check"))
|
||||||
|
.request("application/json")
|
||||||
|
.get();
|
||||||
|
|
||||||
|
assertEquals(200, healthCheck.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void websocket() throws Exception {
|
||||||
|
// test unauthenticated websocket
|
||||||
|
|
||||||
|
final TestWebsocketListener testWebsocketListener = new TestWebsocketListener();
|
||||||
|
webSocketClient.connect(testWebsocketListener,
|
||||||
|
URI.create(String.format("ws://localhost:%d/v1/websocket/", EXTENSION.getLocalPort())))
|
||||||
|
.join();
|
||||||
|
|
||||||
|
final WebSocketResponseMessage keepAlive = testWebsocketListener.doGet("/v1/keepalive").join();
|
||||||
|
|
||||||
|
assertEquals(200, keepAlive.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dynamoDb() {
|
||||||
|
// confirm that local dynamodb nominally works
|
||||||
|
|
||||||
|
final AwsCredentialsProvider awsCredentialsProvider = EXTENSION.getConfiguration().getAwsCredentialsConfiguration()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (DynamoDbClient dynamoDbClient = EXTENSION.getConfiguration().getDynamoDbClientConfiguration()
|
||||||
|
.buildSyncClient(awsCredentialsProvider)) {
|
||||||
|
|
||||||
|
final DynamoDbExtension.TableSchema numbers = DynamoDbExtensionSchema.Tables.NUMBERS;
|
||||||
|
final AttributeValue numberAV = AttributeValues.s("+12125550001");
|
||||||
|
|
||||||
|
final GetItemResponse notFoundResponse = dynamoDbClient.getItem(GetItemRequest.builder()
|
||||||
|
.tableName(numbers.tableName())
|
||||||
|
.key(Map.of(numbers.hashKeyName(), numberAV))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
assertFalse(notFoundResponse.hasItem());
|
||||||
|
|
||||||
|
dynamoDbClient.putItem(PutItemRequest.builder()
|
||||||
|
.tableName(numbers.tableName())
|
||||||
|
.item(Map.of(numbers.hashKeyName(), numberAV))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
final GetItemResponse foundResponse = dynamoDbClient.getItem(GetItemRequest.builder()
|
||||||
|
.tableName(numbers.tableName())
|
||||||
|
.key(Map.of(numbers.hashKeyName(), numberAV))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
assertTrue(foundResponse.hasItem());
|
||||||
|
|
||||||
|
dynamoDbClient.deleteItem(DeleteItemRequest.builder()
|
||||||
|
.tableName(numbers.tableName())
|
||||||
|
.key(Map.of(numbers.hashKeyName(), numberAV))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
|
||||||
|
@JsonTypeName("local")
|
||||||
|
public class LocalDynamoDbFactory implements DynamoDbClientFactory {
|
||||||
|
|
||||||
|
private static final DynamoDbExtension EXTENSION = new DynamoDbExtension(System.getProperty("sqlite.dir"),
|
||||||
|
DynamoDbExtensionSchema.Tables.values());
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
EXTENSION.beforeEach(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> EXTENSION.afterEach(null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamoDbClient buildSyncClient(final AwsCredentialsProvider awsCredentialsProvider) {
|
||||||
|
return EXTENSION.getDynamoDbClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamoDbAsyncClient buildAsyncClient(final AwsCredentialsProvider awsCredentialsProvider) {
|
||||||
|
return EXTENSION.getDynamoDbAsyncClient();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import io.lettuce.core.resource.ClientResources;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
|
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
||||||
|
|
||||||
|
@JsonTypeName("local")
|
||||||
|
public class LocalFaultTolerantRedisClusterFactory implements FaultTolerantRedisClusterFactory {
|
||||||
|
|
||||||
|
private static final RedisClusterExtension redisClusterExtension = RedisClusterExtension.builder().build();
|
||||||
|
|
||||||
|
private final AtomicBoolean shutdownHookConfigured = new AtomicBoolean();
|
||||||
|
|
||||||
|
private LocalFaultTolerantRedisClusterFactory() {
|
||||||
|
try {
|
||||||
|
redisClusterExtension.beforeAll(null);
|
||||||
|
redisClusterExtension.beforeEach(null);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FaultTolerantRedisCluster build(final String name, final ClientResources.Builder clientResourcesBuilder) {
|
||||||
|
|
||||||
|
if (shutdownHookConfigured.compareAndSet(false, true)) {
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
try {
|
||||||
|
redisClusterExtension.afterEach(null);
|
||||||
|
redisClusterExtension.afterAll(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
final RedisClusterConfiguration config = new RedisClusterConfiguration();
|
||||||
|
config.setConfigurationUri(RedisClusterExtension.getRedisURIs().getFirst().toString());
|
||||||
|
|
||||||
|
return new FaultTolerantRedisCluster(name, config, clientResourcesBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import io.dropwizard.lifecycle.Managed;
|
||||||
|
import io.lettuce.core.RedisClient;
|
||||||
|
import io.lettuce.core.resource.ClientResources;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.whispersystems.textsecuregcm.redis.RedisSingletonExtension;
|
||||||
|
|
||||||
|
@JsonTypeName("local")
|
||||||
|
public class LocalSingletonRedisClientFactory implements SingletonRedisClientFactory, Managed {
|
||||||
|
|
||||||
|
private static final RedisSingletonExtension redisSingletonExtension = RedisSingletonExtension.builder().build();
|
||||||
|
|
||||||
|
private final AtomicBoolean shutdownHookConfigured = new AtomicBoolean();
|
||||||
|
|
||||||
|
private LocalSingletonRedisClientFactory() {
|
||||||
|
try {
|
||||||
|
redisSingletonExtension.beforeAll(null);
|
||||||
|
redisSingletonExtension.beforeEach(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RedisClient build(final ClientResources clientResources) {
|
||||||
|
|
||||||
|
if (shutdownHookConfigured.compareAndSet(false, true)) {
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
try {
|
||||||
|
this.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedisClient.create(clientResources, redisSingletonExtension.getRedisUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
redisSingletonExtension.afterEach(null);
|
||||||
|
redisSingletonExtension.afterAll(null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@JsonTypeName("nowait")
|
||||||
|
public class NoWaitDogstatsdConfiguration extends DogstatsdConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Duration getShutdownWaitDuration() {
|
||||||
|
return Duration.ZERO;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
|
||||||
|
@JsonTypeName("static")
|
||||||
|
public class StaticDynamicConfigurationManagerFactory implements DynamicConfigurationManagerFactory {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@NotEmpty
|
||||||
|
private String application;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@NotEmpty
|
||||||
|
private String environment;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@NotEmpty
|
||||||
|
private String configuration;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@NotBlank
|
||||||
|
private String staticConfig;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> DynamicConfigurationManager<T> build(final Class<T> klazz,
|
||||||
|
final ScheduledExecutorService scheduledExecutorService, final AwsCredentialsProvider awsCredentialsProvider) {
|
||||||
|
|
||||||
|
return new StaticDynamicConfigurationManager<>(staticConfig, application, environment, configuration,
|
||||||
|
awsCredentialsProvider, klazz, scheduledExecutorService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StaticDynamicConfigurationManager<T> extends DynamicConfigurationManager<T> {
|
||||||
|
|
||||||
|
private final T configuration;
|
||||||
|
|
||||||
|
public StaticDynamicConfigurationManager(final String config, final String application, final String environment,
|
||||||
|
final String configurationName, final AwsCredentialsProvider awsCredentialsProvider,
|
||||||
|
final Class<T> configurationClass, final ScheduledExecutorService scheduledExecutorService) {
|
||||||
|
|
||||||
|
super(application, environment, configurationName, awsCredentialsProvider, configurationClass,
|
||||||
|
scheduledExecutorService);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.configuration = parseConfiguration(config, configurationClass).orElseThrow();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getConfiguration() {
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@JsonTypeName("static")
|
||||||
|
public class StaticS3ObjectMonitorFactory implements S3ObjectMonitorFactory {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private byte[] object = new byte[0];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public S3ObjectMonitor build(final AwsCredentialsProvider awsCredentialsProvider,
|
||||||
|
final ScheduledExecutorService refreshExecutorService) {
|
||||||
|
return new StaticS3ObjectMonitor(object, awsCredentialsProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StaticS3ObjectMonitor extends S3ObjectMonitor {
|
||||||
|
|
||||||
|
private final byte[] object;
|
||||||
|
|
||||||
|
public StaticS3ObjectMonitor(final byte[] object, final AwsCredentialsProvider awsCredentialsProvider) {
|
||||||
|
super(awsCredentialsProvider, "local-test-region", "test-bucket", null, 0L, null, null);
|
||||||
|
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void start(final Consumer<InputStream> changeListener) {
|
||||||
|
changeListener.accept(new ByteArrayInputStream(object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import org.whispersystems.textsecuregcm.captcha.Action;
|
||||||
|
import org.whispersystems.textsecuregcm.captcha.AssessmentResult;
|
||||||
|
import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
|
||||||
|
@JsonTypeName("stub")
|
||||||
|
public class StubHCaptchaClientFactory implements HCaptchaClientFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HCaptchaClient build(final ScheduledExecutorService retryExecutor,
|
||||||
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
|
|
||||||
|
return new StubHCaptchaClient(retryExecutor, new CircuitBreakerConfiguration(), dynamicConfigurationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts any token of the format "test.test.*.*"
|
||||||
|
*/
|
||||||
|
private static class StubHCaptchaClient extends HCaptchaClient {
|
||||||
|
|
||||||
|
public StubHCaptchaClient(final ScheduledExecutorService retryExecutor,
|
||||||
|
final CircuitBreakerConfiguration circuitBreakerConfiguration,
|
||||||
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
|
super(null, retryExecutor, circuitBreakerConfiguration, null, dynamicConfigurationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String scheme() {
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> validSiteKeys(final Action action) {
|
||||||
|
return Set.of("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssessmentResult verify(final String siteKey, final Action action, final String token, final String ip)
|
||||||
|
throws IOException {
|
||||||
|
return AssessmentResult.alwaysValid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.whispersystems.textsecuregcm.currency.CoinMarketCapClient;
|
||||||
|
import org.whispersystems.textsecuregcm.currency.FixerClient;
|
||||||
|
|
||||||
|
@JsonTypeName("stub")
|
||||||
|
public class StubPaymentsServiceClientsFactory implements PaymentsServiceClientsFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FixerClient buildFixerClient(final HttpClient httpClient) {
|
||||||
|
return new StubFixerClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CoinMarketCapClient buildCoinMarketCapClient(final HttpClient httpClient) {
|
||||||
|
return new StubCoinMarketCapClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always returns an empty map of conversions
|
||||||
|
*/
|
||||||
|
private static class StubFixerClient extends FixerClient {
|
||||||
|
|
||||||
|
public StubFixerClient() {
|
||||||
|
super(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, BigDecimal> getConversionsForBase(final String base) throws FixerException {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always returns {@code 0} for spot price checks
|
||||||
|
*/
|
||||||
|
private static class StubCoinMarketCapClient extends CoinMarketCapClient {
|
||||||
|
|
||||||
|
public StubCoinMarketCapClient() {
|
||||||
|
super(null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal getSpotPrice(final String currency, final String base) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import com.google.api.core.ApiFutures;
|
||||||
|
import com.google.cloud.pubsub.v1.PublisherInterface;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@JsonTypeName("stub")
|
||||||
|
public class StubPubSubPublisherFactory implements PubSubPublisherFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublisherInterface build() {
|
||||||
|
return message -> ApiFutures.immediateFuture(UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
|
import com.google.i18n.phonenumbers.Phonenumber;
|
||||||
|
import io.dropwizard.core.setup.Environment;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
||||||
|
import org.whispersystems.textsecuregcm.registration.ClientType;
|
||||||
|
import org.whispersystems.textsecuregcm.registration.MessageTransport;
|
||||||
|
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||||
|
|
||||||
|
@JsonTypeName("stub")
|
||||||
|
public class StubRegistrationServiceClientFactory implements RegistrationServiceClientFactory {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@NotNull
|
||||||
|
private String registrationCaCertificate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegistrationServiceClient build(final Environment environment, final Executor callbackExecutor,
|
||||||
|
final ScheduledExecutorService identityRefreshExecutor) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new StubRegistrationServiceClient(registrationCaCertificate);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StubRegistrationServiceClient extends RegistrationServiceClient {
|
||||||
|
|
||||||
|
private final static Map<String, RegistrationServiceSession> SESSIONS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public StubRegistrationServiceClient(final String registrationCaCertificate) throws IOException {
|
||||||
|
super("example.com", 8080, null, registrationCaCertificate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<RegistrationServiceSession> createRegistrationSession(
|
||||||
|
final Phonenumber.PhoneNumber phoneNumber, final boolean accountExistsWithPhoneNumber, final Duration timeout) {
|
||||||
|
|
||||||
|
final String e164 = PhoneNumberUtil.getInstance()
|
||||||
|
.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
|
||||||
|
|
||||||
|
final byte[] id = new byte[32];
|
||||||
|
new SecureRandom().nextBytes(id);
|
||||||
|
final RegistrationServiceSession session = new RegistrationServiceSession(id, e164, false, 0L, 0L, null,
|
||||||
|
Instant.now().plus(Duration.ofMinutes(10)).toEpochMilli());
|
||||||
|
SESSIONS.put(Base64.getEncoder().encodeToString(id), session);
|
||||||
|
|
||||||
|
return CompletableFuture.completedFuture(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<RegistrationServiceSession> sendVerificationCode(final byte[] sessionId,
|
||||||
|
final MessageTransport messageTransport, final ClientType clientType, final @Nullable String acceptLanguage,
|
||||||
|
final @Nullable String senderOverride, final Duration timeout) {
|
||||||
|
return CompletableFuture.completedFuture(SESSIONS.get(Base64.getEncoder().encodeToString(sessionId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<RegistrationServiceSession> checkVerificationCode(final byte[] sessionId,
|
||||||
|
final String verificationCode, final Duration timeout) {
|
||||||
|
final RegistrationServiceSession session = SESSIONS.get(Base64.getEncoder().encodeToString(sessionId));
|
||||||
|
|
||||||
|
final RegistrationServiceSession updatedSession = new RegistrationServiceSession(sessionId, session.number(),
|
||||||
|
true, 0L, 0L, 0L,
|
||||||
|
Instant.now().plus(Duration.ofMinutes(10)).toEpochMilli());
|
||||||
|
|
||||||
|
SESSIONS.put(Base64.getEncoder().encodeToString(sessionId), updatedSession);
|
||||||
|
return CompletableFuture.completedFuture(updatedSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<RegistrationServiceSession>> getSession(final byte[] sessionId,
|
||||||
|
final Duration timeout) {
|
||||||
|
return CompletableFuture.completedFuture(
|
||||||
|
Optional.ofNullable(SESSIONS.get(Base64.getEncoder().encodeToString(sessionId))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,7 +8,6 @@ import static org.mockito.Mockito.timeout;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@ -32,7 +31,7 @@ class ProvisioningManagerTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() throws Exception {
|
void setUp() throws Exception {
|
||||||
provisioningManager = new ProvisioningManager(REDIS_EXTENSION.getRedisClient(), Duration.ofSeconds(1), new CircuitBreakerConfiguration());
|
provisioningManager = new ProvisioningManager(REDIS_EXTENSION.getRedisClient(), new CircuitBreakerConfiguration());
|
||||||
provisioningManager.start();
|
provisioningManager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.redis;
|
|||||||
import static org.junit.jupiter.api.Assumptions.assumeFalse;
|
import static org.junit.jupiter.api.Assumptions.assumeFalse;
|
||||||
|
|
||||||
import io.lettuce.core.RedisClient;
|
import io.lettuce.core.RedisClient;
|
||||||
|
import io.lettuce.core.RedisURI;
|
||||||
import io.lettuce.core.api.StatefulRedisConnection;
|
import io.lettuce.core.api.StatefulRedisConnection;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
@ -22,6 +23,7 @@ public class RedisSingletonExtension implements BeforeAllCallback, BeforeEachCal
|
|||||||
|
|
||||||
private static RedisServer redisServer;
|
private static RedisServer redisServer;
|
||||||
private RedisClient redisClient;
|
private RedisClient redisClient;
|
||||||
|
private RedisURI redisUri;
|
||||||
|
|
||||||
public static class RedisSingletonExtensionBuilder {
|
public static class RedisSingletonExtensionBuilder {
|
||||||
|
|
||||||
@ -53,7 +55,8 @@ public class RedisSingletonExtension implements BeforeAllCallback, BeforeEachCal
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeEach(final ExtensionContext context) {
|
public void beforeEach(final ExtensionContext context) {
|
||||||
redisClient = RedisClient.create(String.format("redis://127.0.0.1:%d", redisServer.ports().get(0)));
|
redisUri = RedisURI.create("redis://127.0.0.1:%d".formatted(redisServer.ports().get(0)));
|
||||||
|
redisClient = RedisClient.create(redisUri);
|
||||||
|
|
||||||
try (final StatefulRedisConnection<String, String> connection = redisClient.connect()) {
|
try (final StatefulRedisConnection<String, String> connection = redisClient.connect()) {
|
||||||
connection.sync().flushall();
|
connection.sync().flushall();
|
||||||
@ -76,6 +79,10 @@ public class RedisSingletonExtension implements BeforeAllCallback, BeforeEachCal
|
|||||||
return redisClient;
|
return redisClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RedisURI getRedisUri() {
|
||||||
|
return redisUri;
|
||||||
|
}
|
||||||
|
|
||||||
private static int getAvailablePort() throws IOException {
|
private static int getAvailablePort() throws IOException {
|
||||||
try (ServerSocket socket = new ServerSocket(0)) {
|
try (ServerSocket socket = new ServerSocket(0)) {
|
||||||
socket.setReuseAddress(false);
|
socket.setReuseAddress(false);
|
||||||
|
@ -46,8 +46,7 @@ class S3ObjectMonitorTest {
|
|||||||
objectKey,
|
objectKey,
|
||||||
16 * 1024 * 1024,
|
16 * 1024 * 1024,
|
||||||
mock(ScheduledExecutorService.class),
|
mock(ScheduledExecutorService.class),
|
||||||
Duration.ofMinutes(1),
|
Duration.ofMinutes(1));
|
||||||
listener);
|
|
||||||
|
|
||||||
final String uuid = UUID.randomUUID().toString();
|
final String uuid = UUID.randomUUID().toString();
|
||||||
when(s3Client.headObject(HeadObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(
|
when(s3Client.headObject(HeadObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(
|
||||||
@ -55,8 +54,8 @@ class S3ObjectMonitorTest {
|
|||||||
final ResponseInputStream<GetObjectResponse> ris = responseInputStreamFromString("abc", uuid);
|
final ResponseInputStream<GetObjectResponse> ris = responseInputStreamFromString("abc", uuid);
|
||||||
when(s3Client.getObject(GetObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(ris);
|
when(s3Client.getObject(GetObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(ris);
|
||||||
|
|
||||||
objectMonitor.refresh();
|
objectMonitor.refresh(listener);
|
||||||
objectMonitor.refresh();
|
objectMonitor.refresh(listener);
|
||||||
|
|
||||||
verify(listener).accept(ris);
|
verify(listener).accept(ris);
|
||||||
}
|
}
|
||||||
@ -77,8 +76,7 @@ class S3ObjectMonitorTest {
|
|||||||
objectKey,
|
objectKey,
|
||||||
16 * 1024 * 1024,
|
16 * 1024 * 1024,
|
||||||
mock(ScheduledExecutorService.class),
|
mock(ScheduledExecutorService.class),
|
||||||
Duration.ofMinutes(1),
|
Duration.ofMinutes(1));
|
||||||
listener);
|
|
||||||
|
|
||||||
final String uuid = UUID.randomUUID().toString();
|
final String uuid = UUID.randomUUID().toString();
|
||||||
when(s3Client.headObject(HeadObjectRequest.builder().key(objectKey).bucket(bucket).build()))
|
when(s3Client.headObject(HeadObjectRequest.builder().key(objectKey).bucket(bucket).build()))
|
||||||
@ -87,7 +85,7 @@ class S3ObjectMonitorTest {
|
|||||||
when(s3Client.getObject(GetObjectRequest.builder().key(objectKey).bucket(bucket).build())).thenReturn(responseInputStream);
|
when(s3Client.getObject(GetObjectRequest.builder().key(objectKey).bucket(bucket).build())).thenReturn(responseInputStream);
|
||||||
|
|
||||||
objectMonitor.getObject();
|
objectMonitor.getObject();
|
||||||
objectMonitor.refresh();
|
objectMonitor.refresh(listener);
|
||||||
|
|
||||||
verify(listener, never()).accept(responseInputStream);
|
verify(listener, never()).accept(responseInputStream);
|
||||||
}
|
}
|
||||||
@ -115,8 +113,7 @@ class S3ObjectMonitorTest {
|
|||||||
objectKey,
|
objectKey,
|
||||||
maxObjectSize,
|
maxObjectSize,
|
||||||
mock(ScheduledExecutorService.class),
|
mock(ScheduledExecutorService.class),
|
||||||
Duration.ofMinutes(1),
|
Duration.ofMinutes(1));
|
||||||
listener);
|
|
||||||
|
|
||||||
final String uuid = UUID.randomUUID().toString();
|
final String uuid = UUID.randomUUID().toString();
|
||||||
when(s3Client.headObject(HeadObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(
|
when(s3Client.headObject(HeadObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(
|
||||||
@ -124,7 +121,7 @@ class S3ObjectMonitorTest {
|
|||||||
final ResponseInputStream<GetObjectResponse> ris = responseInputStreamFromString("a".repeat((int) maxObjectSize+1), uuid);
|
final ResponseInputStream<GetObjectResponse> ris = responseInputStreamFromString("a".repeat((int) maxObjectSize+1), uuid);
|
||||||
when(s3Client.getObject(GetObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(ris);
|
when(s3Client.getObject(GetObjectRequest.builder().bucket(bucket).key(objectKey).build())).thenReturn(ris);
|
||||||
|
|
||||||
objectMonitor.refresh();
|
objectMonitor.refresh(listener);
|
||||||
|
|
||||||
verify(listener, never()).accept(any());
|
verify(listener, never()).accept(any());
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer;
|
|||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||||
@ -27,9 +28,12 @@ import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex;
|
import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback {
|
public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback {
|
||||||
|
|
||||||
|
private static final String DEFAULT_LIBRARY_PATH = "target/lib";
|
||||||
|
|
||||||
public interface TableSchema {
|
public interface TableSchema {
|
||||||
String tableName();
|
String tableName();
|
||||||
String hashKeyName();
|
String hashKeyName();
|
||||||
@ -58,22 +62,28 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
|
|||||||
private DynamoDBProxyServer server;
|
private DynamoDBProxyServer server;
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
|
private final String libraryPath;
|
||||||
private final List<TableSchema> schemas;
|
private final List<TableSchema> schemas;
|
||||||
private DynamoDbClient dynamoDB2;
|
private DynamoDbClient dynamoDB2;
|
||||||
private DynamoDbAsyncClient dynamoAsyncDB2;
|
private DynamoDbAsyncClient dynamoAsyncDB2;
|
||||||
|
|
||||||
public DynamoDbExtension(TableSchema... schemas) {
|
public DynamoDbExtension(TableSchema... schemas) {
|
||||||
|
this(DEFAULT_LIBRARY_PATH, schemas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamoDbExtension(@Nullable final String libraryPath, TableSchema... schemas) {
|
||||||
|
this.libraryPath = Optional.ofNullable(libraryPath).orElse(DEFAULT_LIBRARY_PATH);
|
||||||
this.schemas = List.of(schemas);
|
this.schemas = List.of(schemas);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadLibrary() {
|
private void loadLibrary() {
|
||||||
// to avoid noise in the logs from “library already loaded” warnings, we make sure we only set it once
|
// to avoid noise in the logs from “library already loaded” warnings, we make sure we only set it once
|
||||||
if (libraryLoaded.get()) {
|
if (libraryLoaded.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (libraryLoaded.compareAndSet(false, true)) {
|
if (libraryLoaded.compareAndSet(false, true)) {
|
||||||
// if you see a library failed to load error, you need to run mvn test-compile at least once first
|
// if you see a library failed to load error, you need to run mvn test-compile at least once first
|
||||||
SQLite.setLibraryPath("target/lib");
|
SQLite.setLibraryPath(this.libraryPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.NoWaitDogstatsdConfiguration
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.StaticDynamicConfigurationManagerFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.LocalDynamoDbFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.LocalFaultTolerantRedisClusterFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.StubHCaptchaClientFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.StubPaymentsServiceClientsFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.StubPubSubPublisherFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.StubRegistrationServiceClientFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.StaticS3ObjectMonitorFactory
|
@ -0,0 +1 @@
|
|||||||
|
org.whispersystems.textsecuregcm.configuration.LocalSingletonRedisClientFactory
|
132
service/src/test/resources/config/test-secrets-bundle.yml
Normal file
132
service/src/test/resources/config/test-secrets-bundle.yml
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
aws.accessKeyId: accessKey
|
||||||
|
aws.secretAccessKey: secretAccess
|
||||||
|
|
||||||
|
stripe.apiKey: unset
|
||||||
|
stripe.idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
|
||||||
|
|
||||||
|
braintree.privateKey: unset
|
||||||
|
|
||||||
|
directoryV2.client.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth tokens for Signal users
|
||||||
|
directoryV2.client.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
|
svr2.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
||||||
|
svr2.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
|
svr3.userAuthenticationTokenSharedSecret: cbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR3 to generate auth tokens for Signal users
|
||||||
|
svr3.userIdTokenSharedSecret: dbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR3 to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
|
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
|
||||||
|
|
||||||
|
awsAttachments.accessKey: test
|
||||||
|
awsAttachments.accessSecret: test
|
||||||
|
|
||||||
|
# The below private key was key generated exclusively for testing purposes. Do not use it in any other context.
|
||||||
|
gcpAttachments.rsaSigningKey: |
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrfHLw9zr/8mTX
|
||||||
|
c0YMN3P9pNLtn+JCsNx/6sz/7FYoJjH8CKG4zNgcJLATLGxQikTjD6yNDlgkpByD
|
||||||
|
qOmgXgZvIBBJadbbl+plJbU4kKwTRwdrYiq/ICMkVZBk5jfqYqSxzdw80ytj5Tha
|
||||||
|
3M/3uqto7qELK91z/5cCC6pVsQXIrTqq4D41XyORKF2u4eeKOz3jiuXkdxRj4Vsb
|
||||||
|
MDwcS1WEi1ApoG50tDDn7e9mk3MAeE5L54ROHkd7FM471LRSU9ytpOzcH56tExLP
|
||||||
|
21nN5vXZoyJnNvbgd1KZeZajjH+XHJS/wiqNAPEX2yvrFID4ECQMIonXtYyNDkmY
|
||||||
|
YxggNaCnAgMBAAECggEAFLDJStr+8A7BArXSh9AmWz4zLPSTiim+EQ5gJFN8Tw/S
|
||||||
|
DBob2SjuEkc4RLf2waj33XrwqNGdlPOFdTqWJavylB8xl99V9dzYgn0QO9OeJMf3
|
||||||
|
Kd+y+f3Yqkj188FLPH52Z0ryqGwaL3gNWqPge9VhWncgUIa/C4CVKcFakJ2b7bW2
|
||||||
|
NIk2bSMCNW8rptQZ+tWV9k86OAxjIocLbkpPgigRk6T3MAunMGVf6iviNSnOyOlZ
|
||||||
|
qmAPkRVs2uyK3Hnl0lEavaBW3KRs0ChU0rkfXHvGmi7V6aZ4rnG6OdRQiOgk3NYf
|
||||||
|
qQYqhnRMmN4st2WN6CDDdpk5o2pHR625Wqx11t/50QKBgQDmf+fYWKdQa8r+TO4w
|
||||||
|
32JAiEdmFuA8fSEOaWyBik/NliJIPEApGMWLuZSmSzW80l4vt5zQ3LVgvRrxZv2y
|
||||||
|
7odLxUP9jpFGVg3NpCB27nES+psmo7X4kXIfzPWGvkOs2HLpp8elVEPeOn7gkng9
|
||||||
|
XXXmB9vja8g/Jo9ym9FkigB0LQKBgQC+dTFTPvvVYFQ1KmeL94EOEL21ZXkgwjnx
|
||||||
|
1BcnqK4p0M1NQ2xW1wwCljxlEQx5P6UY9HRWS6DecVpj6P7nRF2HWB+xsaO1aPZj
|
||||||
|
nMOETrUXGq8ksQml+0kI5f0A2w22wzpj3+kjiXSFBjxoWLAfKPHMKeUg/oYRfIVp
|
||||||
|
LeShMptIowKBgQC4H44U3ORyMlkKAGv4sEhs4i+elkFzMEU6nO4nIFQVFou2BiL+
|
||||||
|
cSJENe9PUx7PAYBpP5PNp7BfYU/na+zWhQGgfiiMn9jeRZlrHmMsfdXnYjaTjAyt
|
||||||
|
TYnLa07p3oxywsgwa2zoXUKFf1agj3/rDQBDyx1UMmHYSDYoR93hIPex1QKBgQCF
|
||||||
|
4y6sna89ff1Ubp3iKDjiIWSre00eeUtwtC8e4xakMLPSZ95mYcCApQqJ5eVF6zbt
|
||||||
|
hxOtgnbxSPBJIgbnnwi813dYXE+AfOwQdKiBfy8QseKDwazNsQvTpJIqItPOMgn/
|
||||||
|
Ie3r3Ho79XlLxWTyUr9ATgdUHXk0G7xRh0CdDU1aTwKBgC5kDNr/R2XIWZL0TMzz
|
||||||
|
EVL2BkL11YumIpEBm+Hkx6fm3uCgR/ywMqplGdZcD+D5r0fUsckbOd1z6fFGAJqe
|
||||||
|
QJ3/4qaA+dcWPwB5GiKa1WIs48GJMyPrFciindEwr3BaDhhB9cEdxpVY2e/KEeZL
|
||||||
|
TQkqmVUmgKKvCFTPWwCgeIOD
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
|
||||||
|
apn.teamId: team-id
|
||||||
|
apn.keyId: key-id
|
||||||
|
# The below private key was key generated exclusively for testing purposes. Do not use it in any other context.
|
||||||
|
apn.signingKey: |
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxIXnNiHH35DDbKHY
|
||||||
|
8kxoAYbukvMPVWN+kiIhZsFvqaahRANCAAQTWXjgagaLnTxcMJTUpO3rkhi8xjav
|
||||||
|
7NSEd5L+df4M7V9YxxDoYY+UHd8B/KmrWR29SVIRLncSULgfSnHnHvoH
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
|
||||||
|
# The below private key was key generated exclusively for testing purposes. Do not use it in any other context.
|
||||||
|
fcm.credentials: |
|
||||||
|
{ "type": "service_account", "client_id": "client_id", "client_email": "fake@example.com",
|
||||||
|
"private_key_id": "id",
|
||||||
|
"private_key": "-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrfHLw9zr/8mTX
|
||||||
|
c0YMN3P9pNLtn+JCsNx/6sz/7FYoJjH8CKG4zNgcJLATLGxQikTjD6yNDlgkpByD
|
||||||
|
qOmgXgZvIBBJadbbl+plJbU4kKwTRwdrYiq/ICMkVZBk5jfqYqSxzdw80ytj5Tha
|
||||||
|
3M/3uqto7qELK91z/5cCC6pVsQXIrTqq4D41XyORKF2u4eeKOz3jiuXkdxRj4Vsb
|
||||||
|
MDwcS1WEi1ApoG50tDDn7e9mk3MAeE5L54ROHkd7FM471LRSU9ytpOzcH56tExLP
|
||||||
|
21nN5vXZoyJnNvbgd1KZeZajjH+XHJS/wiqNAPEX2yvrFID4ECQMIonXtYyNDkmY
|
||||||
|
YxggNaCnAgMBAAECggEAFLDJStr+8A7BArXSh9AmWz4zLPSTiim+EQ5gJFN8Tw/S
|
||||||
|
DBob2SjuEkc4RLf2waj33XrwqNGdlPOFdTqWJavylB8xl99V9dzYgn0QO9OeJMf3
|
||||||
|
Kd+y+f3Yqkj188FLPH52Z0ryqGwaL3gNWqPge9VhWncgUIa/C4CVKcFakJ2b7bW2
|
||||||
|
NIk2bSMCNW8rptQZ+tWV9k86OAxjIocLbkpPgigRk6T3MAunMGVf6iviNSnOyOlZ
|
||||||
|
qmAPkRVs2uyK3Hnl0lEavaBW3KRs0ChU0rkfXHvGmi7V6aZ4rnG6OdRQiOgk3NYf
|
||||||
|
qQYqhnRMmN4st2WN6CDDdpk5o2pHR625Wqx11t/50QKBgQDmf+fYWKdQa8r+TO4w
|
||||||
|
32JAiEdmFuA8fSEOaWyBik/NliJIPEApGMWLuZSmSzW80l4vt5zQ3LVgvRrxZv2y
|
||||||
|
7odLxUP9jpFGVg3NpCB27nES+psmo7X4kXIfzPWGvkOs2HLpp8elVEPeOn7gkng9
|
||||||
|
XXXmB9vja8g/Jo9ym9FkigB0LQKBgQC+dTFTPvvVYFQ1KmeL94EOEL21ZXkgwjnx
|
||||||
|
1BcnqK4p0M1NQ2xW1wwCljxlEQx5P6UY9HRWS6DecVpj6P7nRF2HWB+xsaO1aPZj
|
||||||
|
nMOETrUXGq8ksQml+0kI5f0A2w22wzpj3+kjiXSFBjxoWLAfKPHMKeUg/oYRfIVp
|
||||||
|
LeShMptIowKBgQC4H44U3ORyMlkKAGv4sEhs4i+elkFzMEU6nO4nIFQVFou2BiL+
|
||||||
|
cSJENe9PUx7PAYBpP5PNp7BfYU/na+zWhQGgfiiMn9jeRZlrHmMsfdXnYjaTjAyt
|
||||||
|
TYnLa07p3oxywsgwa2zoXUKFf1agj3/rDQBDyx1UMmHYSDYoR93hIPex1QKBgQCF
|
||||||
|
4y6sna89ff1Ubp3iKDjiIWSre00eeUtwtC8e4xakMLPSZ95mYcCApQqJ5eVF6zbt
|
||||||
|
hxOtgnbxSPBJIgbnnwi813dYXE+AfOwQdKiBfy8QseKDwazNsQvTpJIqItPOMgn/
|
||||||
|
Ie3r3Ho79XlLxWTyUr9ATgdUHXk0G7xRh0CdDU1aTwKBgC5kDNr/R2XIWZL0TMzz
|
||||||
|
EVL2BkL11YumIpEBm+Hkx6fm3uCgR/ywMqplGdZcD+D5r0fUsckbOd1z6fFGAJqe
|
||||||
|
QJ3/4qaA+dcWPwB5GiKa1WIs48GJMyPrFciindEwr3BaDhhB9cEdxpVY2e/KEeZL
|
||||||
|
TQkqmVUmgKKvCFTPWwCgeIOD
|
||||||
|
-----END PRIVATE KEY-----" }
|
||||||
|
|
||||||
|
cdn.accessKey: test # AWS Access Key ID
|
||||||
|
cdn.accessSecret: test # AWS Access Secret
|
||||||
|
|
||||||
|
cdn3StorageManager.clientSecret: test
|
||||||
|
|
||||||
|
# The below private key was key generated exclusively for testing purposes. Do not use it in any other context.
|
||||||
|
# ca:
|
||||||
|
# Public key : BVDuaR1ZT/5M26nSvFN1XjN4qFqVfwynt03l/GyK2GtP
|
||||||
|
# Private key: 0Ie2/CIxfidwhS+uKSckb2YgtRR7UBLkecHNG2ARiW8=
|
||||||
|
unidentifiedDelivery.certificate: CikIp/SIvwESIQWXK5ikKQdvB7XpwPowV4djRj6Ni0I9MO12AVNpzQ9DDBJApovqX/gmIc+ukn4YeYtCsk9Q0EoDoXAZPD0D5vflcnncUs2fFLEQLlnSZ/ckXVChZByyuiNiegtl468+A+u5gw==
|
||||||
|
unidentifiedDelivery.privateKey: UKRdvrnxcy1hcILN7RHqUG40/mUd/ZnKkqhQToN4K1U=
|
||||||
|
|
||||||
|
storageService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
|
|
||||||
|
# The below secrets were generated exclusively for testing purposes. Do not use them in any other context.
|
||||||
|
zkConfig-libsignal-0.42.serverSecret: ALxy0qUiV9B3OgF1GzTgn6g4NSN22ww87p5xOlYkypQJIqxoTBGOPREr4ZXvOfQ/tFYQJxn3xoFJ5DKt+mDOJw8KzWephNaaXBSLcbWb/fPfSUjoXnPL+fcU26OpMIUrIVews/i0Eh4ongRWuLfFUpLyZ34wfgpKkWbIUBjbVT4M5e7GzRPVFOBGibICk9Q66o9l3K4PphKaoMQKlGPo4g85xsVH1HFN5J5u4/pdq80+3E7WvWN4c8NpDZQ7RqtECDpawSc/J4vim9tL7QR6hFYwzvA0cmCcM0NCPFpp69EHB1eRksDvPHiA+NuMqoFwXwIzSHzA86ALPxnyLKB7KgBG06DxfMmMav3dij6giTawgATRsoNFLQ/ol6H0TtYJCAp8oB0D4EV2q7hSue3Kxzh1Vc88/nmLuRR9G3EefC0+CMcxJFQwDMgjFvFBKx3o6m9gJLevYiKcm/NxXX9WtnEzqAh2DRr0G0fvk9NZF2Lw2kWgAX2qkPHZLJ3nKA90BgryBAJsk8Q78N5ghBhQzdgikURLC+mX1fbmMzkmGcwCYDnLpo8qjrIoBZzDAjI4Ty04MaJcLowJqNMmK29btza50WiZdsd4tuVqQAKlqJcERCsUewlZSkWpsDLrZkeUBY0rGCi51FW5WOUvdXwTHtTL2hlcBqP/E8cbBC+yce8AurjJ6Z9HVtM7tVk7a5xRAqwoFRSH7eq5BA60hDq2sgWIQaN1owunKZvsHFn0qzoGKuWAEO0PpbGbHtFDUjxzBkgUIN13yIbem9KPeZm9ZVrjkyDo3uZZsfFFUHnFeasKOt9WMJLLx1s7DttJ4Ns2o02q4e+aQ60oMeWsyMuzKgNMDHgDgfqHxbCi2rm20SgoHnuoph6XArmEOX6a1xLJVxgDtgfm1IbcyyqROXYxe9v2RvMUAnjbLI/fm0rXXhldjVX7Yoo7ZGAlVBuGOC28haRrd684Lcajdequ6Css1geOzOVVH/BGnJtf3RFSMXIv/YByG21/cgL9KFYqQHqZZeLNQpweILMnU0/iVK/fjLvrhyI2jNy1B0Ox62zE2o8EdVT/H1WgXa2NHC591aEqI5EXwribRKUM56v/O3IDBAxC5CLIQcUeDhWouaFqXjfxNza9rFC69smtUXl3sx8KG6Ze7VkXb372daAN+rMWIdq5kbRcetkXxuqTuYOz4NsDnEPnBNNO6hqOv6+dZAQK3wmhOah3NIL0kKNUHpuk+gidQkLehBvahNKpUfh9yBMkEcLNkR7D1r8fAcrA7u2sCKfRero8FOkCc9ChawShJXdcGtKv50d2/4vJp9B0ddUBYdFnbM/siX4VJghb+rSkhCkkXXS7QXFfbO5A4WJLkwthkNezNgqCBmEoda7UcOOaW9KQMFisRy74OZagCUAJCPC4UJw1/N4IJD7Dtw2cNtaxwFyuG6i7sdm9u3Xr9h0JcvKn5Z3BLxmR6WkhO/IraGw+9/ijFpEtB9VFhdQxfnnj4JRneZtQxC9nAY0liDuO41OhGYaWinVVRkljKe7GAmw726P1wYiyd2ajlkbI/7KB9VwxEicEijeyuR6UZDUFqOPk4q+fdkBRS2GuGKiy4ISgha6sVRkb5sLlAsRhmG1W0Anh8d2dHxmE8CzyoAXbschIeY2LJTUIORpvAaFrDI74dagBDNlIVnHw89PGYWnosia9YFMLpsYyjOccQKAGKJLZ8MdcgRw8W2Mw2AwZbQWUUU1VZxdWX6y878WJ6g4at5NJ2YpdZ/qh5zBTDbTDpGnTsv9Ioyw5+91h6qagDXecb1wekN0RZsI3KWXQeOZZ+uxTNRqqpWZhH9QtrEsPRdNyOlVJDmJ1H2Rl3o30Crt8j6EM6ZgR444GuDf/jATe9shz3Ljc0S5/OTEAJL54OfHq9jGgtXS+05AuhlxCC9zYpEBb30xnZD6zxXnz6IB//uVROuWc1BEFhkvn+JcJxpapbKH6PthuOzMVRkf+I3Xz3/bNjiQSlQkmAXlgB1YujgABYnJ6yJXQKP2mR4UJ3UYoGroYoafWycDa+vUYYqIMcK6tbIgvxFx8TmMoQ1MueOIzDt0Nyx3Uov5qVvcG8gyflIv4fbzlu7GTYE8Ov6sRGY8KzF5ywxvrq0VldgfoGF4AGdQ3RxB5EDdlHIvlOG8VRoD+7Ch/S1kemdyvD2co9wN/GfU58Q1DO7dSQTl/O86t1eZBp+8H4IIarAgLunN/vkV34LAYjk1DOfeNxrCnfHz9RWtT3Vy3FJKuaQUotgZRyu36BLSs/ozriMR1nkT2+Luknw/zDD496ZvyAwvtG18Tk5B5b3DSMSUq9vA4h8KKCFfkgTHNeOHFyogNkfwaeGEflkrckI4RTtGrIL+lmW+1LYRoDU8F2T4VzAnyqsfmfT5+g6nbzw7FRXGfEu/E94Xaacj0t90t+eqtBruaoJ9kqw4iJmQdrSonz4fo4yOAXukaTnzFnalXZdoFjNpSbMWOwFhalgT4fI8mUnZBoVOulWrFfs1exJuDy3fki99Z9kwT1gFnZ4SsO6fCRoTiINCEeXkhcCFscOuvZP9su7zhyXWhobzNsct/ejd+CHyDKoNPBjNId5hGwnIAP5F55x+n51UIPvCkotwIGsvbfExLMw/JgwCJCDSHNgZEmXO+xEVozKRbUDK8d0mR46M59k8qaPKkKAORatQPVXOyszQTLx8gnPf/HDDqthyyp7mfjfE09vv3CpREfzkGYZpMWv1aDG2AHpAdrOH3cVW2UQq9uPRtkiHZMZg9CQnapTCmq3YAvsKugvU2CnrnJlACFYO1Hr5RpjfIRMCkBrfHrdFQEwB4/u6opMApJThcbXzhbVEwIwOq/ZcleloJKnN4GdaZyLFphtApVSuMVYDNm0X6KNGSl1kFbgxs8gBBhLxXqqhdfoBnwOtOXHO+kFGY1WzGUJZviHD43glxBIWPRSjD0pPvuWX91IWv/GZuCIkwCa34p+P9v7iKRuoBGievGCJJ+5SUoBQyhhQdpG+gbw1Bs3KSPwkx0IulpQqkInUCyVgxRXZWI61AkDNr7ybYMhq0nPPY3V0xYzN9Wltf8c37m3IWVkzGR3a0JjoUUWtFaCY9fasiOQM1nZUMe/0UTV2ZKjD4US9c2PEHrPPlwVowar4PlUJU1KIAjgsCyo9DhZPyEgPjGYu+tGpMcUImLukfebXsgzFFhyLgLktlMwFHNn6JHchCURY58OOcMzDKK/6IuLU621a+d8gP8U22MozRrL+GNEYwiyF9XM4Hp+ovB3yv6VFkBp7ukAJDETnNy+nPPjt0ShUpp4hj+WDWo/fs6Oy/fs0wPdziBPrWQ7Dn0vDGsXVbTib8rd4UucpZdGaY1yktsG4MNHAMVv+IH1hYcg87bfsUJbfuHgvLB1l5Qz8j7/Ezi54RFQBGS3QkDQnCl/mMmrNCe5xe1soC+rsCRblHuJjujjK/CxgYEs2Lc9ZWPc3FyzGQbblH5hUX1MxP0V1DM/VxpI8EGVWTk6Q3W1yX16EiWkauVbHsyScbniotURYRstCUmA1Qnz9bsSBgjuCftVHZZ4lFmWogd976tG5uGQ+tvu6xCqH+EsGOQ843I/5w0xTPTJcFyQ9cuRoTPzFIeP2wa9AA
|
||||||
|
genericZkConfig.serverSecret: AIZmPk8ms6TWBTGFcFE1iEuu4kSpTRL1EAPA2ZVWm4EIIF/N811ZhILbCx8QSLBf90mNXhUtsfNF5PY5UdnJMgBGu3AtrVs5erRXf5hi6RxvCkl1QnYs/tcuUGNbkejyR9bPR2uJaK6CxGJS0RRUDWf8f2hQloe/+kWKilM1I/MHSV2+PcyCDJIigPi9RhbD2STXc6cHEpYXReg+1OYSEQk3K2M0qnUoVOAjbPuFXANEPU+106f37w/iF6MhyfWyDCb+oit29DFtoDS31cxheB3x1KVga2ErfnIyHpQrSWYHUdGPZLXc0xRmaa0VwDyyXzK0o3w4oS/F9+xqWYUWkwgsAm9e7dP4l0qVolnPQ67uNj7BFG4JQ0vXxD/JJQ+5B4bHyK+v5ndJpRMXDC9rJw8ehopvDCTXSoICqN7nvY8Fyqhf5zkM880Su2XiBa2paDTVuZgwq07zBeDrrPc2zQ8A4neV6++t95veOfpp94FymnHJ8ILaznKqzJluGDdtCA==
|
||||||
|
callingZkConfig.serverSecret: AIZmPk8ms6TWBTGFcFE1iEuu4kSpTRL1EAPA2ZVWm4EIIF/N811ZhILbCx8QSLBf90mNXhUtsfNF5PY5UdnJMgBGu3AtrVs5erRXf5hi6RxvCkl1QnYs/tcuUGNbkejyR9bPR2uJaK6CxGJS0RRUDWf8f2hQloe/+kWKilM1I/MHSV2+PcyCDJIigPi9RhbD2STXc6cHEpYXReg+1OYSEQk3K2M0qnUoVOAjbPuFXANEPU+106f37w/iF6MhyfWyDCb+oit29DFtoDS31cxheB3x1KVga2ErfnIyHpQrSWYHUdGPZLXc0xRmaa0VwDyyXzK0o3w4oS/F9+xqWYUWkwgsAm9e7dP4l0qVolnPQ67uNj7BFG4JQ0vXxD/JJQ+5B4bHyK+v5ndJpRMXDC9rJw8ehopvDCTXSoICqN7nvY8Fyqhf5zkM880Su2XiBa2paDTVuZgwq07zBeDrrPc2zQ8A4neV6++t95veOfpp94FymnHJ8ILaznKqzJluGDdtCA==
|
||||||
|
backupsZkConfig.serverSecret: AIZmPk8ms6TWBTGFcFE1iEuu4kSpTRL1EAPA2ZVWm4EIIF/N811ZhILbCx8QSLBf90mNXhUtsfNF5PY5UdnJMgBGu3AtrVs5erRXf5hi6RxvCkl1QnYs/tcuUGNbkejyR9bPR2uJaK6CxGJS0RRUDWf8f2hQloe/+kWKilM1I/MHSV2+PcyCDJIigPi9RhbD2STXc6cHEpYXReg+1OYSEQk3K2M0qnUoVOAjbPuFXANEPU+106f37w/iF6MhyfWyDCb+oit29DFtoDS31cxheB3x1KVga2ErfnIyHpQrSWYHUdGPZLXc0xRmaa0VwDyyXzK0o3w4oS/F9+xqWYUWkwgsAm9e7dP4l0qVolnPQ67uNj7BFG4JQ0vXxD/JJQ+5B4bHyK+v5ndJpRMXDC9rJw8ehopvDCTXSoICqN7nvY8Fyqhf5zkM880Su2XiBa2paDTVuZgwq07zBeDrrPc2zQ8A4neV6++t95veOfpp94FymnHJ8ILaznKqzJluGDdtCA==
|
||||||
|
paymentsService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # base64-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
|
||||||
|
paymentsService.fixerApiKey: unset
|
||||||
|
paymentsService.coinMarketCapApiKey: unset
|
||||||
|
|
||||||
|
artService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # base64-encoded 32-byte secret not shared with any external service, but used in ArtController
|
||||||
|
artService.userAuthenticationTokenUserIdSecret: AAAAAAAAAAA= # base64-encoded secret to obscure user phone numbers from Sticker Creator
|
||||||
|
|
||||||
|
currentReportingKey.secret: AAAAAAAAAAA=
|
||||||
|
currentReportingKey.salt: AAAAAAAAAAA=
|
||||||
|
|
||||||
|
turn.secret: AAAAAAAAAAA=
|
||||||
|
|
||||||
|
linkDevice.secret: AAAAAAAAAAA=
|
||||||
|
|
||||||
|
tlsKeyStore.password: unset
|
||||||
|
|
||||||
|
noiseTunnel.recognizedProxySecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
|
472
service/src/test/resources/config/test.yml
Normal file
472
service/src/test/resources/config/test.yml
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
logging:
|
||||||
|
level: INFO
|
||||||
|
appenders:
|
||||||
|
- type: console
|
||||||
|
threshold: ALL
|
||||||
|
timeZone: UTC
|
||||||
|
target: stdout
|
||||||
|
|
||||||
|
health:
|
||||||
|
delayedShutdownHandlerEnabled: false
|
||||||
|
|
||||||
|
awsCredentialsProvider:
|
||||||
|
type: static
|
||||||
|
accessKeyId: secret://aws.accessKeyId
|
||||||
|
secretAccessKey: secret://aws.secretAccessKey
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
reporters:
|
||||||
|
- type: signal-datadog
|
||||||
|
frequency: 10 seconds
|
||||||
|
tags:
|
||||||
|
- "env:test"
|
||||||
|
- "service:chat"
|
||||||
|
udpTransport:
|
||||||
|
statsdHost: localhost
|
||||||
|
port: 8125
|
||||||
|
excludesAttributes:
|
||||||
|
- m1_rate
|
||||||
|
- m5_rate
|
||||||
|
- m15_rate
|
||||||
|
- mean_rate
|
||||||
|
- stddev
|
||||||
|
useRegexFilters: true
|
||||||
|
excludes:
|
||||||
|
- ^.+\.total$
|
||||||
|
- ^.+\.request\.filtering$
|
||||||
|
- ^.+\.response\.filtering$
|
||||||
|
- ^executor\..+$
|
||||||
|
- ^lettuce\..+$
|
||||||
|
reportOnStop: true
|
||||||
|
|
||||||
|
tlsKeyStore:
|
||||||
|
password: secret://tlsKeyStore.password
|
||||||
|
|
||||||
|
stripe:
|
||||||
|
apiKey: secret://stripe.apiKey
|
||||||
|
idempotencyKeyGenerator: secret://stripe.idempotencyKeyGenerator
|
||||||
|
boostDescription: >
|
||||||
|
Example
|
||||||
|
supportedCurrenciesByPaymentMethod:
|
||||||
|
CARD:
|
||||||
|
- usd
|
||||||
|
- eur
|
||||||
|
SEPA_DEBIT:
|
||||||
|
- eur
|
||||||
|
|
||||||
|
braintree:
|
||||||
|
merchantId: unset
|
||||||
|
publicKey: unset
|
||||||
|
privateKey: secret://braintree.privateKey
|
||||||
|
environment: sandbox
|
||||||
|
graphqlUrl: unset
|
||||||
|
merchantAccounts:
|
||||||
|
# ISO 4217 currency code and its corresponding sub-merchant account
|
||||||
|
'xts': unset
|
||||||
|
supportedCurrenciesByPaymentMethod:
|
||||||
|
PAYPAL:
|
||||||
|
- usd
|
||||||
|
pubSubPublisher:
|
||||||
|
type: stub
|
||||||
|
|
||||||
|
dynamoDbClient:
|
||||||
|
type: local
|
||||||
|
|
||||||
|
dynamoDbTables:
|
||||||
|
accounts:
|
||||||
|
tableName: accounts_test
|
||||||
|
phoneNumberTableName: numbers_test
|
||||||
|
phoneNumberIdentifierTableName: pni_assignment_test
|
||||||
|
usernamesTableName: usernames_test
|
||||||
|
backups:
|
||||||
|
tableName: backups_test
|
||||||
|
clientReleases:
|
||||||
|
tableName: client_releases_test
|
||||||
|
deletedAccounts:
|
||||||
|
tableName: deleted_accounts_test
|
||||||
|
deletedAccountsLock:
|
||||||
|
tableName: deleted_accounts_lock_test
|
||||||
|
issuedReceipts:
|
||||||
|
tableName: issued_receipts_test
|
||||||
|
expiration: P30D # Duration of time until rows expire
|
||||||
|
generator: abcdefg12345678= # random base64-encoded binary sequence
|
||||||
|
ecKeys:
|
||||||
|
tableName: keys_test
|
||||||
|
ecSignedPreKeys:
|
||||||
|
tableName: repeated_use_signed_ec_pre_keys_test
|
||||||
|
pqKeys:
|
||||||
|
tableName: pq_keys_test
|
||||||
|
pqLastResortKeys:
|
||||||
|
tableName: repeated_use_signed_kem_pre_keys_test
|
||||||
|
messages:
|
||||||
|
tableName: messages_test
|
||||||
|
expiration: P30D # Duration of time until rows expire
|
||||||
|
onetimeDonations:
|
||||||
|
tableName: onetime_donations_test
|
||||||
|
expiration: P90D
|
||||||
|
phoneNumberIdentifiers:
|
||||||
|
tableName: pni_test
|
||||||
|
profiles:
|
||||||
|
tableName: profiles_test
|
||||||
|
pushChallenge:
|
||||||
|
tableName: push_challenge_test
|
||||||
|
redeemedReceipts:
|
||||||
|
tableName: redeemed_receipts_test
|
||||||
|
expiration: P30D # Duration of time until rows expire
|
||||||
|
registrationRecovery:
|
||||||
|
tableName: registration_recovery_passwords_test
|
||||||
|
expiration: P300D # Duration of time until rows expire
|
||||||
|
remoteConfig:
|
||||||
|
tableName: remote_config_test
|
||||||
|
reportMessage:
|
||||||
|
tableName: report_messages_test
|
||||||
|
subscriptions:
|
||||||
|
tableName: subscriptions_test
|
||||||
|
clientPublicKeys:
|
||||||
|
tableName: client_public_keys_test
|
||||||
|
verificationSessions:
|
||||||
|
tableName: verification_sessions_test
|
||||||
|
|
||||||
|
cacheCluster: # Redis server configuration for cache cluster
|
||||||
|
type: local
|
||||||
|
|
||||||
|
clientPresenceCluster: # Redis server configuration for client presence cluster
|
||||||
|
type: local
|
||||||
|
|
||||||
|
provisioning:
|
||||||
|
pubsub: # Redis server configuration for pubsub cluster
|
||||||
|
type: local
|
||||||
|
|
||||||
|
pushSchedulerCluster: # Redis server configuration for push scheduler cluster
|
||||||
|
type: local
|
||||||
|
|
||||||
|
rateLimitersCluster: # Redis server configuration for rate limiters cluster
|
||||||
|
type: local
|
||||||
|
|
||||||
|
directoryV2:
|
||||||
|
client: # Configuration for interfacing with Contact Discovery Service v2 cluster
|
||||||
|
userAuthenticationTokenSharedSecret: secret://directoryV2.client.userAuthenticationTokenSharedSecret
|
||||||
|
userIdTokenSharedSecret: secret://directoryV2.client.userIdTokenSharedSecret
|
||||||
|
|
||||||
|
svr2:
|
||||||
|
uri: svr2.example.com
|
||||||
|
userAuthenticationTokenSharedSecret: secret://svr2.userAuthenticationTokenSharedSecret
|
||||||
|
userIdTokenSharedSecret: secret://svr2.userIdTokenSharedSecret
|
||||||
|
svrCaCertificates:
|
||||||
|
# this is a randomly generated test certificate
|
||||||
|
- |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDazCCAlOgAwIBAgIUW5lcNWkuynRVc8Rq5pO6mHQBuZAwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAzMjUwMzE4MTNaFw0yOTAz
|
||||||
|
MjQwMzE4MTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||||
|
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQCfH4Um+fv2r4KudhD37/UXp8duRLTmp4XvpBTpDHpD
|
||||||
|
2HF8p2yThVKlJnMkP/9Ey1Rb0vhxO7DCltLdW8IYcxJuHoyMvyhGUEtxxkOZbrk8
|
||||||
|
ciUR9jTZ37x7vXRGj/RxcdlS6iD0MeF0D/LAkImt4T/kiKwDbENrVEnYWJmipCKP
|
||||||
|
ribxWky7HqxDCoYMQr0zatxB3A9mx5stH+H3kbw3CZcm+ugF9ZIKDEVHb0lf28gq
|
||||||
|
llmD120q/vs9YV3rzVL7sBGDqf6olkulvHQJKElZg2rdcHWFcngSlU2BjR04oyuH
|
||||||
|
c/SSiLSB3YB0tdFGta5uorXyV1y7RElPeBfOfvEjsG3TAgMBAAGjUzBRMB0GA1Ud
|
||||||
|
DgQWBBQX+xlgSWWbDjv0SrJ+h67xauJ80zAfBgNVHSMEGDAWgBQX+xlgSWWbDjv0
|
||||||
|
SrJ+h67xauJ80zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw
|
||||||
|
ZG2MCCjscn6h/QOoJU+IDfa68OqLq0I37gMnLMde4yEhAmm//miePIq4Uz9GRJ+h
|
||||||
|
rAmdEnspKgyQ93PjF7Xpk/JdJA4B1bIrsOl/cSwqx2sFhRt8Kt1DHGlGWXqOaHRP
|
||||||
|
UkZ86MyRL3sXly6WkxEYxZJeQaOzMy2XmQh7grzrlTBuSI+0xf7vsRRDipxr6LVQ
|
||||||
|
6qGWyGODLLc2JD1IXj/1HpRVT2LoGGlKMuyxACQAm4oak1vvJ9mGxgfd9AU+eo58
|
||||||
|
O/esB2Eaf+QqMPELdFSZQfG2jvp+3WQTZK8fDKHyLr076G3UetEMy867F6fzTSZd
|
||||||
|
9Kxq0DY7RCEpdHMCKcOL
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
svr3:
|
||||||
|
uri: svr3.example.com
|
||||||
|
userAuthenticationTokenSharedSecret: secret://svr3.userAuthenticationTokenSharedSecret
|
||||||
|
userIdTokenSharedSecret: secret://svr3.userIdTokenSharedSecret
|
||||||
|
svrCaCertificates:
|
||||||
|
- |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
AAAAAAAAAAAAAAAAAAAA
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
messageCache: # Redis server configuration for message store cache
|
||||||
|
persistDelayMinutes: 1
|
||||||
|
cluster:
|
||||||
|
type: local
|
||||||
|
|
||||||
|
metricsCluster:
|
||||||
|
type: local
|
||||||
|
|
||||||
|
awsAttachments: # AWS S3 configuration
|
||||||
|
bucket: aws-attachments
|
||||||
|
credentials:
|
||||||
|
accessKeyId: secret://awsAttachments.accessKey
|
||||||
|
secretAccessKey: secret://awsAttachments.accessSecret
|
||||||
|
region: us-west-2
|
||||||
|
|
||||||
|
gcpAttachments: # GCP Storage configuration
|
||||||
|
domain: example.com
|
||||||
|
email: user@example.cocm
|
||||||
|
maxSizeInBytes: 1024
|
||||||
|
pathPrefix:
|
||||||
|
rsaSigningKey: secret://gcpAttachments.rsaSigningKey
|
||||||
|
|
||||||
|
tus:
|
||||||
|
uploadUri: https://example.org/upload
|
||||||
|
userAuthenticationTokenSharedSecret: secret://tus.userAuthenticationTokenSharedSecret
|
||||||
|
|
||||||
|
apn: # Apple Push Notifications configuration
|
||||||
|
sandbox: true
|
||||||
|
bundleId: com.example.textsecuregcm
|
||||||
|
keyId: secret://apn.keyId
|
||||||
|
teamId: secret://apn.teamId
|
||||||
|
signingKey: secret://apn.signingKey
|
||||||
|
|
||||||
|
fcm: # FCM configuration
|
||||||
|
credentials: secret://fcm.credentials
|
||||||
|
|
||||||
|
cdn:
|
||||||
|
bucket: cdn # S3 Bucket name
|
||||||
|
credentials:
|
||||||
|
accessKeyId: secret://cdn.accessKey
|
||||||
|
secretAccessKey: secret://cdn.accessSecret
|
||||||
|
region: us-west-2 # AWS region
|
||||||
|
|
||||||
|
clientCdn:
|
||||||
|
attachmentUrls:
|
||||||
|
2: https://cdn2.example.com/attachments/
|
||||||
|
caCertificates:
|
||||||
|
- |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDazCCAlOgAwIBAgIUW5lcNWkuynRVc8Rq5pO6mHQBuZAwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAzMjUwMzE4MTNaFw0yOTAz
|
||||||
|
MjQwMzE4MTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||||
|
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQCfH4Um+fv2r4KudhD37/UXp8duRLTmp4XvpBTpDHpD
|
||||||
|
2HF8p2yThVKlJnMkP/9Ey1Rb0vhxO7DCltLdW8IYcxJuHoyMvyhGUEtxxkOZbrk8
|
||||||
|
ciUR9jTZ37x7vXRGj/RxcdlS6iD0MeF0D/LAkImt4T/kiKwDbENrVEnYWJmipCKP
|
||||||
|
ribxWky7HqxDCoYMQr0zatxB3A9mx5stH+H3kbw3CZcm+ugF9ZIKDEVHb0lf28gq
|
||||||
|
llmD120q/vs9YV3rzVL7sBGDqf6olkulvHQJKElZg2rdcHWFcngSlU2BjR04oyuH
|
||||||
|
c/SSiLSB3YB0tdFGta5uorXyV1y7RElPeBfOfvEjsG3TAgMBAAGjUzBRMB0GA1Ud
|
||||||
|
DgQWBBQX+xlgSWWbDjv0SrJ+h67xauJ80zAfBgNVHSMEGDAWgBQX+xlgSWWbDjv0
|
||||||
|
SrJ+h67xauJ80zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw
|
||||||
|
ZG2MCCjscn6h/QOoJU+IDfa68OqLq0I37gMnLMde4yEhAmm//miePIq4Uz9GRJ+h
|
||||||
|
rAmdEnspKgyQ93PjF7Xpk/JdJA4B1bIrsOl/cSwqx2sFhRt8Kt1DHGlGWXqOaHRP
|
||||||
|
UkZ86MyRL3sXly6WkxEYxZJeQaOzMy2XmQh7grzrlTBuSI+0xf7vsRRDipxr6LVQ
|
||||||
|
6qGWyGODLLc2JD1IXj/1HpRVT2LoGGlKMuyxACQAm4oak1vvJ9mGxgfd9AU+eo58
|
||||||
|
O/esB2Eaf+QqMPELdFSZQfG2jvp+3WQTZK8fDKHyLr076G3UetEMy867F6fzTSZd
|
||||||
|
9Kxq0DY7RCEpdHMCKcOL
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
cdn3StorageManager:
|
||||||
|
baseUri: https://storage-manager.example.com
|
||||||
|
clientId: example
|
||||||
|
clientSecret: secret://cdn3StorageManager.clientSecret
|
||||||
|
|
||||||
|
dogstatsd:
|
||||||
|
type: nowait
|
||||||
|
environment: dev
|
||||||
|
host: 127.0.0.1
|
||||||
|
|
||||||
|
unidentifiedDelivery:
|
||||||
|
certificate: secret://unidentifiedDelivery.certificate
|
||||||
|
privateKey: secret://unidentifiedDelivery.privateKey
|
||||||
|
expiresDays: 7
|
||||||
|
|
||||||
|
hCaptcha:
|
||||||
|
type: stub
|
||||||
|
|
||||||
|
shortCode:
|
||||||
|
baseUrl: https://example.com/shortcodes/
|
||||||
|
|
||||||
|
storageService:
|
||||||
|
uri: storage.example.com
|
||||||
|
userAuthenticationTokenSharedSecret: secret://storageService.userAuthenticationTokenSharedSecret
|
||||||
|
storageCaCertificates:
|
||||||
|
- |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDazCCAlOgAwIBAgIUW5lcNWkuynRVc8Rq5pO6mHQBuZAwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAzMjUwMzE4MTNaFw0yOTAz
|
||||||
|
MjQwMzE4MTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||||
|
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQCfH4Um+fv2r4KudhD37/UXp8duRLTmp4XvpBTpDHpD
|
||||||
|
2HF8p2yThVKlJnMkP/9Ey1Rb0vhxO7DCltLdW8IYcxJuHoyMvyhGUEtxxkOZbrk8
|
||||||
|
ciUR9jTZ37x7vXRGj/RxcdlS6iD0MeF0D/LAkImt4T/kiKwDbENrVEnYWJmipCKP
|
||||||
|
ribxWky7HqxDCoYMQr0zatxB3A9mx5stH+H3kbw3CZcm+ugF9ZIKDEVHb0lf28gq
|
||||||
|
llmD120q/vs9YV3rzVL7sBGDqf6olkulvHQJKElZg2rdcHWFcngSlU2BjR04oyuH
|
||||||
|
c/SSiLSB3YB0tdFGta5uorXyV1y7RElPeBfOfvEjsG3TAgMBAAGjUzBRMB0GA1Ud
|
||||||
|
DgQWBBQX+xlgSWWbDjv0SrJ+h67xauJ80zAfBgNVHSMEGDAWgBQX+xlgSWWbDjv0
|
||||||
|
SrJ+h67xauJ80zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw
|
||||||
|
ZG2MCCjscn6h/QOoJU+IDfa68OqLq0I37gMnLMde4yEhAmm//miePIq4Uz9GRJ+h
|
||||||
|
rAmdEnspKgyQ93PjF7Xpk/JdJA4B1bIrsOl/cSwqx2sFhRt8Kt1DHGlGWXqOaHRP
|
||||||
|
UkZ86MyRL3sXly6WkxEYxZJeQaOzMy2XmQh7grzrlTBuSI+0xf7vsRRDipxr6LVQ
|
||||||
|
6qGWyGODLLc2JD1IXj/1HpRVT2LoGGlKMuyxACQAm4oak1vvJ9mGxgfd9AU+eo58
|
||||||
|
O/esB2Eaf+QqMPELdFSZQfG2jvp+3WQTZK8fDKHyLr076G3UetEMy867F6fzTSZd
|
||||||
|
9Kxq0DY7RCEpdHMCKcOL
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
zkConfig:
|
||||||
|
serverPublic: AAp8oB0D4EV2q7hSue3Kxzh1Vc88/nmLuRR9G3EefC0+CMcxJFQwDMgjFvFBKx3o6m9gJLevYiKcm/NxXX9WtnFMDHgDgfqHxbCi2rm20SgoHnuoph6XArmEOX6a1xLJVxgDtgfm1IbcyyqROXYxe9v2RvMUAnjbLI/fm0rXXhldjszlVR/wRpybX90RUjFyL/2Achttf3IC/ShWKkB6mWXwuFCcNfzeCCQ+w7cNnDbWscBcrhuou7HZvbt16/YdCXLyp+WdwS8ZkelpITvyK2hsPvf4oxaRLQfVRYXUMX55xpapbKH6PthuOzMVRkf+I3Xz3/bNjiQSlQkmAXlgB1YujgABYnJ6yJXQKP2mR4UJ3UYoGroYoafWycDa+vUYYozaUmzFjsBYWpYE+HyPJlJ2QaFTrpVqxX7NXsSbg8t35IvfWfZME9YBZ2eErDunwkaE4iDQhHl5IXAhbHDrr2QaJ68YIkn7lJSgFDKGFB2kb6BvDUGzcpI/CTHQi6WlCqQidQLJWDFFdlYjrUCQM2vvJtgyGrSc89jdXTFjM31aqmtcPWgWL0qv+RmK/BC392Nsu8WoSJcAE4yhccQuRSemtolgwewnjasoOFBNOPh4+pX55SwhyTVgtwl+NTNVNFydxGp9Me8ogRWElzwA9BFtNAgQtlfgIyZRTetFqLkYmIBDxwMcpizDKES5lPhV2uJJuzcMq/06mVQz2OrXgglWk01uN8U59pfNFpTZhcGQv+MHjwEAudq5eLpt3aFrdxJ7D26Fwl5j215SJ0yZo7vmSEML1vf7FaGh0IL57bRpCvdebB5WapSChUX+PPvCXohVjGrERFvQpeET6pydGGlEKYLWuWa3zFGmPvJJYZ/QfcmIP9zyhqzQT/7a7RIqFA==
|
||||||
|
serverSecret: secret://zkConfig-libsignal-0.42.serverSecret
|
||||||
|
|
||||||
|
callingZkConfig:
|
||||||
|
serverSecret: secret://callingZkConfig.serverSecret
|
||||||
|
|
||||||
|
backupsZkConfig:
|
||||||
|
serverSecret: secret://backupsZkConfig.serverSecret
|
||||||
|
|
||||||
|
appConfig:
|
||||||
|
type: static
|
||||||
|
application: test
|
||||||
|
environment: test
|
||||||
|
configuration: test
|
||||||
|
staticConfig: |
|
||||||
|
captcha:
|
||||||
|
scoreFloor: 1.0
|
||||||
|
|
||||||
|
remoteConfig:
|
||||||
|
globalConfig: # keys and values that are given to clients on GET /v1/config
|
||||||
|
EXAMPLE_KEY: VALUE
|
||||||
|
|
||||||
|
paymentsService:
|
||||||
|
userAuthenticationTokenSharedSecret: secret://paymentsService.userAuthenticationTokenSharedSecret
|
||||||
|
paymentCurrencies:
|
||||||
|
# list of symbols for supported currencies
|
||||||
|
- MOB
|
||||||
|
externalClients:
|
||||||
|
type: stub
|
||||||
|
|
||||||
|
artService:
|
||||||
|
userAuthenticationTokenSharedSecret: secret://artService.userAuthenticationTokenSharedSecret
|
||||||
|
userAuthenticationTokenUserIdSecret: secret://artService.userAuthenticationTokenUserIdSecret
|
||||||
|
|
||||||
|
badges:
|
||||||
|
badges:
|
||||||
|
- id: TEST
|
||||||
|
category: other
|
||||||
|
sprites: # exactly 6
|
||||||
|
- sprite-1.png
|
||||||
|
- sprite-2.png
|
||||||
|
- sprite-3.png
|
||||||
|
- sprite-4.png
|
||||||
|
- sprite-5.png
|
||||||
|
- sprite-6.png
|
||||||
|
svg: example.svg
|
||||||
|
svgs:
|
||||||
|
- light: example-light.svg
|
||||||
|
dark: example-dark.svg
|
||||||
|
badgeIdsEnabledForAll:
|
||||||
|
- TEST
|
||||||
|
receiptLevels:
|
||||||
|
'1': TEST
|
||||||
|
|
||||||
|
subscription: # configuration for Stripe subscriptions
|
||||||
|
badgeExpiration: P30D
|
||||||
|
badgeGracePeriod: P15D
|
||||||
|
levels:
|
||||||
|
500:
|
||||||
|
badge: EXAMPLE
|
||||||
|
prices:
|
||||||
|
# list of ISO 4217 currency codes and amounts for the given badge level
|
||||||
|
xts:
|
||||||
|
amount: '10'
|
||||||
|
processorIds:
|
||||||
|
STRIPE: price_example # stripe Price ID
|
||||||
|
BRAINTREE: plan_example # braintree Plan ID
|
||||||
|
|
||||||
|
oneTimeDonations:
|
||||||
|
sepaMaximumEuros: '10000'
|
||||||
|
boost:
|
||||||
|
level: 1
|
||||||
|
expiration: P90D
|
||||||
|
badge: EXAMPLE
|
||||||
|
gift:
|
||||||
|
level: 10
|
||||||
|
expiration: P90D
|
||||||
|
badge: EXAMPLE
|
||||||
|
currencies:
|
||||||
|
# ISO 4217 currency codes and amounts in those currencies
|
||||||
|
xts:
|
||||||
|
minimum: '0.5'
|
||||||
|
gift: '2'
|
||||||
|
boosts:
|
||||||
|
- '1'
|
||||||
|
- '2'
|
||||||
|
- '4'
|
||||||
|
- '8'
|
||||||
|
- '20'
|
||||||
|
- '40'
|
||||||
|
|
||||||
|
registrationService:
|
||||||
|
type: stub
|
||||||
|
registrationCaCertificate: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDazCCAlOgAwIBAgIUW5lcNWkuynRVc8Rq5pO6mHQBuZAwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAzMjUwMzE4MTNaFw0yOTAz
|
||||||
|
MjQwMzE4MTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||||
|
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQCfH4Um+fv2r4KudhD37/UXp8duRLTmp4XvpBTpDHpD
|
||||||
|
2HF8p2yThVKlJnMkP/9Ey1Rb0vhxO7DCltLdW8IYcxJuHoyMvyhGUEtxxkOZbrk8
|
||||||
|
ciUR9jTZ37x7vXRGj/RxcdlS6iD0MeF0D/LAkImt4T/kiKwDbENrVEnYWJmipCKP
|
||||||
|
ribxWky7HqxDCoYMQr0zatxB3A9mx5stH+H3kbw3CZcm+ugF9ZIKDEVHb0lf28gq
|
||||||
|
llmD120q/vs9YV3rzVL7sBGDqf6olkulvHQJKElZg2rdcHWFcngSlU2BjR04oyuH
|
||||||
|
c/SSiLSB3YB0tdFGta5uorXyV1y7RElPeBfOfvEjsG3TAgMBAAGjUzBRMB0GA1Ud
|
||||||
|
DgQWBBQX+xlgSWWbDjv0SrJ+h67xauJ80zAfBgNVHSMEGDAWgBQX+xlgSWWbDjv0
|
||||||
|
SrJ+h67xauJ80zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw
|
||||||
|
ZG2MCCjscn6h/QOoJU+IDfa68OqLq0I37gMnLMde4yEhAmm//miePIq4Uz9GRJ+h
|
||||||
|
rAmdEnspKgyQ93PjF7Xpk/JdJA4B1bIrsOl/cSwqx2sFhRt8Kt1DHGlGWXqOaHRP
|
||||||
|
UkZ86MyRL3sXly6WkxEYxZJeQaOzMy2XmQh7grzrlTBuSI+0xf7vsRRDipxr6LVQ
|
||||||
|
6qGWyGODLLc2JD1IXj/1HpRVT2LoGGlKMuyxACQAm4oak1vvJ9mGxgfd9AU+eo58
|
||||||
|
O/esB2Eaf+QqMPELdFSZQfG2jvp+3WQTZK8fDKHyLr076G3UetEMy867F6fzTSZd
|
||||||
|
9Kxq0DY7RCEpdHMCKcOL
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
turn:
|
||||||
|
secret: secret://turn.secret
|
||||||
|
|
||||||
|
linkDevice:
|
||||||
|
secret: secret://linkDevice.secret
|
||||||
|
|
||||||
|
maxmindCityDatabase:
|
||||||
|
type: static
|
||||||
|
|
||||||
|
callingTurnDnsRecords:
|
||||||
|
type: static
|
||||||
|
|
||||||
|
callingTurnPerformanceTable:
|
||||||
|
type: static
|
||||||
|
|
||||||
|
callingTurnManualTable:
|
||||||
|
type: static
|
||||||
|
|
||||||
|
noiseTunnel:
|
||||||
|
port: 8443
|
||||||
|
recognizedProxySecret: secret://noiseTunnel.recognizedProxySecret
|
||||||
|
|
||||||
|
externalRequestFilter:
|
||||||
|
grpcMethods:
|
||||||
|
- com.example.grpc.ExampleService/exampleMethod
|
||||||
|
paths:
|
||||||
|
- /example
|
||||||
|
permittedInternalRanges:
|
||||||
|
- 127.0.0.0/8
|
Loading…
Reference in New Issue
Block a user