Moving secret values out of the main configuration file
This commit is contained in:
parent
8d1c26d07d
commit
287e2fa89a
|
@ -0,0 +1,86 @@
|
||||||
|
datadog.apiKey: unset
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
awsAttachments.accessKey: test
|
||||||
|
awsAttachments.accessSecret: test
|
||||||
|
|
||||||
|
gcpAttachments.rsaSigningKey: |
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
AAAAAAAA
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
|
||||||
|
apn.signingKey: |
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
AAAAAAAA
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
|
||||||
|
fcm.credentials: |
|
||||||
|
{ "json": true }
|
||||||
|
|
||||||
|
cdn.accessKey: test # AWS Access Key ID
|
||||||
|
cdn.accessSecret: test # AWS Access Secret
|
||||||
|
|
||||||
|
unidentifiedDelivery.certificate: ABCD1234
|
||||||
|
unidentifiedDelivery.privateKey: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
|
||||||
|
|
||||||
|
hCaptcha.apiKey: unset
|
||||||
|
|
||||||
|
storageService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
|
|
||||||
|
backupService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
|
|
||||||
|
zkConfig.serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
|
||||||
|
|
||||||
|
genericZkConfig.serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
|
||||||
|
|
||||||
|
remoteConfig.authorizedTokens:
|
||||||
|
- token1 # 1st authorized token
|
||||||
|
- token2 # 2nd authorized token
|
||||||
|
|
||||||
|
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=
|
|
@ -3,16 +3,53 @@
|
||||||
# `unset` values will need to be set to work properly.
|
# `unset` values will need to be set to work properly.
|
||||||
# Most other values are technically valid for a local/demonstration environment, but are probably not production-ready.
|
# Most other values are technically valid for a local/demonstration environment, but are probably not production-ready.
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level: INFO
|
||||||
|
appenders:
|
||||||
|
- type: console
|
||||||
|
threshold: ALL
|
||||||
|
timeZone: UTC
|
||||||
|
target: stdout
|
||||||
|
- type: logstashtcpsocket
|
||||||
|
destination: example.com:10516
|
||||||
|
apiKey: secret://datadog.apiKey
|
||||||
|
environment: staging
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
reporters:
|
||||||
|
- type: signal-datadog
|
||||||
|
frequency: 10 seconds
|
||||||
|
tags:
|
||||||
|
- "env:staging"
|
||||||
|
- "service:chat"
|
||||||
|
transport:
|
||||||
|
apiKey: secret://datadog.apiKey
|
||||||
|
excludesAttributes:
|
||||||
|
- m1_rate
|
||||||
|
- m5_rate
|
||||||
|
- m15_rate
|
||||||
|
- mean_rate
|
||||||
|
- stddev
|
||||||
|
useRegexFilters: true
|
||||||
|
excludes:
|
||||||
|
- ^.+\.total$
|
||||||
|
- ^.+\.request\.filtering$
|
||||||
|
- ^.+\.response\.filtering$
|
||||||
|
- ^executor\..+$
|
||||||
|
- ^lettuce\..+$
|
||||||
|
reportOnStop: true
|
||||||
|
|
||||||
adminEventLoggingConfiguration:
|
adminEventLoggingConfiguration:
|
||||||
credentials: |
|
credentials: |
|
||||||
Some credentials text
|
{
|
||||||
blah blah blah
|
"key": "value"
|
||||||
|
}
|
||||||
projectId: some-project-id
|
projectId: some-project-id
|
||||||
logName: some-log-name
|
logName: some-log-name
|
||||||
|
|
||||||
stripe:
|
stripe:
|
||||||
apiKey: unset
|
apiKey: secret://stripe.apiKey
|
||||||
idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
|
idempotencyKeyGenerator: secret://stripe.idempotencyKeyGenerator
|
||||||
boostDescription: >
|
boostDescription: >
|
||||||
Example
|
Example
|
||||||
supportedCurrencies:
|
supportedCurrencies:
|
||||||
|
@ -24,7 +61,7 @@ stripe:
|
||||||
braintree:
|
braintree:
|
||||||
merchantId: unset
|
merchantId: unset
|
||||||
publicKey: unset
|
publicKey: unset
|
||||||
privateKey: unset
|
privateKey: secret://braintree.privateKey
|
||||||
environment: unset
|
environment: unset
|
||||||
graphqlUrl: unset
|
graphqlUrl: unset
|
||||||
merchantAccounts:
|
merchantAccounts:
|
||||||
|
@ -104,14 +141,14 @@ rateLimitersCluster: # Redis server configuration for rate limiters cluster
|
||||||
|
|
||||||
directoryV2:
|
directoryV2:
|
||||||
client: # Configuration for interfacing with Contact Discovery Service v2 cluster
|
client: # Configuration for interfacing with Contact Discovery Service v2 cluster
|
||||||
userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth tokens for Signal users
|
userAuthenticationTokenSharedSecret: secret://directoryV2.client.userAuthenticationTokenSharedSecret
|
||||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth identity tokens for Signal users
|
userIdTokenSharedSecret: secret://directoryV2.client.userIdTokenSharedSecret
|
||||||
|
|
||||||
svr2:
|
svr2:
|
||||||
enabled: false
|
enabled: false
|
||||||
uri: svr2.example.com
|
uri: svr2.example.com
|
||||||
userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
userAuthenticationTokenSharedSecret: secret://svr2.userAuthenticationTokenSharedSecret
|
||||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
userIdTokenSharedSecret: secret://svr2.userIdTokenSharedSecret
|
||||||
svrCaCertificates:
|
svrCaCertificates:
|
||||||
- |
|
- |
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
|
@ -146,8 +183,8 @@ metricsCluster:
|
||||||
configurationUri: redis://redis.example.com:6379/
|
configurationUri: redis://redis.example.com:6379/
|
||||||
|
|
||||||
awsAttachments: # AWS S3 configuration
|
awsAttachments: # AWS S3 configuration
|
||||||
accessKey: test
|
accessKey: secret://awsAttachments.accessKey
|
||||||
accessSecret: test
|
accessSecret: secret://awsAttachments.accessSecret
|
||||||
bucket: aws-attachments
|
bucket: aws-attachments
|
||||||
region: us-west-2
|
region: us-west-2
|
||||||
|
|
||||||
|
@ -156,35 +193,7 @@ gcpAttachments: # GCP Storage configuration
|
||||||
email: user@example.cocm
|
email: user@example.cocm
|
||||||
maxSizeInBytes: 1024
|
maxSizeInBytes: 1024
|
||||||
pathPrefix:
|
pathPrefix:
|
||||||
rsaSigningKey: |
|
rsaSigningKey: secret://gcpAttachments.rsaSigningKey
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
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
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
AAAAAAAA
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
|
|
||||||
accountDatabaseCrawler:
|
accountDatabaseCrawler:
|
||||||
chunkSize: 10 # accounts per run
|
chunkSize: 10 # accounts per run
|
||||||
|
@ -194,31 +203,24 @@ apn: # Apple Push Notifications configuration
|
||||||
bundleId: com.example.textsecuregcm
|
bundleId: com.example.textsecuregcm
|
||||||
keyId: unset
|
keyId: unset
|
||||||
teamId: unset
|
teamId: unset
|
||||||
signingKey: |
|
signingKey: secret://apn.signingKey
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
|
||||||
AAAAAAAA
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
|
|
||||||
fcm: # FCM configuration
|
fcm: # FCM configuration
|
||||||
credentials: |
|
credentials: secret://fcm.credentials
|
||||||
{ "json": true }
|
|
||||||
|
|
||||||
cdn:
|
cdn:
|
||||||
accessKey: test # AWS Access Key ID
|
accessKey: secret://cdn.accessKey
|
||||||
accessSecret: test # AWS Access Secret
|
accessSecret: secret://cdn.accessSecret
|
||||||
bucket: cdn # S3 Bucket name
|
bucket: cdn # S3 Bucket name
|
||||||
region: us-west-2 # AWS region
|
region: us-west-2 # AWS region
|
||||||
|
|
||||||
datadog:
|
datadog:
|
||||||
apiKey: unset
|
apiKey: secret://datadog.apiKey
|
||||||
environment: dev
|
environment: dev
|
||||||
|
|
||||||
unidentifiedDelivery:
|
unidentifiedDelivery:
|
||||||
certificate: ABCD1234
|
certificate: secret://unidentifiedDelivery.certificate
|
||||||
privateKey: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
|
privateKey: secret://unidentifiedDelivery.privateKey
|
||||||
expiresDays: 7
|
expiresDays: 7
|
||||||
|
|
||||||
recaptcha:
|
recaptcha:
|
||||||
|
@ -226,11 +228,11 @@ recaptcha:
|
||||||
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
|
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
|
||||||
|
|
||||||
hCaptcha:
|
hCaptcha:
|
||||||
apiKey: unset
|
apiKey: secret://hCaptcha.apiKey
|
||||||
|
|
||||||
storageService:
|
storageService:
|
||||||
uri: storage.example.com
|
uri: storage.example.com
|
||||||
userAuthenticationTokenSharedSecret: 00000f
|
userAuthenticationTokenSharedSecret: secret://storageService.userAuthenticationTokenSharedSecret
|
||||||
storageCaCertificates:
|
storageCaCertificates:
|
||||||
- |
|
- |
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
|
@ -257,7 +259,7 @@ storageService:
|
||||||
|
|
||||||
backupService:
|
backupService:
|
||||||
uri: backup.example.com
|
uri: backup.example.com
|
||||||
userAuthenticationTokenSharedSecret: 00000f
|
userAuthenticationTokenSharedSecret: secret://backupService.userAuthenticationTokenSharedSecret
|
||||||
backupCaCertificates:
|
backupCaCertificates:
|
||||||
- |
|
- |
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
|
@ -284,10 +286,10 @@ backupService:
|
||||||
|
|
||||||
zkConfig:
|
zkConfig:
|
||||||
serverPublic: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
serverPublic: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
|
serverSecret: secret://zkConfig.serverSecret
|
||||||
|
|
||||||
genericZkConfig:
|
genericZkConfig:
|
||||||
serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
|
serverSecret: secret://genericZkConfig.serverSecret
|
||||||
|
|
||||||
appConfig:
|
appConfig:
|
||||||
application: example
|
application: example
|
||||||
|
@ -295,18 +297,14 @@ appConfig:
|
||||||
configuration: example
|
configuration: example
|
||||||
|
|
||||||
remoteConfig:
|
remoteConfig:
|
||||||
authorizedTokens:
|
authorizedTokens: secret://remoteConfig.authorizedTokens
|
||||||
- # 1st authorized token
|
|
||||||
- # 2nd authorized token
|
|
||||||
- # ...
|
|
||||||
- # Nth authorized token
|
|
||||||
globalConfig: # keys and values that are given to clients on GET /v1/config
|
globalConfig: # keys and values that are given to clients on GET /v1/config
|
||||||
EXAMPLE_KEY: VALUE
|
EXAMPLE_KEY: VALUE
|
||||||
|
|
||||||
paymentsService:
|
paymentsService:
|
||||||
userAuthenticationTokenSharedSecret: 0000000f0000000f0000000f0000000f0000000f0000000f0000000f0000000f # hex-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
|
userAuthenticationTokenSharedSecret: secret://paymentsService.userAuthenticationTokenSharedSecret
|
||||||
fixerApiKey: unset
|
fixerApiKey: secret://paymentsService.fixerApiKey
|
||||||
coinMarketCapApiKey: unset
|
coinMarketCapApiKey: secret://paymentsService.coinMarketCapApiKey
|
||||||
coinMarketCapCurrencyIds:
|
coinMarketCapCurrencyIds:
|
||||||
MOB: 7878
|
MOB: 7878
|
||||||
paymentCurrencies:
|
paymentCurrencies:
|
||||||
|
@ -314,8 +312,8 @@ paymentsService:
|
||||||
- MOB
|
- MOB
|
||||||
|
|
||||||
artService:
|
artService:
|
||||||
userAuthenticationTokenSharedSecret: 0000000f0000000f0000000f0000000f0000000f0000000f0000000f0000000f # hex-encoded 32-byte secret not shared with any external service, but used in ArtController
|
userAuthenticationTokenSharedSecret: secret://artService.userAuthenticationTokenSharedSecret
|
||||||
userAuthenticationTokenUserIdSecret: 00000f # hex-encoded secret to obscure user phone numbers from Sticker Creator
|
userAuthenticationTokenUserIdSecret: secret://artService.userAuthenticationTokenUserIdSecret
|
||||||
|
|
||||||
badges:
|
badges:
|
||||||
badges:
|
badges:
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package org.whispersystems.textsecuregcm;
|
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 com.amazonaws.ClientConfiguration;
|
import com.amazonaws.ClientConfiguration;
|
||||||
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
|
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.RecaptchaClient;
|
||||||
import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager;
|
import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
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.AccountController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.AccountControllerV2;
|
import org.whispersystems.textsecuregcm.controllers.AccountControllerV2;
|
||||||
import org.whispersystems.textsecuregcm.controllers.ArtController;
|
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);
|
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
|
@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 DeleteUserCommand());
|
||||||
bootstrap.addCommand(new CertificateCommand());
|
bootstrap.addCommand(new CertificateCommand());
|
||||||
bootstrap.addCommand(new ZkParamsCommand());
|
bootstrap.addCommand(new ZkParamsCommand());
|
||||||
|
@ -289,8 +308,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
environment.lifecycle().manage(new MicrometerRegistryManager(Metrics.globalRegistry));
|
environment.lifecycle().manage(new MicrometerRegistryManager(Metrics.globalRegistry));
|
||||||
|
|
||||||
SystemMapper.configureMapper(environment.getObjectMapper());
|
|
||||||
|
|
||||||
HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup =
|
HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup =
|
||||||
new HeaderControlledResourceBundleLookup();
|
new HeaderControlledResourceBundleLookup();
|
||||||
ConfiguredProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter(
|
ConfiguredProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter(
|
||||||
|
@ -300,11 +317,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
||||||
config.getDynamoDbClientConfiguration(),
|
config.getDynamoDbClientConfiguration(),
|
||||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER);
|
||||||
|
|
||||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
||||||
config.getDynamoDbClientConfiguration(),
|
config.getDynamoDbClientConfiguration(),
|
||||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER);
|
||||||
|
|
||||||
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
|
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
|
||||||
.withRegion(config.getDynamoDbClientConfiguration().getRegion())
|
.withRegion(config.getDynamoDbClientConfiguration().getRegion())
|
||||||
|
@ -446,11 +463,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
config.getAdminEventLoggingConfiguration().projectId(),
|
config.getAdminEventLoggingConfiguration().projectId(),
|
||||||
config.getAdminEventLoggingConfiguration().logName());
|
config.getAdminEventLoggingConfiguration().logName());
|
||||||
|
|
||||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey(), subscriptionProcessorExecutor,
|
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey().value(), subscriptionProcessorExecutor,
|
||||||
config.getStripe().idempotencyKeyGenerator(), config.getStripe().boostDescription(), config.getStripe()
|
config.getStripe().idempotencyKeyGenerator().value(), config.getStripe().boostDescription(), config.getStripe()
|
||||||
.supportedCurrencies());
|
.supportedCurrencies());
|
||||||
BraintreeManager braintreeManager = new BraintreeManager(config.getBraintree().merchantId(),
|
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().supportedCurrencies(), config.getBraintree().merchantAccounts(),
|
||||||
config.getBraintree().graphqlUrl(), config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor);
|
config.getBraintree().graphqlUrl(), config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor);
|
||||||
|
|
||||||
|
@ -506,9 +523,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
deletedAccountsManager, keys, messagesManager, profilesManager,
|
deletedAccountsManager, keys, messagesManager, profilesManager,
|
||||||
pendingAccountsManager, secureStorageClient, secureBackupClient, secureValueRecovery2Client, clientPresenceManager,
|
pendingAccountsManager, secureStorageClient, secureBackupClient, secureValueRecovery2Client, clientPresenceManager,
|
||||||
experimentEnrollmentManager, registrationRecoveryPasswordsManager, clock);
|
experimentEnrollmentManager, registrationRecoveryPasswordsManager, clock);
|
||||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||||
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
|
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
|
||||||
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials());
|
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials().value());
|
||||||
ApnPushNotificationScheduler apnPushNotificationScheduler = new ApnPushNotificationScheduler(pushSchedulerCluster,
|
ApnPushNotificationScheduler apnPushNotificationScheduler = new ApnPushNotificationScheduler(pushSchedulerCluster,
|
||||||
apnSender, accountsManager);
|
apnSender, accountsManager);
|
||||||
PushNotificationManager pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender,
|
PushNotificationManager pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender,
|
||||||
|
@ -553,7 +570,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
|
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
|
||||||
dynamicConfigurationManager);
|
dynamicConfigurationManager);
|
||||||
HttpClient hcaptchaHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
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));
|
CaptchaChecker captchaChecker = new CaptchaChecker(List.of(recaptchaClient, hCaptchaClient));
|
||||||
|
|
||||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(pushNotificationManager, pushChallengeDynamoDb);
|
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();
|
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().getFixerApiKey());
|
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().fixerApiKey().value());
|
||||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().getCoinMarketCapApiKey(), config.getPaymentsServiceConfiguration().getCoinMarketCapCurrencyIds());
|
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().coinMarketCapApiKey().value(), config.getPaymentsServiceConfiguration().coinMarketCapCurrencyIds());
|
||||||
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, coinMarketCapClient,
|
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(apnSender);
|
||||||
environment.lifecycle().manage(apnPushNotificationScheduler);
|
environment.lifecycle().manage(apnPushNotificationScheduler);
|
||||||
|
@ -610,19 +627,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
|
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
|
||||||
.create(AwsBasicCredentials.create(
|
.create(AwsBasicCredentials.create(
|
||||||
config.getCdnConfiguration().getAccessKey(),
|
config.getCdnConfiguration().accessKey().value(),
|
||||||
config.getCdnConfiguration().getAccessSecret()));
|
config.getCdnConfiguration().accessSecret().value()));
|
||||||
S3Client cdnS3Client = S3Client.builder()
|
S3Client cdnS3Client = S3Client.builder()
|
||||||
.credentialsProvider(cdnCredentialsProvider)
|
.credentialsProvider(cdnCredentialsProvider)
|
||||||
.region(Region.of(config.getCdnConfiguration().getRegion()))
|
.region(Region.of(config.getCdnConfiguration().region()))
|
||||||
.build();
|
.build();
|
||||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().getRegion(),
|
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().region(),
|
||||||
config.getCdnConfiguration().getBucket(), config.getCdnConfiguration().getAccessKey());
|
config.getCdnConfiguration().bucket(), config.getCdnConfiguration().accessKey().value());
|
||||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().getAccessSecret(),
|
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().accessSecret().value(),
|
||||||
config.getCdnConfiguration().getRegion());
|
config.getCdnConfiguration().region());
|
||||||
|
|
||||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().getServerSecret());
|
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().serverSecret().value());
|
||||||
GenericServerSecretParams genericZkSecretParams = new GenericServerSecretParams(config.getGenericZkConfig().serverSecret());
|
GenericServerSecretParams genericZkSecretParams = new GenericServerSecretParams(config.getGenericZkConfig().serverSecret().value());
|
||||||
ServerZkProfileOperations zkProfileOperations = new ServerZkProfileOperations(zkSecretParams);
|
ServerZkProfileOperations zkProfileOperations = new ServerZkProfileOperations(zkSecretParams);
|
||||||
ServerZkAuthOperations zkAuthOperations = new ServerZkAuthOperations(zkSecretParams);
|
ServerZkAuthOperations zkAuthOperations = new ServerZkAuthOperations(zkSecretParams);
|
||||||
ServerZkReceiptOperations zkReceiptOperations = new ServerZkReceiptOperations(zkSecretParams);
|
ServerZkReceiptOperations zkReceiptOperations = new ServerZkReceiptOperations(zkSecretParams);
|
||||||
|
@ -720,10 +737,10 @@ 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().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getRegion(), config.getAwsAttachmentsConfiguration().getBucket()),
|
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().accessKey().value(), config.getAwsAttachmentsConfiguration().accessSecret().value(), config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
|
||||||
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()),
|
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().domain(), config.getGcpAttachmentsConfiguration().email(), config.getGcpAttachmentsConfiguration().maxSizeInBytes(), config.getGcpAttachmentsConfiguration().pathPrefix(), config.getGcpAttachmentsConfiguration().rsaSigningKey().value()),
|
||||||
new CallLinkController(rateLimiters, genericZkSecretParams),
|
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 ChallengeController(rateLimitChallengeManager),
|
||||||
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keys, rateLimiters, config.getMaxDevices()),
|
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keys, rateLimiters, config.getMaxDevices()),
|
||||||
new DirectoryV2Controller(directoryV2CredentialsGenerator),
|
new DirectoryV2Controller(directoryV2CredentialsGenerator),
|
||||||
|
@ -735,19 +752,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
||||||
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
|
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
|
||||||
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
|
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
|
||||||
config.getCdnConfiguration().getBucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
config.getCdnConfiguration().bucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
||||||
new ProvisioningController(rateLimiters, provisioningManager),
|
new ProvisioningController(rateLimiters, provisioningManager),
|
||||||
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
|
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
|
||||||
rateLimiters),
|
rateLimiters),
|
||||||
new RemoteConfigController(remoteConfigsManager, adminEventLogger,
|
new RemoteConfigController(remoteConfigsManager, adminEventLogger,
|
||||||
config.getRemoteConfigConfiguration().getAuthorizedTokens(),
|
config.getRemoteConfigConfiguration().authorizedTokens().value(),
|
||||||
config.getRemoteConfigConfiguration().getGlobalConfig()),
|
config.getRemoteConfigConfiguration().globalConfig()),
|
||||||
new SecureBackupController(backupCredentialsGenerator, accountsManager),
|
new SecureBackupController(backupCredentialsGenerator, accountsManager),
|
||||||
new SecureStorageController(storageCredentialsGenerator),
|
new SecureStorageController(storageCredentialsGenerator),
|
||||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager, config.getSvr2Configuration()),
|
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager, config.getSvr2Configuration()),
|
||||||
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
new StickerController(rateLimiters, config.getCdnConfiguration().accessKey().value(),
|
||||||
config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(),
|
config.getCdnConfiguration().accessSecret().value(), config.getCdnConfiguration().region(),
|
||||||
config.getCdnConfiguration().getBucket()),
|
config.getCdnConfiguration().bucket()),
|
||||||
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
||||||
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
||||||
accountsManager, clock)
|
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.hmac256TruncatedToHexString;
|
||||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmacHexStringsEqual;
|
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmacHexStringsEqual;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -17,6 +18,7 @@ import java.util.UUID;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
|
|
||||||
public class ExternalServiceCredentialsGenerator {
|
public class ExternalServiceCredentialsGenerator {
|
||||||
|
|
||||||
|
@ -40,6 +42,12 @@ public class ExternalServiceCredentialsGenerator {
|
||||||
|
|
||||||
private final int derivedUsernameTruncateLength;
|
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) {
|
public static ExternalServiceCredentialsGenerator.Builder builder(final byte[] key) {
|
||||||
return new Builder(key);
|
return new Builder(key);
|
||||||
}
|
}
|
||||||
|
@ -240,6 +248,10 @@ public class ExternalServiceCredentialsGenerator {
|
||||||
this.key = requireNonNull(key);
|
this.key = requireNonNull(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder withUserDerivationKey(final SecretBytes userDerivationKey) {
|
||||||
|
return withUserDerivationKey(userDerivationKey.value());
|
||||||
|
}
|
||||||
|
|
||||||
public Builder withUserDerivationKey(final byte[] userDerivationKey) {
|
public Builder withUserDerivationKey(final byte[] userDerivationKey) {
|
||||||
Validate.isTrue(requireNonNull(userDerivationKey).length > 0, "userDerivationKey must not be empty");
|
Validate.isTrue(requireNonNull(userDerivationKey).length > 0, "userDerivationKey must not be empty");
|
||||||
this.userDerivationKey = userDerivationKey;
|
this.userDerivationKey = userDerivationKey;
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
public record AdminEventLoggingConfiguration(
|
public record AdminEventLoggingConfiguration(
|
||||||
@NotEmpty String credentials,
|
@NotBlank String credentials,
|
||||||
@NotEmpty String projectId,
|
@NotEmpty String projectId,
|
||||||
@NotEmpty String logName) {
|
@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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
|
|
||||||
|
|
||||||
public class ApnConfiguration {
|
public record ApnConfiguration(@NotBlank String teamId,
|
||||||
|
@NotBlank String keyId,
|
||||||
@NotEmpty
|
@NotNull SecretString signingKey,
|
||||||
@JsonProperty
|
@NotBlank String bundleId,
|
||||||
private String teamId;
|
boolean sandbox) {
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,35 +5,17 @@
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
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.time.Duration;
|
||||||
import java.util.HexFormat;
|
|
||||||
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.util.ExactlySize;
|
||||||
|
|
||||||
public class ArtServiceConfiguration {
|
public record ArtServiceConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||||
|
@NotNull SecretBytes userAuthenticationTokenUserIdSecret,
|
||||||
@NotEmpty
|
@NotNull Duration tokenExpiration) {
|
||||||
@JsonProperty
|
public ArtServiceConfiguration {
|
||||||
private String userAuthenticationTokenSharedSecret;
|
tokenExpiration = firstNonNull(tokenExpiration, Duration.ofDays(1));
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,15 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
|
|
||||||
public class AwsAttachmentsConfiguration {
|
public record AwsAttachmentsConfiguration(@NotNull SecretString accessKey,
|
||||||
|
@NotNull SecretString accessSecret,
|
||||||
@NotEmpty
|
@NotBlank String bucket,
|
||||||
@JsonProperty
|
@NotBlank String region) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotBlank;
|
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.SecretString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param merchantId the Braintree merchant ID
|
* @param merchantId the Braintree merchant ID
|
||||||
|
@ -24,7 +25,7 @@ import javax.validation.constraints.NotNull;
|
||||||
*/
|
*/
|
||||||
public record BraintreeConfiguration(@NotBlank String merchantId,
|
public record BraintreeConfiguration(@NotBlank String merchantId,
|
||||||
@NotBlank String publicKey,
|
@NotBlank String publicKey,
|
||||||
@NotBlank String privateKey,
|
@NotNull SecretString privateKey,
|
||||||
@NotBlank String environment,
|
@NotBlank String environment,
|
||||||
@NotEmpty Set<@NotBlank String> supportedCurrencies,
|
@NotEmpty Set<@NotBlank String> supportedCurrencies,
|
||||||
@NotBlank String graphqlUrl,
|
@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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -7,16 +7,17 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import io.micrometer.datadog.DatadogConfig;
|
import io.micrometer.datadog.DatadogConfig;
|
||||||
|
import java.time.Duration;
|
||||||
import javax.validation.constraints.Min;
|
import javax.validation.constraints.Min;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.time.Duration;
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
|
|
||||||
public class DatadogConfiguration implements DatadogConfig {
|
public class DatadogConfiguration implements DatadogConfig {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotBlank
|
@NotNull
|
||||||
private String apiKey;
|
private SecretString apiKey;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -32,7 +33,7 @@ public class DatadogConfiguration implements DatadogConfig {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String apiKey() {
|
public String apiKey() {
|
||||||
return apiKey;
|
return apiKey.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2023 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||||
|
|
||||||
public record DirectoryV2ClientConfiguration(@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
public record DirectoryV2ClientConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret) {
|
@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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import io.dropwizard.util.Strings;
|
import io.dropwizard.util.Strings;
|
||||||
import io.dropwizard.validation.ValidationMethod;
|
import io.dropwizard.validation.ValidationMethod;
|
||||||
import javax.validation.constraints.Min;
|
import javax.validation.constraints.Min;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
public class GcpAttachmentsConfiguration {
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public record GcpAttachmentsConfiguration(@NotBlank String domain,
|
||||||
|
@NotBlank String email,
|
||||||
|
@Min(1) int maxSizeInBytes,
|
||||||
|
String pathPrefix,
|
||||||
|
@NotNull SecretString rsaSigningKey) {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@ValidationMethod(message = "pathPrefix must be empty or start with /")
|
@ValidationMethod(message = "pathPrefix must be empty or start with /")
|
||||||
public boolean isPathPrefixValid() {
|
public boolean isPathPrefixValid() {
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
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 javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
|
|
||||||
public record GenericZkConfig (
|
public record GenericZkConfig(@NotNull SecretBytes serverSecret) {
|
||||||
@JsonProperty
|
}
|
||||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
|
||||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
|
||||||
@NotNull
|
|
||||||
byte[] serverSecret
|
|
||||||
) {}
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2021-2022 Signal Messenger, LLC
|
* Copyright 2021 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import java.util.HexFormat;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotEmpty;
|
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 {
|
public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||||
|
@NotNull SecretString coinMarketCapApiKey,
|
||||||
@NotEmpty
|
@NotNull SecretString fixerApiKey,
|
||||||
@JsonProperty
|
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds,
|
||||||
private String userAuthenticationTokenSharedSecret;
|
@NotEmpty List<String> paymentCurrencies) {
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,14 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
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 java.util.Map;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretStringList;
|
||||||
|
|
||||||
public class RemoteConfigConfiguration {
|
public record RemoteConfigConfiguration(@NotNull SecretStringList authorizedTokens,
|
||||||
|
@NotNull Map<String, String> globalConfig) {
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -7,18 +7,18 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import java.util.HexFormat;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotBlank;
|
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;
|
||||||
|
|
||||||
public class SecureBackupServiceConfiguration {
|
public class SecureBackupServiceConfiguration {
|
||||||
|
|
||||||
@NotEmpty
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String userAuthenticationTokenSharedSecret;
|
private SecretBytes userAuthenticationTokenSharedSecret;
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -38,8 +38,8 @@ public class SecureBackupServiceConfiguration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RetryConfiguration retry = new RetryConfiguration();
|
private RetryConfiguration retry = new RetryConfiguration();
|
||||||
|
|
||||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
public SecretBytes userAuthenticationTokenSharedSecret() {
|
||||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
return userAuthenticationTokenSharedSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
|
@ -5,18 +5,18 @@
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import java.util.HexFormat;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotEmpty;
|
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,
|
@NotBlank String uri,
|
||||||
@NotEmpty List<@NotBlank String> storageCaCertificates,
|
@NotEmpty List<@NotBlank String> storageCaCertificates,
|
||||||
@Valid CircuitBreakerConfiguration circuitBreaker,
|
@Valid CircuitBreakerConfiguration circuitBreaker,
|
||||||
@Valid RetryConfiguration retry) {
|
@Valid RetryConfiguration retry) {
|
||||||
|
|
||||||
public SecureStorageServiceConfiguration {
|
public SecureStorageServiceConfiguration {
|
||||||
if (circuitBreaker == null) {
|
if (circuitBreaker == null) {
|
||||||
circuitBreaker = new CircuitBreakerConfiguration();
|
circuitBreaker = new CircuitBreakerConfiguration();
|
||||||
|
@ -25,8 +25,4 @@ public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticat
|
||||||
retry = new RetryConfiguration();
|
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.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.util.ExactlySize;
|
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||||
|
|
||||||
public record SecureValueRecovery2Configuration(
|
public record SecureValueRecovery2Configuration(
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
@NotBlank String uri,
|
@NotBlank String uri,
|
||||||
@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret,
|
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
|
||||||
@NotEmpty List<@NotBlank String> svrCaCertificates,
|
@NotEmpty List<@NotBlank String> svrCaCertificates,
|
||||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||||
@NotNull @Valid RetryConfiguration retry) {
|
@NotNull @Valid RetryConfiguration retry) {
|
||||||
|
|
|
@ -8,10 +8,12 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotEmpty;
|
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,
|
public record StripeConfiguration(@NotNull SecretString apiKey,
|
||||||
@NotEmpty byte[] idempotencyKeyGenerator,
|
@NotNull SecretBytes idempotencyKeyGenerator,
|
||||||
@NotBlank String boostDescription,
|
@NotBlank String boostDescription,
|
||||||
@NotEmpty Set<@NotBlank String> supportedCurrencies) {
|
@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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import javax.validation.constraints.NotNull;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
|
||||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||||
import org.signal.libsignal.protocol.ecc.Curve;
|
import org.signal.libsignal.protocol.ecc.Curve;
|
||||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
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;
|
public record UnidentifiedDeliveryConfiguration(@NotNull SecretBytes certificate,
|
||||||
import javax.validation.constraints.Size;
|
@ExactlySize(32) SecretBytes privateKey,
|
||||||
|
int expiresDays) {
|
||||||
public class UnidentifiedDeliveryConfiguration {
|
public ECPrivateKey ecPrivateKey() throws InvalidKeyException {
|
||||||
|
return Curve.decodePrivatePoint(privateKey.value());
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,14 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
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 javax.validation.constraints.NotNull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
|
|
||||||
public class ZkConfig {
|
public record ZkConfig(@NotNull SecretBytes serverSecret,
|
||||||
|
@NotEmpty byte[] serverPublic) {
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final ArtServiceConfiguration cfg) {
|
||||||
return ExternalServiceCredentialsGenerator
|
return ExternalServiceCredentialsGenerator
|
||||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
.withUserDerivationKey(cfg.getUserAuthenticationTokenUserIdSecret())
|
.withUserDerivationKey(cfg.userAuthenticationTokenUserIdSecret())
|
||||||
.prependUsername(false)
|
.prependUsername(false)
|
||||||
.truncateSignature(false)
|
.truncateSignature(false)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtController(RateLimiters rateLimiters,
|
public ArtController(final RateLimiters rateLimiters,
|
||||||
ExternalServiceCredentialsGenerator artServiceCredentialsGenerator) {
|
final ExternalServiceCredentialsGenerator artServiceCredentialsGenerator) {
|
||||||
this.artServiceCredentialsGenerator = artServiceCredentialsGenerator;
|
this.artServiceCredentialsGenerator = artServiceCredentialsGenerator;
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ public class ArtController {
|
||||||
@GET
|
@GET
|
||||||
@Path("/auth")
|
@Path("/auth")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public ExternalServiceCredentials getAuth(@Auth AuthenticatedAccount auth)
|
public ExternalServiceCredentials getAuth(final @Auth AuthenticatedAccount auth)
|
||||||
throws RateLimitExceededException {
|
throws RateLimitExceededException {
|
||||||
final UUID uuid = auth.getAccount().getUuid();
|
final UUID uuid = auth.getAccount().getUuid();
|
||||||
rateLimiters.getArtPackLimiter().validate(uuid);
|
rateLimiters.getArtPackLimiter().validate(uuid);
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class DirectoryV2Controller {
|
||||||
return credentialsGenerator(cfg, Clock.systemUTC());
|
return credentialsGenerator(cfg, Clock.systemUTC());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryV2Controller(ExternalServiceCredentialsGenerator userTokenGenerator) {
|
public DirectoryV2Controller(final ExternalServiceCredentialsGenerator userTokenGenerator) {
|
||||||
this.directoryServiceTokenGenerator = userTokenGenerator;
|
this.directoryServiceTokenGenerator = userTokenGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public class DirectoryV2Controller {
|
||||||
@GET
|
@GET
|
||||||
@Path("/auth")
|
@Path("/auth")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Response getAuthToken(@Auth AuthenticatedAccount auth) {
|
public Response getAuthToken(final @Auth AuthenticatedAccount auth) {
|
||||||
final UUID uuid = auth.getAccount().getUuid();
|
final UUID uuid = auth.getAccount().getUuid();
|
||||||
final ExternalServiceCredentials credentials = directoryServiceTokenGenerator.generateForUuid(uuid);
|
final ExternalServiceCredentials credentials = directoryServiceTokenGenerator.generateForUuid(uuid);
|
||||||
return Response.ok().entity(credentials).build();
|
return Response.ok().entity(credentials).build();
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class PaymentsController {
|
||||||
|
|
||||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final PaymentsServiceConfiguration cfg) {
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final PaymentsServiceConfiguration cfg) {
|
||||||
return ExternalServiceCredentialsGenerator
|
return ExternalServiceCredentialsGenerator
|
||||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
.prependUsername(true)
|
.prependUsername(true)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckResponse;
|
import org.whispersystems.textsecuregcm.entities.AuthCheckResponse;
|
||||||
|
@ -56,7 +56,7 @@ public class SecureBackupController {
|
||||||
final SecureBackupServiceConfiguration cfg,
|
final SecureBackupServiceConfiguration cfg,
|
||||||
final Clock clock) {
|
final Clock clock) {
|
||||||
return ExternalServiceCredentialsGenerator
|
return ExternalServiceCredentialsGenerator
|
||||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
.prependUsername(true)
|
.prependUsername(true)
|
||||||
.withClock(clock)
|
.withClock(clock)
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class SecureStorageController {
|
||||||
|
|
||||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureStorageServiceConfiguration cfg) {
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureStorageServiceConfiguration cfg) {
|
||||||
return ExternalServiceCredentialsGenerator
|
return ExternalServiceCredentialsGenerator
|
||||||
.builder(cfg.decodeUserAuthenticationTokenSharedSecret())
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
.prependUsername(true)
|
.prependUsername(true)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,11 @@ import io.dropwizard.auth.Auth;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
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.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.ws.rs.Consumes;
|
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.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.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")
|
@Path("/v2/backup")
|
||||||
@Tag(name = "Secure Value Recovery")
|
@Tag(name = "Secure Value Recovery")
|
||||||
|
@ -54,7 +51,7 @@ public class SecureValueRecovery2Controller {
|
||||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery2Configuration cfg, final Clock clock) {
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery2Configuration cfg, final Clock clock) {
|
||||||
return ExternalServiceCredentialsGenerator
|
return ExternalServiceCredentialsGenerator
|
||||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret())
|
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
||||||
.prependUsername(false)
|
.prependUsername(false)
|
||||||
.withDerivedUsernameTruncateLength(16)
|
.withDerivedUsernameTruncateLength(16)
|
||||||
.withClock(clock)
|
.withClock(clock)
|
||||||
|
|
|
@ -22,17 +22,27 @@ import io.dropwizard.logging.filter.LevelFilterFactory;
|
||||||
import io.dropwizard.logging.layout.LayoutFactory;
|
import io.dropwizard.logging.layout.LayoutFactory;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import net.logstash.logback.appender.LogstashTcpSocketAppender;
|
import net.logstash.logback.appender.LogstashTcpSocketAppender;
|
||||||
import net.logstash.logback.encoder.LogstashEncoder;
|
import net.logstash.logback.encoder.LogstashEncoder;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
||||||
|
|
||||||
@JsonTypeName("logstashtcpsocket")
|
@JsonTypeName("logstashtcpsocket")
|
||||||
public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<ILoggingEvent> {
|
public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<ILoggingEvent> {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
private String destination;
|
private String destination;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
private Duration keepAlive = Duration.ofSeconds(20);
|
private Duration keepAlive = Duration.ofSeconds(20);
|
||||||
private String apiKey;
|
|
||||||
|
@JsonProperty
|
||||||
|
@NotNull
|
||||||
|
private SecretString apiKey;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
private String environment;
|
private String environment;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -47,8 +57,7 @@ public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<IL
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@NotEmpty
|
public SecretString getApiKey() {
|
||||||
public String getApiKey() {
|
|
||||||
return apiKey;
|
return apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +93,7 @@ public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<IL
|
||||||
encoder.setCustomFields(customFieldsNode.toString());
|
encoder.setCustomFields(customFieldsNode.toString());
|
||||||
final LayoutWrappingEncoder<ILoggingEvent> prefix = new LayoutWrappingEncoder<>();
|
final LayoutWrappingEncoder<ILoggingEvent> prefix = new LayoutWrappingEncoder<>();
|
||||||
final PatternLayout layout = new PatternLayout();
|
final PatternLayout layout = new PatternLayout();
|
||||||
layout.setPattern(String.format("%s ", apiKey));
|
layout.setPattern(String.format("%s ", apiKey.value()));
|
||||||
prefix.setLayout(layout);
|
prefix.setLayout(layout);
|
||||||
encoder.setPrefix(prefix);
|
encoder.setPrefix(prefix);
|
||||||
appender.setEncoder(encoder);
|
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.
|
* This is derived from Coursera's dropwizard datadog reporter.
|
||||||
* https://github.com/coursera/metrics-datadog
|
* 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.JsonProperty;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
import io.dropwizard.metrics.BaseReporterFactory;
|
import io.dropwizard.metrics.BaseReporterFactory;
|
||||||
|
import io.dropwizard.util.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
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.DefaultMetricNameFormatterFactory;
|
||||||
import org.coursera.metrics.datadog.DynamicTagsCallbackFactory;
|
import org.coursera.metrics.datadog.DynamicTagsCallbackFactory;
|
||||||
import org.coursera.metrics.datadog.MetricNameFormatterFactory;
|
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.WhisperServerVersion;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||||
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
||||||
|
|
||||||
@JsonTypeName("signal-datadog")
|
@JsonTypeName("signal-datadog")
|
||||||
|
@ -44,8 +51,8 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty("transport")
|
||||||
private AbstractTransportFactory transport = null;
|
private HttpTransportConfig httpTransportConfig;
|
||||||
|
|
||||||
private static final EnumSet<Expansion> EXPANSIONS = EnumSet.of(
|
private static final EnumSet<Expansion> EXPANSIONS = EnumSet.of(
|
||||||
Expansion.COUNT,
|
Expansion.COUNT,
|
||||||
|
@ -59,7 +66,7 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||||
Expansion.P999
|
Expansion.P999
|
||||||
);
|
);
|
||||||
|
|
||||||
public ScheduledReporter build(MetricRegistry registry) {
|
public ScheduledReporter build(final MetricRegistry registry) {
|
||||||
final List<String> tagsWithVersion;
|
final List<String> tagsWithVersion;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -74,7 +81,7 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
return DatadogReporter.forRegistry(registry)
|
return DatadogReporter.forRegistry(registry)
|
||||||
.withTransport(transport.build())
|
.withTransport(httpTransportConfig.httpTransport())
|
||||||
.withHost(HostnameUtil.getLocalHostname())
|
.withHost(HostnameUtil.getLocalHostname())
|
||||||
.withTags(tagsWithVersion)
|
.withTags(tagsWithVersion)
|
||||||
.withPrefix(prefix)
|
.withPrefix(prefix)
|
||||||
|
@ -86,4 +93,26 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
||||||
.convertRatesTo(getRateUnit())
|
.convertRatesTo(getRateUnit())
|
||||||
.build();
|
.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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.push;
|
package org.whispersystems.textsecuregcm.push;
|
||||||
|
|
||||||
|
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||||
|
|
||||||
import com.eatthepath.pushy.apns.ApnsClient;
|
import com.eatthepath.pushy.apns.ApnsClient;
|
||||||
import com.eatthepath.pushy.apns.ApnsClientBuilder;
|
import com.eatthepath.pushy.apns.ApnsClientBuilder;
|
||||||
import com.eatthepath.pushy.apns.DeliveryPriority;
|
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.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import io.dropwizard.lifecycle.Managed;
|
import io.dropwizard.lifecycle.Managed;
|
||||||
|
import io.micrometer.core.instrument.Metrics;
|
||||||
|
import io.micrometer.core.instrument.Timer;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
@ -21,12 +25,8 @@ import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import io.micrometer.core.instrument.Metrics;
|
|
||||||
import io.micrometer.core.instrument.Timer;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
||||||
|
|
||||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
|
||||||
|
|
||||||
public class APNSender implements Managed, PushNotificationSender {
|
public class APNSender implements Managed, PushNotificationSender {
|
||||||
|
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
|
@ -61,12 +61,12 @@ public class APNSender implements Managed, PushNotificationSender {
|
||||||
throws IOException, NoSuchAlgorithmException, InvalidKeyException
|
throws IOException, NoSuchAlgorithmException, InvalidKeyException
|
||||||
{
|
{
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.bundleId = configuration.getBundleId();
|
this.bundleId = configuration.bundleId();
|
||||||
this.apnsClient = new ApnsClientBuilder().setSigningKey(
|
this.apnsClient = new ApnsClientBuilder().setSigningKey(
|
||||||
ApnsSigningKey.loadFromInputStream(new ByteArrayInputStream(configuration.getSigningKey().getBytes()),
|
ApnsSigningKey.loadFromInputStream(new ByteArrayInputStream(configuration.signingKey().value().getBytes()),
|
||||||
configuration.getTeamId(), configuration.getKeyId()))
|
configuration.teamId(), configuration.keyId()))
|
||||||
.setTrustedServerCertificateChain(getClass().getResourceAsStream(APNS_CA_FILENAME))
|
.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();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -25,12 +25,12 @@ import javax.validation.Payload;
|
||||||
ExactlySizeValidatorForString.class,
|
ExactlySizeValidatorForString.class,
|
||||||
ExactlySizeValidatorForArraysOfByte.class,
|
ExactlySizeValidatorForArraysOfByte.class,
|
||||||
ExactlySizeValidatorForCollection.class,
|
ExactlySizeValidatorForCollection.class,
|
||||||
|
ExactlySizeValidatorForSecretBytes.class,
|
||||||
})
|
})
|
||||||
@Documented
|
@Documented
|
||||||
public @interface ExactlySize {
|
public @interface ExactlySize {
|
||||||
|
|
||||||
String message() default "{org.whispersystems.textsecuregcm.util.ExactlySize." +
|
String message() default "{org.whispersystems.textsecuregcm.util.ExactlySize.message}";
|
||||||
"message}";
|
|
||||||
|
|
||||||
Class<?>[] groups() default { };
|
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.jdk8.Jdk8Module;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretsModule;
|
||||||
|
|
||||||
public class SystemMapper {
|
public class SystemMapper {
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ public class SystemMapper {
|
||||||
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
|
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
|
||||||
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
|
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
|
||||||
.registerModules(
|
.registerModules(
|
||||||
|
SecretsModule.INSTANCE,
|
||||||
new JavaTimeModule(),
|
new JavaTimeModule(),
|
||||||
new Jdk8Module());
|
new Jdk8Module());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<validation-config
|
||||||
|
xmlns="http://xmlns.jcp.org/xml/ns/validation/configuration"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/configuration validation-configuration-2.0.xsd"
|
||||||
|
version="2.0">
|
||||||
|
<constraint-mapping>META-INF/validation/constraints-custom.xml</constraint-mapping>
|
||||||
|
</validation-config>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<constraint-mappings
|
||||||
|
xmlns="http://xmlns.jcp.org/xml/ns/validation/mapping"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/mapping
|
||||||
|
http://xmlns.jcp.org/xml/ns/validation/mapping/validation-mapping-2.0.xsd"
|
||||||
|
version="2.0">
|
||||||
|
<constraint-definition annotation="javax.validation.constraints.NotEmpty">
|
||||||
|
<validated-by include-existing-validators="true">
|
||||||
|
<value>org.whispersystems.textsecuregcm.configuration.secrets.SecretStringList$ValidatorNotEmpty</value>
|
||||||
|
<value>org.whispersystems.textsecuregcm.configuration.secrets.SecretBytesList$ValidatorNotEmpty</value>
|
||||||
|
</validated-by>
|
||||||
|
</constraint-definition>
|
||||||
|
</constraint-mappings>
|
||||||
|
|
|
@ -15,19 +15,27 @@ import java.util.Arrays;
|
||||||
*/
|
*/
|
||||||
public class CheckServiceConfigurations {
|
public class CheckServiceConfigurations {
|
||||||
|
|
||||||
|
private static final String SECRETS_BUNDLE_FILENAME = "sample-secrets-bundle.yml";
|
||||||
|
|
||||||
private void checkConfiguration(final File configDirectory) {
|
private void checkConfiguration(final File configDirectory) {
|
||||||
|
|
||||||
final File[] configFiles = configDirectory.listFiles(f ->
|
final File[] configFiles = configDirectory.listFiles(f ->
|
||||||
!f.isDirectory()
|
!f.isDirectory()
|
||||||
&& f.getPath().endsWith(".yml"));
|
&& f.getPath().endsWith(".yml")
|
||||||
|
&& !f.getPath().endsWith(SECRETS_BUNDLE_FILENAME));
|
||||||
|
|
||||||
if (configFiles == null || configFiles.length == 0) {
|
if (configFiles == null || configFiles.length == 0) {
|
||||||
throw new IllegalArgumentException("No .yml configuration files found at " + configDirectory.getPath());
|
throw new IllegalArgumentException("No .yml configuration files found at " + configDirectory.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (File configFile : configFiles) {
|
final File[] secretsBundle = configDirectory.listFiles(f -> !f.isDirectory() && f.getName().equals(SECRETS_BUNDLE_FILENAME));
|
||||||
String[] args = new String[]{"check", configFile.getAbsolutePath()};
|
if (secretsBundle == null || secretsBundle.length != 1) {
|
||||||
|
throw new IllegalArgumentException("No [%s] file found at %s".formatted(SECRETS_BUNDLE_FILENAME, configDirectory.getPath()));
|
||||||
|
}
|
||||||
|
System.setProperty(WhisperServerService.SECRETS_BUNDLE_FILE_NAME_PROPERTY, secretsBundle[0].getAbsolutePath());
|
||||||
|
|
||||||
|
for (final File configFile : configFiles) {
|
||||||
|
final String[] args = new String[]{"check", configFile.getAbsolutePath()};
|
||||||
try {
|
try {
|
||||||
new WhisperServerService().run(args);
|
new WhisperServerService().run(args);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
|
@ -38,8 +46,7 @@ public class CheckServiceConfigurations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(final String[] args) {
|
||||||
|
|
||||||
if (args.length != 1) {
|
if (args.length != 1) {
|
||||||
throw new IllegalArgumentException("Expected single argument with config directory: " + Arrays.toString(args));
|
throw new IllegalArgumentException("Expected single argument with config directory: " + Arrays.toString(args));
|
||||||
}
|
}
|
||||||
|
@ -52,5 +59,4 @@ public class CheckServiceConfigurations {
|
||||||
|
|
||||||
new CheckServiceConfigurations().checkConfiguration(configDirectory);
|
new CheckServiceConfigurations().checkConfiguration(configDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||||
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
|
|
||||||
|
public class SecretsTest {
|
||||||
|
|
||||||
|
private static final String SECRET_REF = "secret_string";
|
||||||
|
|
||||||
|
private static final String SECRET_LIST_REF = "secret_string_list";
|
||||||
|
|
||||||
|
private static final String SECRET_BYTES_REF = "secret_bytes";
|
||||||
|
|
||||||
|
private static final String SECRET_BYTES_LIST_REF = "secret_bytes_list";
|
||||||
|
|
||||||
|
public record TestData(SecretString secret,
|
||||||
|
SecretBytes secretBytes,
|
||||||
|
SecretStringList secretList,
|
||||||
|
SecretBytesList secretBytesList) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String VALID_CONFIG_YAML = """
|
||||||
|
secret: secret://%s
|
||||||
|
secretBytes: secret://%s
|
||||||
|
secretList: secret://%s
|
||||||
|
secretBytesList: secret://%s
|
||||||
|
""".formatted(SECRET_REF, SECRET_BYTES_REF, SECRET_LIST_REF, SECRET_BYTES_LIST_REF);
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeserialization() throws Exception {
|
||||||
|
final String secretString = "secret_string";
|
||||||
|
final byte[] secretBytes = RandomUtils.nextBytes(16);
|
||||||
|
final String secretBytesBase64 = Base64.getEncoder().encodeToString(secretBytes);
|
||||||
|
final List<String> secretStringList = List.of("secret1", "secret2", "secret3");
|
||||||
|
final List<byte[]> secretBytesList = List.of(RandomUtils.nextBytes(16), RandomUtils.nextBytes(16), RandomUtils.nextBytes(16));
|
||||||
|
final List<String> secretBytesListBase64 = secretBytesList.stream().map(Base64.getEncoder()::encodeToString).toList();
|
||||||
|
final Map<String, Secret<?>> storeMap = Map.of(
|
||||||
|
SECRET_REF, new SecretString(secretString),
|
||||||
|
SECRET_BYTES_REF, new SecretString(secretBytesBase64),
|
||||||
|
SECRET_LIST_REF, new SecretStringList(secretStringList),
|
||||||
|
SECRET_BYTES_LIST_REF, new SecretStringList(secretBytesListBase64)
|
||||||
|
);
|
||||||
|
SecretsModule.INSTANCE.setSecretStore(new SecretStore(storeMap));
|
||||||
|
|
||||||
|
final TestData result = SystemMapper.yamlMapper().readValue(VALID_CONFIG_YAML, TestData.class);
|
||||||
|
assertEquals(secretString, result.secret().value());
|
||||||
|
assertEquals(secretStringList, result.secretList().value());
|
||||||
|
assertArrayEquals(secretBytes, result.secretBytes().value());
|
||||||
|
for (int i = 0; i < secretBytesList.size(); i++) {
|
||||||
|
assertArrayEquals(secretBytesList.get(i), result.secretBytesList().value().get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValueWithoutPrefix() throws Exception {
|
||||||
|
final String config = """
|
||||||
|
secret: ref
|
||||||
|
""";
|
||||||
|
SecretsModule.INSTANCE.setSecretStore(new SecretStore(Collections.emptyMap()));
|
||||||
|
assertThrows(JsonMappingException.class, () -> SystemMapper.yamlMapper().readValue(config, TestData.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoSecretInTheStore() throws Exception {
|
||||||
|
final String config = """
|
||||||
|
secret: secret://missing
|
||||||
|
secretBytes: secret://missing
|
||||||
|
secretList: secret://missing
|
||||||
|
secretBytesList: secret://missing
|
||||||
|
""";
|
||||||
|
SecretsModule.INSTANCE.setSecretStore(new SecretStore(Collections.emptyMap()));
|
||||||
|
assertThrows(JsonMappingException.class, () -> SystemMapper.yamlMapper().readValue(config, TestData.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecretStoreNotSet() throws Exception {
|
||||||
|
assertThrows(JsonMappingException.class, () -> SystemMapper.yamlMapper().readValue(VALID_CONFIG_YAML, TestData.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadFromJson() throws Exception {
|
||||||
|
// checking that valid json secrets bundle is read correctly
|
||||||
|
final SecretStore secretStore = SecretStore.fromYamlStringSecretsBundle("""
|
||||||
|
secret_string: value
|
||||||
|
secret_string_list:
|
||||||
|
- value1
|
||||||
|
- value2
|
||||||
|
- value3
|
||||||
|
""");
|
||||||
|
assertEquals("value", secretStore.secretString("secret_string").value());
|
||||||
|
assertEquals(List.of("value1", "value2", "value3"), secretStore.secretStringList("secret_string_list").value());
|
||||||
|
|
||||||
|
// checking that secrets bundle can't have objects as values
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> SecretStore.fromYamlStringSecretsBundle("""
|
||||||
|
secret_string: value
|
||||||
|
not_a_string_or_list:
|
||||||
|
k: v
|
||||||
|
"""));
|
||||||
|
|
||||||
|
// checking that secrets bundle can't have numbers as values
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> SecretStore.fromYamlStringSecretsBundle("""
|
||||||
|
secret_string: value
|
||||||
|
not_a_string_or_list: 42
|
||||||
|
"""));
|
||||||
|
}
|
||||||
|
|
||||||
|
record NotEmptySecretStringList(@NotEmpty SecretStringList secret) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record NotEmptySecretBytesList(@NotEmpty SecretBytesList secret) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record ExactlySizeBytesSecret(@ExactlySize(32) SecretBytes secret) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidators() throws Exception {
|
||||||
|
final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||||
|
|
||||||
|
// @NotEmpty SecretStringList
|
||||||
|
assertFalse(validator.validate(new NotEmptySecretStringList(new SecretStringList(List.of()))).isEmpty());
|
||||||
|
assertTrue(validator.validate(new NotEmptySecretStringList(new SecretStringList(List.of("smth")))).isEmpty());
|
||||||
|
|
||||||
|
// @NotEmpty SecretBytesList
|
||||||
|
assertFalse(validator.validate(new NotEmptySecretBytesList(new SecretBytesList(List.of()))).isEmpty());
|
||||||
|
assertTrue(validator.validate(new NotEmptySecretBytesList(new SecretBytesList(List.of(new byte[4])))).isEmpty());
|
||||||
|
|
||||||
|
// @ExactlySize SecretBytes
|
||||||
|
assertFalse(validator.validate(new ExactlySizeBytesSecret(new SecretBytes(new byte[16]))).isEmpty());
|
||||||
|
assertTrue(validator.validate(new ExactlySizeBytesSecret(new SecretBytes(new byte[32]))).isEmpty());
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.net.HttpHeaders;
|
import com.google.common.net.HttpHeaders;
|
||||||
|
@ -72,7 +73,6 @@ import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||||
import org.signal.libsignal.usernames.BaseUsernameException;
|
import org.signal.libsignal.usernames.BaseUsernameException;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
|
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
|
||||||
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
|
@ -99,7 +99,6 @@ import org.whispersystems.textsecuregcm.entities.RegistrationLock;
|
||||||
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
|
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
|
||||||
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashRequest;
|
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashResponse;
|
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashResponse;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
|
||||||
import org.whispersystems.textsecuregcm.entities.UsernameHashResponse;
|
import org.whispersystems.textsecuregcm.entities.UsernameHashResponse;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimitByIpFilter;
|
import org.whispersystems.textsecuregcm.limits.RateLimitByIpFilter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
|
@ -203,13 +202,13 @@ class AccountControllerTest {
|
||||||
|
|
||||||
private static final SecureBackupServiceConfiguration SVR1_CFG = MockUtils.buildMock(
|
private static final SecureBackupServiceConfiguration SVR1_CFG = MockUtils.buildMock(
|
||||||
SecureBackupServiceConfiguration.class,
|
SecureBackupServiceConfiguration.class,
|
||||||
cfg -> when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]));
|
cfg -> when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(randomSecretBytes(32)));
|
||||||
|
|
||||||
private static final SecureValueRecovery2Configuration SVR2_CFG = MockUtils.buildMock(
|
private static final SecureValueRecovery2Configuration SVR2_CFG = MockUtils.buildMock(
|
||||||
SecureValueRecovery2Configuration.class,
|
SecureValueRecovery2Configuration.class,
|
||||||
cfg -> {
|
cfg -> {
|
||||||
when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(new byte[32]);
|
when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(randomSecretBytes(32));
|
||||||
when(cfg.userIdTokenSharedSecret()).thenReturn(new byte[32]);
|
when(cfg.userIdTokenSharedSecret()).thenReturn(randomSecretBytes(32));
|
||||||
});
|
});
|
||||||
|
|
||||||
private static final ExternalServiceCredentialsGenerator svr1CredentialsGenerator = SecureBackupController.credentialsGenerator(
|
private static final ExternalServiceCredentialsGenerator svr1CredentialsGenerator = SecureBackupController.credentialsGenerator(
|
||||||
|
|
|
@ -5,32 +5,17 @@
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.controllers;
|
package org.whispersystems.textsecuregcm.controllers;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import javax.ws.rs.client.Entity;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import org.apache.commons.lang3.RandomUtils;
|
|
||||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckResponse;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
import org.whispersystems.textsecuregcm.util.MockUtils;
|
import org.whispersystems.textsecuregcm.util.MockUtils;
|
||||||
|
@ -40,11 +25,11 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||||
class SecureBackupControllerTest extends SecureValueRecoveryControllerBaseTest {
|
class SecureBackupControllerTest extends SecureValueRecoveryControllerBaseTest {
|
||||||
|
|
||||||
private static final byte[] SECRET = RandomUtils.nextBytes(32);
|
private static final SecretBytes SECRET = randomSecretBytes(32);
|
||||||
|
|
||||||
private static final SecureBackupServiceConfiguration CFG = MockUtils.buildMock(
|
private static final SecureBackupServiceConfiguration CFG = MockUtils.buildMock(
|
||||||
SecureBackupServiceConfiguration.class,
|
SecureBackupServiceConfiguration.class,
|
||||||
cfg -> Mockito.when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(SECRET)
|
cfg -> Mockito.when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(SECRET)
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final MutableClock CLOCK = new MutableClock();
|
private static final MutableClock CLOCK = new MutableClock();
|
||||||
|
|
|
@ -7,10 +7,10 @@ package org.whispersystems.textsecuregcm.controllers;
|
||||||
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||||
import org.apache.commons.lang3.RandomUtils;
|
|
||||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
|
@ -26,8 +26,8 @@ public class SecureValueRecovery2ControllerTest extends SecureValueRecoveryContr
|
||||||
private static final SecureValueRecovery2Configuration CFG = new SecureValueRecovery2Configuration(
|
private static final SecureValueRecovery2Configuration CFG = new SecureValueRecovery2Configuration(
|
||||||
true,
|
true,
|
||||||
"",
|
"",
|
||||||
RandomUtils.nextBytes(32),
|
randomSecretBytes(32),
|
||||||
RandomUtils.nextBytes(32),
|
randomSecretBytes(32),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
|
|
|
@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
@ -53,7 +54,7 @@ class SecureStorageClientTest {
|
||||||
httpExecutor = Executors.newSingleThreadExecutor();
|
httpExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
final SecureStorageServiceConfiguration config = new SecureStorageServiceConfiguration(
|
final SecureStorageServiceConfiguration config = new SecureStorageServiceConfiguration(
|
||||||
"not_used",
|
randomSecretBytes(32),
|
||||||
"http://localhost:" + wireMock.getPort(),
|
"http://localhost:" + wireMock.getPort(),
|
||||||
List.of("""
|
List.of("""
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
|
|
|
@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
@ -53,7 +54,8 @@ class SecureValueRecovery2ClientTest {
|
||||||
|
|
||||||
final SecureValueRecovery2Configuration config = new SecureValueRecovery2Configuration(true,
|
final SecureValueRecovery2Configuration config = new SecureValueRecovery2Configuration(true,
|
||||||
"http://localhost:" + wireMock.getPort(),
|
"http://localhost:" + wireMock.getPort(),
|
||||||
new byte[0], new byte[0],
|
randomSecretBytes(32),
|
||||||
|
randomSecretBytes(32),
|
||||||
// This is a randomly-generated, throwaway certificate that's not actually connected to anything
|
// This is a randomly-generated, throwaway certificate that's not actually connected to anything
|
||||||
List.of("""
|
List.of("""
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
|
|
|
@ -8,15 +8,16 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||||
|
import java.time.Duration;
|
||||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
|
@ -26,18 +27,12 @@ import org.whispersystems.textsecuregcm.controllers.ArtController;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
import org.whispersystems.textsecuregcm.util.MockUtils;
|
|
||||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
|
|
||||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||||
class ArtControllerTest {
|
class ArtControllerTest {
|
||||||
|
private static final ArtServiceConfiguration ART_SERVICE_CONFIGURATION = new ArtServiceConfiguration(
|
||||||
private static final ArtServiceConfiguration ART_SERVICE_CONFIGURATION = MockUtils.buildMock(
|
randomSecretBytes(32), randomSecretBytes(32), Duration.ofDays(1));
|
||||||
ArtServiceConfiguration.class,
|
|
||||||
cfg -> {
|
|
||||||
Mockito.when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]);
|
|
||||||
Mockito.when(cfg.getUserAuthenticationTokenUserIdSecret()).thenReturn(new byte[32]);
|
|
||||||
});
|
|
||||||
private static final ExternalServiceCredentialsGenerator artCredentialsGenerator = ArtController.credentialsGenerator(ART_SERVICE_CONFIGURATION);
|
private static final ExternalServiceCredentialsGenerator artCredentialsGenerator = ArtController.credentialsGenerator(ART_SERVICE_CONFIGURATION);
|
||||||
private static final RateLimiter rateLimiter = mock(RateLimiter.class);
|
private static final RateLimiter rateLimiter = mock(RateLimiter.class);
|
||||||
private static final RateLimiters rateLimiters = mock(RateLimiters.class);
|
private static final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.secretBytesOf;
|
||||||
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
@ -28,7 +29,7 @@ class DirectoryControllerV2Test {
|
||||||
@Test
|
@Test
|
||||||
void testAuthToken() {
|
void testAuthToken() {
|
||||||
final ExternalServiceCredentialsGenerator credentialsGenerator = DirectoryV2Controller.credentialsGenerator(
|
final ExternalServiceCredentialsGenerator credentialsGenerator = DirectoryV2Controller.credentialsGenerator(
|
||||||
new DirectoryV2ClientConfiguration(new byte[]{0x1}, new byte[]{0x2}),
|
new DirectoryV2ClientConfiguration(secretBytesOf(0x01), secretBytesOf(0x02)),
|
||||||
Clock.fixed(Instant.ofEpochSecond(1633738643L), ZoneId.of("Etc/UTC"))
|
Clock.fixed(Instant.ofEpochSecond(1633738643L), ZoneId.of("Etc/UTC"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
||||||
|
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||||
|
@ -31,7 +32,7 @@ class SecureStorageControllerTest {
|
||||||
|
|
||||||
private static final SecureStorageServiceConfiguration STORAGE_CFG = MockUtils.buildMock(
|
private static final SecureStorageServiceConfiguration STORAGE_CFG = MockUtils.buildMock(
|
||||||
SecureStorageServiceConfiguration.class,
|
SecureStorageServiceConfiguration.class,
|
||||||
cfg -> when(cfg.decodeUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]));
|
cfg -> when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(randomSecretBytes(32)));
|
||||||
|
|
||||||
private static final ExternalServiceCredentialsGenerator STORAGE_CREDENTIAL_GENERATOR = SecureStorageController
|
private static final ExternalServiceCredentialsGenerator STORAGE_CREDENTIAL_GENERATOR = SecureStorageController
|
||||||
.credentialsGenerator(STORAGE_CFG);
|
.credentialsGenerator(STORAGE_CFG);
|
||||||
|
|
|
@ -12,7 +12,9 @@ import static org.mockito.Mockito.doThrow;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
|
@ -70,4 +72,16 @@ public final class MockUtils {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SecretBytes randomSecretBytes(final int size) {
|
||||||
|
return new SecretBytes(RandomUtils.nextBytes(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SecretBytes secretBytesOf(final int... byteVals) {
|
||||||
|
final byte[] bytes = new byte[byteVals.length];
|
||||||
|
for (int i = 0; i < byteVals.length; i++) {
|
||||||
|
bytes[i] = (byte) byteVals[i];
|
||||||
|
}
|
||||||
|
return new SecretBytes(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue