mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 17:08:27 +01:00
Moving secret values out of the main configuration file
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
package org.whispersystems.textsecuregcm;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.amazonaws.ClientConfiguration;
|
||||
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
|
||||
@@ -81,6 +82,8 @@ import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.captcha.RecaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretStore;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretsModule;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountControllerV2;
|
||||
import org.whispersystems.textsecuregcm.controllers.ArtController;
|
||||
@@ -236,8 +239,24 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WhisperServerService.class);
|
||||
|
||||
public static final String SECRETS_BUNDLE_FILE_NAME_PROPERTY = "secrets.bundle.filename";
|
||||
|
||||
private static final software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER =
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create();
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(Bootstrap<WhisperServerConfiguration> bootstrap) {
|
||||
public void initialize(final Bootstrap<WhisperServerConfiguration> bootstrap) {
|
||||
// `SecretStore` needs to be initialized before Dropwizard reads the main application config file.
|
||||
final String secretsBundleFileName = requireNonNull(
|
||||
System.getProperty(SECRETS_BUNDLE_FILE_NAME_PROPERTY),
|
||||
"Application requires property [%s] to be provided".formatted(SECRETS_BUNDLE_FILE_NAME_PROPERTY));
|
||||
final SecretStore secretStore = SecretStore.fromYamlFileSecretsBundle(secretsBundleFileName);
|
||||
SecretsModule.INSTANCE.setSecretStore(secretStore);
|
||||
|
||||
// Initializing SystemMapper here because parsing of the main application config happens before `run()` method is called.
|
||||
SystemMapper.configureMapper(bootstrap.getObjectMapper());
|
||||
|
||||
bootstrap.addCommand(new DeleteUserCommand());
|
||||
bootstrap.addCommand(new CertificateCommand());
|
||||
bootstrap.addCommand(new ZkParamsCommand());
|
||||
@@ -289,8 +308,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
|
||||
environment.lifecycle().manage(new MicrometerRegistryManager(Metrics.globalRegistry));
|
||||
|
||||
SystemMapper.configureMapper(environment.getObjectMapper());
|
||||
|
||||
HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup =
|
||||
new HeaderControlledResourceBundleLookup();
|
||||
ConfiguredProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter(
|
||||
@@ -300,11 +317,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
||||
config.getDynamoDbClientConfiguration(),
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||
AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER);
|
||||
|
||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
||||
config.getDynamoDbClientConfiguration(),
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||
AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER);
|
||||
|
||||
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
|
||||
.withRegion(config.getDynamoDbClientConfiguration().getRegion())
|
||||
@@ -446,11 +463,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
config.getAdminEventLoggingConfiguration().projectId(),
|
||||
config.getAdminEventLoggingConfiguration().logName());
|
||||
|
||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey(), subscriptionProcessorExecutor,
|
||||
config.getStripe().idempotencyKeyGenerator(), config.getStripe().boostDescription(), config.getStripe()
|
||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey().value(), subscriptionProcessorExecutor,
|
||||
config.getStripe().idempotencyKeyGenerator().value(), config.getStripe().boostDescription(), config.getStripe()
|
||||
.supportedCurrencies());
|
||||
BraintreeManager braintreeManager = new BraintreeManager(config.getBraintree().merchantId(),
|
||||
config.getBraintree().publicKey(), config.getBraintree().privateKey(), config.getBraintree().environment(),
|
||||
config.getBraintree().publicKey(), config.getBraintree().privateKey().value(), config.getBraintree().environment(),
|
||||
config.getBraintree().supportedCurrencies(), config.getBraintree().merchantAccounts(),
|
||||
config.getBraintree().graphqlUrl(), config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor);
|
||||
|
||||
@@ -506,9 +523,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
deletedAccountsManager, keys, messagesManager, profilesManager,
|
||||
pendingAccountsManager, secureStorageClient, secureBackupClient, secureValueRecovery2Client, clientPresenceManager,
|
||||
experimentEnrollmentManager, registrationRecoveryPasswordsManager, clock);
|
||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
|
||||
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials());
|
||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
|
||||
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials().value());
|
||||
ApnPushNotificationScheduler apnPushNotificationScheduler = new ApnPushNotificationScheduler(pushSchedulerCluster,
|
||||
apnSender, accountsManager);
|
||||
PushNotificationManager pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender,
|
||||
@@ -553,7 +570,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
|
||||
dynamicConfigurationManager);
|
||||
HttpClient hcaptchaHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||
HCaptchaClient hCaptchaClient = new HCaptchaClient(config.getHCaptchaConfiguration().apiKey(), hcaptchaHttpClient, dynamicConfigurationManager);
|
||||
HCaptchaClient hCaptchaClient = new HCaptchaClient(config.getHCaptchaConfiguration().apiKey().value(), hcaptchaHttpClient, dynamicConfigurationManager);
|
||||
CaptchaChecker captchaChecker = new CaptchaChecker(List.of(recaptchaClient, hCaptchaClient));
|
||||
|
||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(pushNotificationManager, pushChallengeDynamoDb);
|
||||
@@ -589,10 +606,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
);
|
||||
|
||||
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().getFixerApiKey());
|
||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().getCoinMarketCapApiKey(), config.getPaymentsServiceConfiguration().getCoinMarketCapCurrencyIds());
|
||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().fixerApiKey().value());
|
||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().coinMarketCapApiKey().value(), config.getPaymentsServiceConfiguration().coinMarketCapCurrencyIds());
|
||||
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, coinMarketCapClient,
|
||||
cacheCluster, config.getPaymentsServiceConfiguration().getPaymentCurrencies(), Clock.systemUTC());
|
||||
cacheCluster, config.getPaymentsServiceConfiguration().paymentCurrencies(), Clock.systemUTC());
|
||||
|
||||
environment.lifecycle().manage(apnSender);
|
||||
environment.lifecycle().manage(apnPushNotificationScheduler);
|
||||
@@ -610,19 +627,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
|
||||
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
|
||||
.create(AwsBasicCredentials.create(
|
||||
config.getCdnConfiguration().getAccessKey(),
|
||||
config.getCdnConfiguration().getAccessSecret()));
|
||||
config.getCdnConfiguration().accessKey().value(),
|
||||
config.getCdnConfiguration().accessSecret().value()));
|
||||
S3Client cdnS3Client = S3Client.builder()
|
||||
.credentialsProvider(cdnCredentialsProvider)
|
||||
.region(Region.of(config.getCdnConfiguration().getRegion()))
|
||||
.region(Region.of(config.getCdnConfiguration().region()))
|
||||
.build();
|
||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().getRegion(),
|
||||
config.getCdnConfiguration().getBucket(), config.getCdnConfiguration().getAccessKey());
|
||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().getAccessSecret(),
|
||||
config.getCdnConfiguration().getRegion());
|
||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().region(),
|
||||
config.getCdnConfiguration().bucket(), config.getCdnConfiguration().accessKey().value());
|
||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().accessSecret().value(),
|
||||
config.getCdnConfiguration().region());
|
||||
|
||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().getServerSecret());
|
||||
GenericServerSecretParams genericZkSecretParams = new GenericServerSecretParams(config.getGenericZkConfig().serverSecret());
|
||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().serverSecret().value());
|
||||
GenericServerSecretParams genericZkSecretParams = new GenericServerSecretParams(config.getGenericZkConfig().serverSecret().value());
|
||||
ServerZkProfileOperations zkProfileOperations = new ServerZkProfileOperations(zkSecretParams);
|
||||
ServerZkAuthOperations zkAuthOperations = new ServerZkAuthOperations(zkSecretParams);
|
||||
ServerZkReceiptOperations zkReceiptOperations = new ServerZkReceiptOperations(zkSecretParams);
|
||||
@@ -720,10 +737,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
|
||||
registrationLockVerificationManager, rateLimiters),
|
||||
new ArtController(rateLimiters, artCredentialsGenerator),
|
||||
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getRegion(), config.getAwsAttachmentsConfiguration().getBucket()),
|
||||
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()),
|
||||
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().accessKey().value(), config.getAwsAttachmentsConfiguration().accessSecret().value(), config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
|
||||
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().domain(), config.getGcpAttachmentsConfiguration().email(), config.getGcpAttachmentsConfiguration().maxSizeInBytes(), config.getGcpAttachmentsConfiguration().pathPrefix(), config.getGcpAttachmentsConfiguration().rsaSigningKey().value()),
|
||||
new CallLinkController(rateLimiters, genericZkSecretParams),
|
||||
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations, genericZkSecretParams, clock),
|
||||
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().certificate().value(), config.getDeliveryCertificate().ecPrivateKey(), config.getDeliveryCertificate().expiresDays()), zkAuthOperations, genericZkSecretParams, clock),
|
||||
new ChallengeController(rateLimitChallengeManager),
|
||||
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keys, rateLimiters, config.getMaxDevices()),
|
||||
new DirectoryV2Controller(directoryV2CredentialsGenerator),
|
||||
@@ -735,19 +752,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
||||
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
|
||||
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
|
||||
config.getCdnConfiguration().getBucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
||||
config.getCdnConfiguration().bucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
||||
new ProvisioningController(rateLimiters, provisioningManager),
|
||||
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
|
||||
rateLimiters),
|
||||
new RemoteConfigController(remoteConfigsManager, adminEventLogger,
|
||||
config.getRemoteConfigConfiguration().getAuthorizedTokens(),
|
||||
config.getRemoteConfigConfiguration().getGlobalConfig()),
|
||||
config.getRemoteConfigConfiguration().authorizedTokens().value(),
|
||||
config.getRemoteConfigConfiguration().globalConfig()),
|
||||
new SecureBackupController(backupCredentialsGenerator, accountsManager),
|
||||
new SecureStorageController(storageCredentialsGenerator),
|
||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager, config.getSvr2Configuration()),
|
||||
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
||||
config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(),
|
||||
config.getCdnConfiguration().getBucket()),
|
||||
new StickerController(rateLimiters, config.getCdnConfiguration().accessKey().value(),
|
||||
config.getCdnConfiguration().accessSecret().value(), config.getCdnConfiguration().region(),
|
||||
config.getCdnConfiguration().bucket()),
|
||||
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
||||
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
||||
accountsManager, clock)
|
||||
|
||||
@@ -10,6 +10,7 @@ import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256ToHexString
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256TruncatedToHexString;
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmacHexStringsEqual;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
@@ -17,6 +18,7 @@ import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class ExternalServiceCredentialsGenerator {
|
||||
|
||||
@@ -40,6 +42,12 @@ public class ExternalServiceCredentialsGenerator {
|
||||
|
||||
private final int derivedUsernameTruncateLength;
|
||||
|
||||
|
||||
public static ExternalServiceCredentialsGenerator.Builder builder(final SecretBytes key) {
|
||||
return builder(key.value());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static ExternalServiceCredentialsGenerator.Builder builder(final byte[] key) {
|
||||
return new Builder(key);
|
||||
}
|
||||
@@ -240,6 +248,10 @@ public class ExternalServiceCredentialsGenerator {
|
||||
this.key = requireNonNull(key);
|
||||
}
|
||||
|
||||
public Builder withUserDerivationKey(final SecretBytes userDerivationKey) {
|
||||
return withUserDerivationKey(userDerivationKey.value());
|
||||
}
|
||||
|
||||
public Builder withUserDerivationKey(final byte[] userDerivationKey) {
|
||||
Validate.isTrue(requireNonNull(userDerivationKey).length > 0, "userDerivationKey must not be empty");
|
||||
this.userDerivationKey = userDerivationKey;
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public record AdminEventLoggingConfiguration(
|
||||
@NotEmpty String credentials,
|
||||
@NotBlank String credentials,
|
||||
@NotEmpty String projectId,
|
||||
@NotEmpty String logName) {
|
||||
}
|
||||
|
||||
@@ -1,51 +1,17 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
|
||||
public class ApnConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String teamId;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String keyId;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String signingKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bundleId;
|
||||
|
||||
@JsonProperty
|
||||
private boolean sandbox = false;
|
||||
|
||||
public String getTeamId() {
|
||||
return teamId;
|
||||
}
|
||||
|
||||
public String getKeyId() {
|
||||
return keyId;
|
||||
}
|
||||
|
||||
public String getSigningKey() {
|
||||
return signingKey;
|
||||
}
|
||||
|
||||
public String getBundleId() {
|
||||
return bundleId;
|
||||
}
|
||||
|
||||
public boolean isSandboxEnabled() {
|
||||
return sandbox;
|
||||
}
|
||||
public record ApnConfiguration(@NotBlank String teamId,
|
||||
@NotBlank String keyId,
|
||||
@NotNull SecretString signingKey,
|
||||
@NotBlank String bundleId,
|
||||
boolean sandbox) {
|
||||
}
|
||||
|
||||
@@ -5,35 +5,17 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import static org.apache.commons.lang3.ObjectUtils.firstNonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HexFormat;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public class ArtServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenUserIdSecret;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Duration tokenExpiration = Duration.ofDays(1);
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
|
||||
public byte[] getUserAuthenticationTokenUserIdSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenUserIdSecret);
|
||||
}
|
||||
|
||||
public Duration getTokenExpiration() {
|
||||
return tokenExpiration;
|
||||
public record ArtServiceConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotNull SecretBytes userAuthenticationTokenUserIdSecret,
|
||||
@NotNull Duration tokenExpiration) {
|
||||
public ArtServiceConfiguration {
|
||||
tokenExpiration = firstNonNull(tokenExpiration, Duration.ofDays(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,15 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class AwsAttachmentsConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bucket;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String region;
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public String getAccessSecret() {
|
||||
return accessSecret;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
public record AwsAttachmentsConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
/**
|
||||
* @param merchantId the Braintree merchant ID
|
||||
@@ -24,7 +25,7 @@ import javax.validation.constraints.NotNull;
|
||||
*/
|
||||
public record BraintreeConfiguration(@NotBlank String merchantId,
|
||||
@NotBlank String publicKey,
|
||||
@NotBlank String privateKey,
|
||||
@NotNull SecretString privateKey,
|
||||
@NotBlank String environment,
|
||||
@NotEmpty Set<@NotBlank String> supportedCurrencies,
|
||||
@NotBlank String graphqlUrl,
|
||||
|
||||
@@ -1,44 +1,16 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public class CdnConfiguration {
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bucket;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String region;
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public String getAccessSecret() {
|
||||
return accessSecret;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record CdnConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
@@ -7,16 +7,17 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.micrometer.datadog.DatadogConfig;
|
||||
import java.time.Duration;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.Duration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class DatadogConfiguration implements DatadogConfig {
|
||||
|
||||
@JsonProperty
|
||||
@NotBlank
|
||||
private String apiKey;
|
||||
@NotNull
|
||||
private SecretString apiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@@ -32,7 +33,7 @@ public class DatadogConfiguration implements DatadogConfig {
|
||||
|
||||
@Override
|
||||
public String apiKey() {
|
||||
return apiKey;
|
||||
return apiKey.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2013-2023 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public record DirectoryV2ClientConfiguration(@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret) {
|
||||
public record DirectoryV2ClientConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userIdTokenSharedSecret) {
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record FcmConfiguration(@NotBlank String credentials) {
|
||||
public record FcmConfiguration(@NotNull SecretString credentials) {
|
||||
}
|
||||
|
||||
@@ -1,57 +1,22 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.dropwizard.util.Strings;
|
||||
import io.dropwizard.validation.ValidationMethod;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public class GcpAttachmentsConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String domain;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String email;
|
||||
|
||||
@JsonProperty
|
||||
@Min(1)
|
||||
private int maxSizeInBytes;
|
||||
|
||||
@JsonProperty
|
||||
private String pathPrefix;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String rsaSigningKey;
|
||||
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public int getMaxSizeInBytes() {
|
||||
return maxSizeInBytes;
|
||||
}
|
||||
|
||||
public String getPathPrefix() {
|
||||
return pathPrefix;
|
||||
}
|
||||
|
||||
public String getRsaSigningKey() {
|
||||
return rsaSigningKey;
|
||||
}
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record GcpAttachmentsConfiguration(@NotBlank String domain,
|
||||
@NotBlank String email,
|
||||
@Min(1) int maxSizeInBytes,
|
||||
String pathPrefix,
|
||||
@NotNull SecretString rsaSigningKey) {
|
||||
@SuppressWarnings("unused")
|
||||
@ValidationMethod(message = "pathPrefix must be empty or start with /")
|
||||
public boolean isPathPrefixValid() {
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
/*
|
||||
* Copyright 2023 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.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public record GenericZkConfig (
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
byte[] serverSecret
|
||||
) {}
|
||||
public record GenericZkConfig(@NotNull SecretBytes serverSecret) {
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2021-2022 Signal Messenger, LLC
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record HCaptchaConfiguration(@NotBlank String apiKey) {
|
||||
public record HCaptchaConfiguration(@NotNull SecretString apiKey) {
|
||||
}
|
||||
|
||||
@@ -1,56 +1,21 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
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.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class PaymentsServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotBlank
|
||||
@JsonProperty
|
||||
private String coinMarketCapApiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private Map<@NotBlank String, Integer> coinMarketCapCurrencyIds;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String fixerApiKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private List<String> paymentCurrencies;
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
|
||||
public String getCoinMarketCapApiKey() {
|
||||
return coinMarketCapApiKey;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getCoinMarketCapCurrencyIds() {
|
||||
return coinMarketCapCurrencyIds;
|
||||
}
|
||||
|
||||
public String getFixerApiKey() {
|
||||
return fixerApiKey;
|
||||
}
|
||||
|
||||
public List<String> getPaymentCurrencies() {
|
||||
return paymentCurrencies;
|
||||
}
|
||||
public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotNull SecretString coinMarketCapApiKey,
|
||||
@NotNull SecretString fixerApiKey,
|
||||
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds,
|
||||
@NotEmpty List<String> paymentCurrencies) {
|
||||
}
|
||||
|
||||
@@ -1,33 +1,14 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretStringList;
|
||||
|
||||
public class RemoteConfigConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private List<String> authorizedTokens = new LinkedList<>();
|
||||
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private Map<String, String> globalConfig = new HashMap<>();
|
||||
|
||||
public List<String> getAuthorizedTokens() {
|
||||
return authorizedTokens;
|
||||
}
|
||||
|
||||
public Map<String, String> getGlobalConfig() {
|
||||
return globalConfig;
|
||||
}
|
||||
public record RemoteConfigConfiguration(@NotNull SecretStringList authorizedTokens,
|
||||
@NotNull Map<String, String> globalConfig) {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
@@ -7,18 +7,18 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class SecureBackupServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
private SecretBytes userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotBlank
|
||||
@JsonProperty
|
||||
@@ -38,8 +38,8 @@ public class SecureBackupServiceConfiguration {
|
||||
@JsonProperty
|
||||
private RetryConfiguration retry = new RetryConfiguration();
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
public SecretBytes userAuthenticationTokenSharedSecret() {
|
||||
return userAuthenticationTokenSharedSecret;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticationTokenSharedSecret,
|
||||
public record SecureStorageServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotBlank String uri,
|
||||
@NotEmpty List<@NotBlank String> storageCaCertificates,
|
||||
@Valid CircuitBreakerConfiguration circuitBreaker,
|
||||
@Valid RetryConfiguration retry) {
|
||||
|
||||
public SecureStorageServiceConfiguration {
|
||||
if (circuitBreaker == null) {
|
||||
circuitBreaker = new CircuitBreakerConfiguration();
|
||||
@@ -25,8 +25,4 @@ public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticat
|
||||
retry = new RetryConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decodeUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@ import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public record SecureValueRecovery2Configuration(
|
||||
boolean enabled,
|
||||
@NotBlank String uri,
|
||||
@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
|
||||
@NotEmpty List<@NotBlank String> svrCaCertificates,
|
||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||
@NotNull @Valid RetryConfiguration retry) {
|
||||
|
||||
@@ -8,10 +8,12 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
import java.util.Set;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record StripeConfiguration(@NotBlank String apiKey,
|
||||
@NotEmpty byte[] idempotencyKeyGenerator,
|
||||
public record StripeConfiguration(@NotNull SecretString apiKey,
|
||||
@NotNull SecretBytes idempotencyKeyGenerator,
|
||||
@NotBlank String boostDescription,
|
||||
@NotEmpty Set<@NotBlank String> supportedCurrencies) {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,48 +1,21 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 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.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class UnidentifiedDeliveryConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] certificate;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
@Size(min = 32, max = 32)
|
||||
private byte[] privateKey;
|
||||
|
||||
@NotNull
|
||||
private int expiresDays;
|
||||
|
||||
public byte[] getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public ECPrivateKey getPrivateKey() throws InvalidKeyException {
|
||||
return Curve.decodePrivatePoint(privateKey);
|
||||
}
|
||||
|
||||
public int getExpiresDays() {
|
||||
return expiresDays;
|
||||
public record UnidentifiedDeliveryConfiguration(@NotNull SecretBytes certificate,
|
||||
@ExactlySize(32) SecretBytes privateKey,
|
||||
int expiresDays) {
|
||||
public ECPrivateKey ecPrivateKey() throws InvalidKeyException {
|
||||
return Curve.decodePrivatePoint(privateKey.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,14 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 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.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class ZkConfig {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] serverSecret;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] serverPublic;
|
||||
|
||||
public byte[] getServerSecret() {
|
||||
return serverSecret;
|
||||
}
|
||||
|
||||
public byte[] getServerPublic() {
|
||||
return serverPublic;
|
||||
}
|
||||
public record ZkConfig(@NotNull SecretBytes serverSecret,
|
||||
@NotEmpty byte[] serverPublic) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public abstract class BaseSecretValidator<A extends Annotation, T, S extends Secret<? extends T>> implements ConstraintValidator<A, S> {
|
||||
|
||||
private final ConstraintValidator<A, T> validator;
|
||||
|
||||
|
||||
protected BaseSecretValidator(final ConstraintValidator<A, T> validator) {
|
||||
this.validator = requireNonNull(validator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final A constraintAnnotation) {
|
||||
validator.initialize(constraintAnnotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(final S value, final ConstraintValidatorContext context) {
|
||||
return validator.isValid(value.value(), context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
public class Secret<T> {
|
||||
|
||||
private final T value;
|
||||
|
||||
|
||||
public Secret(final T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public T value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[REDACTED]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class SecretBytes extends Secret<byte[]> {
|
||||
|
||||
public SecretBytes(final byte[] value) {
|
||||
super(requireNotEmpty(value));
|
||||
}
|
||||
|
||||
private static byte[] requireNotEmpty(final byte[] value) {
|
||||
Validate.isTrue(value.length > 0, "SecretBytes value must not be empty");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCollection;
|
||||
|
||||
public class SecretBytesList extends Secret<List<byte[]>> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class ValidatorNotEmpty extends BaseSecretValidator<NotEmpty, Collection, SecretBytesList> {
|
||||
public ValidatorNotEmpty() {
|
||||
super(new NotEmptyValidatorForCollection());
|
||||
}
|
||||
}
|
||||
|
||||
public SecretBytesList(final List<byte[]> value) {
|
||||
super(ImmutableList.copyOf(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
public class SecretStore {
|
||||
|
||||
private final Map<String, Secret<?>> secrets;
|
||||
|
||||
|
||||
public static SecretStore fromYamlFileSecretsBundle(final String filename) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> secretsBundle = SystemMapper.yamlMapper().readValue(new File(filename), Map.class);
|
||||
return fromSecretsBundle(secretsBundle);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Failed to parse YAML file [%s]".formatted(filename), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public SecretStore(final Map<String, Secret<?>> secrets) {
|
||||
this.secrets = Map.copyOf(secrets);
|
||||
}
|
||||
|
||||
public SecretString secretString(final String reference) {
|
||||
return fromStore(reference, SecretString.class);
|
||||
}
|
||||
|
||||
public SecretBytes secretBytesFromBase64String(final String reference) {
|
||||
final SecretString secret = fromStore(reference, SecretString.class);
|
||||
return new SecretBytes(decodeBase64(secret.value()));
|
||||
}
|
||||
|
||||
public SecretStringList secretStringList(final String reference) {
|
||||
return fromStore(reference, SecretStringList.class);
|
||||
}
|
||||
|
||||
public SecretBytesList secretBytesListFromBase64Strings(final String reference) {
|
||||
final List<String> secrets = secretStringList(reference).value();
|
||||
final List<byte[]> byteSecrets = secrets.stream().map(SecretStore::decodeBase64).toList();
|
||||
return new SecretBytesList(byteSecrets);
|
||||
}
|
||||
|
||||
private <T extends Secret<?>> T fromStore(final String name, final Class<T> expected) {
|
||||
final Secret<?> secret = secrets.get(name);
|
||||
if (secret == null) {
|
||||
throw new IllegalArgumentException("Secret [%s] is not present in the secrets bundle".formatted(name));
|
||||
}
|
||||
if (!expected.isInstance(secret)) {
|
||||
throw new IllegalArgumentException("Secret [%s] is of type [%s] but caller expects type [%s]".formatted(
|
||||
name, secret.getClass().getSimpleName(), expected.getSimpleName()));
|
||||
}
|
||||
return expected.cast(secret);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static SecretStore fromYamlStringSecretsBundle(final String secretsBundleYaml) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> secretsBundle = SystemMapper.yamlMapper().readValue(secretsBundleYaml, Map.class);
|
||||
return fromSecretsBundle(secretsBundle);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Failed to parse JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretStore fromSecretsBundle(final Map<String, Object> secretsBundle) {
|
||||
final Map<String, Secret<?>> store = new HashMap<>();
|
||||
secretsBundle.forEach((k, v) -> {
|
||||
if (v instanceof final String str) {
|
||||
store.put(k, new SecretString(str));
|
||||
return;
|
||||
}
|
||||
if (v instanceof final List<?> list) {
|
||||
final List<String> secrets = list.stream().map(o -> {
|
||||
if (o instanceof final String s) {
|
||||
return s;
|
||||
}
|
||||
throw new IllegalArgumentException("Secrets bundle JSON object is only supposed to have values of types String and list of Strings");
|
||||
}).toList();
|
||||
store.put(k, new SecretStringList(secrets));
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("Secrets bundle JSON object is only supposed to have values of types String and list of Strings");
|
||||
});
|
||||
return new SecretStore(store);
|
||||
}
|
||||
|
||||
private static byte[] decodeBase64(final String str) {
|
||||
return Base64.getDecoder().decode(str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class SecretString extends Secret<String> {
|
||||
public SecretString(final String value) {
|
||||
super(Validate.notBlank(value, "SecretString value must not be blank"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCollection;
|
||||
|
||||
public class SecretStringList extends Secret<List<String>> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class ValidatorNotEmpty extends BaseSecretValidator<NotEmpty, Collection, SecretStringList> {
|
||||
public ValidatorNotEmpty() {
|
||||
super(new NotEmptyValidatorForCollection());
|
||||
}
|
||||
}
|
||||
|
||||
public SecretStringList(final List<String> value) {
|
||||
super(ImmutableList.copyOf(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class SecretsModule extends SimpleModule {
|
||||
|
||||
public static final SecretsModule INSTANCE = new SecretsModule();
|
||||
|
||||
public static final String PREFIX = "secret://";
|
||||
|
||||
private final AtomicReference<SecretStore> secretStoreHolder = new AtomicReference<>(null);
|
||||
|
||||
|
||||
private SecretsModule() {
|
||||
addDeserializer(SecretString.class, createDeserializer(SecretStore::secretString));
|
||||
addDeserializer(SecretBytes.class, createDeserializer(SecretStore::secretBytesFromBase64String));
|
||||
addDeserializer(SecretStringList.class, createDeserializer(SecretStore::secretStringList));
|
||||
addDeserializer(SecretBytesList.class, createDeserializer(SecretStore::secretBytesListFromBase64Strings));
|
||||
}
|
||||
|
||||
public void setSecretStore(final SecretStore secretStore) {
|
||||
this.secretStoreHolder.set(requireNonNull(secretStore));
|
||||
}
|
||||
|
||||
private <T> JsonDeserializer<T> createDeserializer(final BiFunction<SecretStore, String, T> constructor) {
|
||||
return new JsonDeserializer<>() {
|
||||
@Override
|
||||
public T deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JacksonException {
|
||||
final SecretStore secretStore = secretStoreHolder.get();
|
||||
if (secretStore == null) {
|
||||
throw new IllegalStateException(
|
||||
"An instance of a SecretStore must be set for the SecretsModule via setSecretStore() method");
|
||||
}
|
||||
final String reference = p.getValueAsString();
|
||||
if (!reference.startsWith(PREFIX) || reference.length() <= PREFIX.length()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Value of a secret field must start with a [%s] prefix and refer to an entry in a secrets bundle".formatted(PREFIX));
|
||||
}
|
||||
return constructor.apply(secretStore, reference.substring(PREFIX.length()));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -27,15 +27,15 @@ public class ArtController {
|
||||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final ArtServiceConfiguration cfg) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.getUserAuthenticationTokenUserIdSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.userAuthenticationTokenUserIdSecret())
|
||||
.prependUsername(false)
|
||||
.truncateSignature(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
public ArtController(RateLimiters rateLimiters,
|
||||
ExternalServiceCredentialsGenerator artServiceCredentialsGenerator) {
|
||||
public ArtController(final RateLimiters rateLimiters,
|
||||
final ExternalServiceCredentialsGenerator artServiceCredentialsGenerator) {
|
||||
this.artServiceCredentialsGenerator = artServiceCredentialsGenerator;
|
||||
this.rateLimiters = rateLimiters;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class ArtController {
|
||||
@GET
|
||||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ExternalServiceCredentials getAuth(@Auth AuthenticatedAccount auth)
|
||||
public ExternalServiceCredentials getAuth(final @Auth AuthenticatedAccount auth)
|
||||
throws RateLimitExceededException {
|
||||
final UUID uuid = auth.getAccount().getUuid();
|
||||
rateLimiters.getArtPackLimiter().validate(uuid);
|
||||
|
||||
@@ -41,7 +41,7 @@ public class DirectoryV2Controller {
|
||||
return credentialsGenerator(cfg, Clock.systemUTC());
|
||||
}
|
||||
|
||||
public DirectoryV2Controller(ExternalServiceCredentialsGenerator userTokenGenerator) {
|
||||
public DirectoryV2Controller(final ExternalServiceCredentialsGenerator userTokenGenerator) {
|
||||
this.directoryServiceTokenGenerator = userTokenGenerator;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class DirectoryV2Controller {
|
||||
@GET
|
||||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getAuthToken(@Auth AuthenticatedAccount auth) {
|
||||
public Response getAuthToken(final @Auth AuthenticatedAccount auth) {
|
||||
final UUID uuid = auth.getAccount().getUuid();
|
||||
final ExternalServiceCredentials credentials = directoryServiceTokenGenerator.generateForUuid(uuid);
|
||||
return Response.ok().entity(credentials).build();
|
||||
|
||||
@@ -29,7 +29,7 @@ public class PaymentsController {
|
||||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final PaymentsServiceConfiguration cfg) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.AuthCheckResponse;
|
||||
@@ -56,7 +56,7 @@ public class SecureBackupController {
|
||||
final SecureBackupServiceConfiguration cfg,
|
||||
final Clock clock) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.withClock(clock)
|
||||
.build();
|
||||
|
||||
@@ -25,7 +25,7 @@ public class SecureStorageController {
|
||||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureStorageServiceConfiguration cfg) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.decodeUserAuthenticationTokenSharedSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@ import io.dropwizard.auth.Auth;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.Consumes;
|
||||
@@ -31,14 +36,6 @@ import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Path("/v2/backup")
|
||||
@Tag(name = "Secure Value Recovery")
|
||||
@@ -54,7 +51,7 @@ public class SecureValueRecovery2Controller {
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery2Configuration cfg, final Clock clock) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
||||
.prependUsername(false)
|
||||
.withDerivedUsernameTruncateLength(16)
|
||||
.withClock(clock)
|
||||
|
||||
@@ -22,17 +22,27 @@ import io.dropwizard.logging.filter.LevelFilterFactory;
|
||||
import io.dropwizard.logging.layout.LayoutFactory;
|
||||
import java.time.Duration;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import net.logstash.logback.appender.LogstashTcpSocketAppender;
|
||||
import net.logstash.logback.encoder.LogstashEncoder;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
||||
|
||||
@JsonTypeName("logstashtcpsocket")
|
||||
public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<ILoggingEvent> {
|
||||
|
||||
@JsonProperty
|
||||
private String destination;
|
||||
|
||||
@JsonProperty
|
||||
private Duration keepAlive = Duration.ofSeconds(20);
|
||||
private String apiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private SecretString apiKey;
|
||||
|
||||
@JsonProperty
|
||||
private String environment;
|
||||
|
||||
@JsonProperty
|
||||
@@ -47,8 +57,7 @@ public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<IL
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
public String getApiKey() {
|
||||
public SecretString getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
@@ -84,7 +93,7 @@ public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<IL
|
||||
encoder.setCustomFields(customFieldsNode.toString());
|
||||
final LayoutWrappingEncoder<ILoggingEvent> prefix = new LayoutWrappingEncoder<>();
|
||||
final PatternLayout layout = new PatternLayout();
|
||||
layout.setPattern(String.format("%s ", apiKey));
|
||||
layout.setPattern(String.format("%s ", apiKey.value()));
|
||||
prefix.setLayout(layout);
|
||||
encoder.setPrefix(prefix);
|
||||
appender.setEncoder(encoder);
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is derived from Coursera's dropwizard datadog reporter.
|
||||
* https://github.com/coursera/metrics-datadog
|
||||
@@ -10,6 +15,7 @@ import com.codahale.metrics.ScheduledReporter;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.dropwizard.metrics.BaseReporterFactory;
|
||||
import io.dropwizard.util.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@@ -20,8 +26,9 @@ import org.coursera.metrics.datadog.DatadogReporter.Expansion;
|
||||
import org.coursera.metrics.datadog.DefaultMetricNameFormatterFactory;
|
||||
import org.coursera.metrics.datadog.DynamicTagsCallbackFactory;
|
||||
import org.coursera.metrics.datadog.MetricNameFormatterFactory;
|
||||
import org.coursera.metrics.datadog.transport.AbstractTransportFactory;
|
||||
import org.coursera.metrics.datadog.transport.HttpTransport;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
||||
|
||||
@JsonTypeName("signal-datadog")
|
||||
@@ -44,8 +51,8 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private AbstractTransportFactory transport = null;
|
||||
@JsonProperty("transport")
|
||||
private HttpTransportConfig httpTransportConfig;
|
||||
|
||||
private static final EnumSet<Expansion> EXPANSIONS = EnumSet.of(
|
||||
Expansion.COUNT,
|
||||
@@ -59,7 +66,7 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||
Expansion.P999
|
||||
);
|
||||
|
||||
public ScheduledReporter build(MetricRegistry registry) {
|
||||
public ScheduledReporter build(final MetricRegistry registry) {
|
||||
final List<String> tagsWithVersion;
|
||||
|
||||
{
|
||||
@@ -74,7 +81,7 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||
}
|
||||
|
||||
return DatadogReporter.forRegistry(registry)
|
||||
.withTransport(transport.build())
|
||||
.withTransport(httpTransportConfig.httpTransport())
|
||||
.withHost(HostnameUtil.getLocalHostname())
|
||||
.withTags(tagsWithVersion)
|
||||
.withPrefix(prefix)
|
||||
@@ -86,4 +93,26 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||
.convertRatesTo(getRateUnit())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class HttpTransportConfig {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private SecretString apiKey;
|
||||
|
||||
@JsonProperty
|
||||
private Duration connectTimeout = Duration.seconds(5);
|
||||
|
||||
@JsonProperty
|
||||
private Duration socketTimeout = Duration.seconds(5);
|
||||
|
||||
|
||||
public HttpTransport httpTransport() {
|
||||
return new HttpTransport.Builder()
|
||||
.withApiKey(apiKey.value())
|
||||
.withConnectTimeout((int) connectTimeout.toMilliseconds())
|
||||
.withSocketTimeout((int) socketTimeout.toMilliseconds())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.push;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
import com.eatthepath.pushy.apns.ApnsClient;
|
||||
import com.eatthepath.pushy.apns.ApnsClientBuilder;
|
||||
import com.eatthepath.pushy.apns.DeliveryPriority;
|
||||
@@ -13,6 +15,8 @@ import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder;
|
||||
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
@@ -21,12 +25,8 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
public class APNSender implements Managed, PushNotificationSender {
|
||||
|
||||
private final ExecutorService executor;
|
||||
@@ -61,12 +61,12 @@ public class APNSender implements Managed, PushNotificationSender {
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeyException
|
||||
{
|
||||
this.executor = executor;
|
||||
this.bundleId = configuration.getBundleId();
|
||||
this.bundleId = configuration.bundleId();
|
||||
this.apnsClient = new ApnsClientBuilder().setSigningKey(
|
||||
ApnsSigningKey.loadFromInputStream(new ByteArrayInputStream(configuration.getSigningKey().getBytes()),
|
||||
configuration.getTeamId(), configuration.getKeyId()))
|
||||
ApnsSigningKey.loadFromInputStream(new ByteArrayInputStream(configuration.signingKey().value().getBytes()),
|
||||
configuration.teamId(), configuration.keyId()))
|
||||
.setTrustedServerCertificateChain(getClass().getResourceAsStream(APNS_CA_FILENAME))
|
||||
.setApnsServer(configuration.isSandboxEnabled() ? ApnsClientBuilder.DEVELOPMENT_APNS_HOST : ApnsClientBuilder.PRODUCTION_APNS_HOST)
|
||||
.setApnsServer(configuration.sandbox() ? ApnsClientBuilder.DEVELOPMENT_APNS_HOST : ApnsClientBuilder.PRODUCTION_APNS_HOST)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
@@ -25,12 +25,12 @@ import javax.validation.Payload;
|
||||
ExactlySizeValidatorForString.class,
|
||||
ExactlySizeValidatorForArraysOfByte.class,
|
||||
ExactlySizeValidatorForCollection.class,
|
||||
ExactlySizeValidatorForSecretBytes.class,
|
||||
})
|
||||
@Documented
|
||||
public @interface ExactlySize {
|
||||
|
||||
String message() default "{org.whispersystems.textsecuregcm.util.ExactlySize." +
|
||||
"message}";
|
||||
String message() default "{org.whispersystems.textsecuregcm.util.ExactlySize.message}";
|
||||
|
||||
Class<?>[] groups() default { };
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class ExactlySizeValidatorForSecretBytes extends ExactlySizeValidator<SecretBytes> {
|
||||
@Override
|
||||
protected int size(final SecretBytes value) {
|
||||
return value == null ? 0 : value.value().length;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretsModule;
|
||||
|
||||
public class SystemMapper {
|
||||
|
||||
@@ -37,6 +38,7 @@ public class SystemMapper {
|
||||
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
|
||||
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
|
||||
.registerModules(
|
||||
SecretsModule.INSTANCE,
|
||||
new JavaTimeModule(),
|
||||
new Jdk8Module());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user