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.
|
||||
# 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:
|
||||
credentials: |
|
||||
Some credentials text
|
||||
blah blah blah
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
projectId: some-project-id
|
||||
logName: some-log-name
|
||||
|
||||
stripe:
|
||||
apiKey: unset
|
||||
idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
|
||||
apiKey: secret://stripe.apiKey
|
||||
idempotencyKeyGenerator: secret://stripe.idempotencyKeyGenerator
|
||||
boostDescription: >
|
||||
Example
|
||||
supportedCurrencies:
|
||||
|
@ -24,7 +61,7 @@ stripe:
|
|||
braintree:
|
||||
merchantId: unset
|
||||
publicKey: unset
|
||||
privateKey: unset
|
||||
privateKey: secret://braintree.privateKey
|
||||
environment: unset
|
||||
graphqlUrl: unset
|
||||
merchantAccounts:
|
||||
|
@ -104,14 +141,14 @@ rateLimitersCluster: # Redis server configuration for rate limiters cluster
|
|||
|
||||
directoryV2:
|
||||
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
|
||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth identity tokens for Signal users
|
||||
userAuthenticationTokenSharedSecret: secret://directoryV2.client.userAuthenticationTokenSharedSecret
|
||||
userIdTokenSharedSecret: secret://directoryV2.client.userIdTokenSharedSecret
|
||||
|
||||
svr2:
|
||||
enabled: false
|
||||
uri: svr2.example.com
|
||||
userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
||||
userAuthenticationTokenSharedSecret: secret://svr2.userAuthenticationTokenSharedSecret
|
||||
userIdTokenSharedSecret: secret://svr2.userIdTokenSharedSecret
|
||||
svrCaCertificates:
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
|
@ -146,8 +183,8 @@ metricsCluster:
|
|||
configurationUri: redis://redis.example.com:6379/
|
||||
|
||||
awsAttachments: # AWS S3 configuration
|
||||
accessKey: test
|
||||
accessSecret: test
|
||||
accessKey: secret://awsAttachments.accessKey
|
||||
accessSecret: secret://awsAttachments.accessSecret
|
||||
bucket: aws-attachments
|
||||
region: us-west-2
|
||||
|
||||
|
@ -156,35 +193,7 @@ gcpAttachments: # GCP Storage configuration
|
|||
email: user@example.cocm
|
||||
maxSizeInBytes: 1024
|
||||
pathPrefix:
|
||||
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-----
|
||||
rsaSigningKey: secret://gcpAttachments.rsaSigningKey
|
||||
|
||||
accountDatabaseCrawler:
|
||||
chunkSize: 10 # accounts per run
|
||||
|
@ -194,31 +203,24 @@ apn: # Apple Push Notifications configuration
|
|||
bundleId: com.example.textsecuregcm
|
||||
keyId: unset
|
||||
teamId: unset
|
||||
signingKey: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
AAAAAAAA
|
||||
-----END PRIVATE KEY-----
|
||||
signingKey: secret://apn.signingKey
|
||||
|
||||
fcm: # FCM configuration
|
||||
credentials: |
|
||||
{ "json": true }
|
||||
credentials: secret://fcm.credentials
|
||||
|
||||
cdn:
|
||||
accessKey: test # AWS Access Key ID
|
||||
accessSecret: test # AWS Access Secret
|
||||
accessKey: secret://cdn.accessKey
|
||||
accessSecret: secret://cdn.accessSecret
|
||||
bucket: cdn # S3 Bucket name
|
||||
region: us-west-2 # AWS region
|
||||
|
||||
datadog:
|
||||
apiKey: unset
|
||||
apiKey: secret://datadog.apiKey
|
||||
environment: dev
|
||||
|
||||
unidentifiedDelivery:
|
||||
certificate: ABCD1234
|
||||
privateKey: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
|
||||
certificate: secret://unidentifiedDelivery.certificate
|
||||
privateKey: secret://unidentifiedDelivery.privateKey
|
||||
expiresDays: 7
|
||||
|
||||
recaptcha:
|
||||
|
@ -226,11 +228,11 @@ recaptcha:
|
|||
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
|
||||
|
||||
hCaptcha:
|
||||
apiKey: unset
|
||||
apiKey: secret://hCaptcha.apiKey
|
||||
|
||||
storageService:
|
||||
uri: storage.example.com
|
||||
userAuthenticationTokenSharedSecret: 00000f
|
||||
userAuthenticationTokenSharedSecret: secret://storageService.userAuthenticationTokenSharedSecret
|
||||
storageCaCertificates:
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
|
@ -257,7 +259,7 @@ storageService:
|
|||
|
||||
backupService:
|
||||
uri: backup.example.com
|
||||
userAuthenticationTokenSharedSecret: 00000f
|
||||
userAuthenticationTokenSharedSecret: secret://backupService.userAuthenticationTokenSharedSecret
|
||||
backupCaCertificates:
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
|
@ -284,10 +286,10 @@ backupService:
|
|||
|
||||
zkConfig:
|
||||
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:
|
||||
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:
|
||||
application: example
|
||||
|
@ -295,18 +297,14 @@ appConfig:
|
|||
configuration: example
|
||||
|
||||
remoteConfig:
|
||||
authorizedTokens:
|
||||
- # 1st authorized token
|
||||
- # 2nd authorized token
|
||||
- # ...
|
||||
- # Nth authorized token
|
||||
authorizedTokens: secret://remoteConfig.authorizedTokens
|
||||
globalConfig: # keys and values that are given to clients on GET /v1/config
|
||||
EXAMPLE_KEY: VALUE
|
||||
|
||||
paymentsService:
|
||||
userAuthenticationTokenSharedSecret: 0000000f0000000f0000000f0000000f0000000f0000000f0000000f0000000f # hex-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
|
||||
fixerApiKey: unset
|
||||
coinMarketCapApiKey: unset
|
||||
userAuthenticationTokenSharedSecret: secret://paymentsService.userAuthenticationTokenSharedSecret
|
||||
fixerApiKey: secret://paymentsService.fixerApiKey
|
||||
coinMarketCapApiKey: secret://paymentsService.coinMarketCapApiKey
|
||||
coinMarketCapCurrencyIds:
|
||||
MOB: 7878
|
||||
paymentCurrencies:
|
||||
|
@ -314,8 +312,8 @@ paymentsService:
|
|||
- MOB
|
||||
|
||||
artService:
|
||||
userAuthenticationTokenSharedSecret: 0000000f0000000f0000000f0000000f0000000f0000000f0000000f0000000f # hex-encoded 32-byte secret not shared with any external service, but used in ArtController
|
||||
userAuthenticationTokenUserIdSecret: 00000f # hex-encoded secret to obscure user phone numbers from Sticker Creator
|
||||
userAuthenticationTokenSharedSecret: secret://artService.userAuthenticationTokenSharedSecret
|
||||
userAuthenticationTokenUserIdSecret: secret://artService.userAuthenticationTokenUserIdSecret
|
||||
|
||||
badges:
|
||||
badges:
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package org.whispersystems.textsecuregcm;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.amazonaws.ClientConfiguration;
|
||||
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
|
||||
|
@ -81,6 +82,8 @@ import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
|||
import org.whispersystems.textsecuregcm.captcha.RecaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretStore;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretsModule;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountControllerV2;
|
||||
import org.whispersystems.textsecuregcm.controllers.ArtController;
|
||||
|
@ -236,8 +239,24 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(WhisperServerService.class);
|
||||
|
||||
public static final String SECRETS_BUNDLE_FILE_NAME_PROPERTY = "secrets.bundle.filename";
|
||||
|
||||
private static final software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER =
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create();
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(Bootstrap<WhisperServerConfiguration> bootstrap) {
|
||||
public void initialize(final Bootstrap<WhisperServerConfiguration> bootstrap) {
|
||||
// `SecretStore` needs to be initialized before Dropwizard reads the main application config file.
|
||||
final String secretsBundleFileName = requireNonNull(
|
||||
System.getProperty(SECRETS_BUNDLE_FILE_NAME_PROPERTY),
|
||||
"Application requires property [%s] to be provided".formatted(SECRETS_BUNDLE_FILE_NAME_PROPERTY));
|
||||
final SecretStore secretStore = SecretStore.fromYamlFileSecretsBundle(secretsBundleFileName);
|
||||
SecretsModule.INSTANCE.setSecretStore(secretStore);
|
||||
|
||||
// Initializing SystemMapper here because parsing of the main application config happens before `run()` method is called.
|
||||
SystemMapper.configureMapper(bootstrap.getObjectMapper());
|
||||
|
||||
bootstrap.addCommand(new DeleteUserCommand());
|
||||
bootstrap.addCommand(new CertificateCommand());
|
||||
bootstrap.addCommand(new ZkParamsCommand());
|
||||
|
@ -289,8 +308,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
|
||||
environment.lifecycle().manage(new MicrometerRegistryManager(Metrics.globalRegistry));
|
||||
|
||||
SystemMapper.configureMapper(environment.getObjectMapper());
|
||||
|
||||
HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup =
|
||||
new HeaderControlledResourceBundleLookup();
|
||||
ConfiguredProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter(
|
||||
|
@ -300,11 +317,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
||||
config.getDynamoDbClientConfiguration(),
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||
AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER);
|
||||
|
||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
||||
config.getDynamoDbClientConfiguration(),
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||
AWSSDK_INSTANCE_PROFILE_CREDENTIALS_PROVIDER);
|
||||
|
||||
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
|
||||
.withRegion(config.getDynamoDbClientConfiguration().getRegion())
|
||||
|
@ -446,11 +463,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
config.getAdminEventLoggingConfiguration().projectId(),
|
||||
config.getAdminEventLoggingConfiguration().logName());
|
||||
|
||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey(), subscriptionProcessorExecutor,
|
||||
config.getStripe().idempotencyKeyGenerator(), config.getStripe().boostDescription(), config.getStripe()
|
||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey().value(), subscriptionProcessorExecutor,
|
||||
config.getStripe().idempotencyKeyGenerator().value(), config.getStripe().boostDescription(), config.getStripe()
|
||||
.supportedCurrencies());
|
||||
BraintreeManager braintreeManager = new BraintreeManager(config.getBraintree().merchantId(),
|
||||
config.getBraintree().publicKey(), config.getBraintree().privateKey(), config.getBraintree().environment(),
|
||||
config.getBraintree().publicKey(), config.getBraintree().privateKey().value(), config.getBraintree().environment(),
|
||||
config.getBraintree().supportedCurrencies(), config.getBraintree().merchantAccounts(),
|
||||
config.getBraintree().graphqlUrl(), config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor);
|
||||
|
||||
|
@ -506,9 +523,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
deletedAccountsManager, keys, messagesManager, profilesManager,
|
||||
pendingAccountsManager, secureStorageClient, secureBackupClient, secureValueRecovery2Client, clientPresenceManager,
|
||||
experimentEnrollmentManager, registrationRecoveryPasswordsManager, clock);
|
||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
|
||||
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials());
|
||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
|
||||
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials().value());
|
||||
ApnPushNotificationScheduler apnPushNotificationScheduler = new ApnPushNotificationScheduler(pushSchedulerCluster,
|
||||
apnSender, accountsManager);
|
||||
PushNotificationManager pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender,
|
||||
|
@ -553,7 +570,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
|
||||
dynamicConfigurationManager);
|
||||
HttpClient hcaptchaHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||
HCaptchaClient hCaptchaClient = new HCaptchaClient(config.getHCaptchaConfiguration().apiKey(), hcaptchaHttpClient, dynamicConfigurationManager);
|
||||
HCaptchaClient hCaptchaClient = new HCaptchaClient(config.getHCaptchaConfiguration().apiKey().value(), hcaptchaHttpClient, dynamicConfigurationManager);
|
||||
CaptchaChecker captchaChecker = new CaptchaChecker(List.of(recaptchaClient, hCaptchaClient));
|
||||
|
||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(pushNotificationManager, pushChallengeDynamoDb);
|
||||
|
@ -589,10 +606,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
);
|
||||
|
||||
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().getFixerApiKey());
|
||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().getCoinMarketCapApiKey(), config.getPaymentsServiceConfiguration().getCoinMarketCapCurrencyIds());
|
||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().fixerApiKey().value());
|
||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().coinMarketCapApiKey().value(), config.getPaymentsServiceConfiguration().coinMarketCapCurrencyIds());
|
||||
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, coinMarketCapClient,
|
||||
cacheCluster, config.getPaymentsServiceConfiguration().getPaymentCurrencies(), Clock.systemUTC());
|
||||
cacheCluster, config.getPaymentsServiceConfiguration().paymentCurrencies(), Clock.systemUTC());
|
||||
|
||||
environment.lifecycle().manage(apnSender);
|
||||
environment.lifecycle().manage(apnPushNotificationScheduler);
|
||||
|
@ -610,19 +627,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
|
||||
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
|
||||
.create(AwsBasicCredentials.create(
|
||||
config.getCdnConfiguration().getAccessKey(),
|
||||
config.getCdnConfiguration().getAccessSecret()));
|
||||
config.getCdnConfiguration().accessKey().value(),
|
||||
config.getCdnConfiguration().accessSecret().value()));
|
||||
S3Client cdnS3Client = S3Client.builder()
|
||||
.credentialsProvider(cdnCredentialsProvider)
|
||||
.region(Region.of(config.getCdnConfiguration().getRegion()))
|
||||
.region(Region.of(config.getCdnConfiguration().region()))
|
||||
.build();
|
||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().getRegion(),
|
||||
config.getCdnConfiguration().getBucket(), config.getCdnConfiguration().getAccessKey());
|
||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().getAccessSecret(),
|
||||
config.getCdnConfiguration().getRegion());
|
||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().region(),
|
||||
config.getCdnConfiguration().bucket(), config.getCdnConfiguration().accessKey().value());
|
||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().accessSecret().value(),
|
||||
config.getCdnConfiguration().region());
|
||||
|
||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().getServerSecret());
|
||||
GenericServerSecretParams genericZkSecretParams = new GenericServerSecretParams(config.getGenericZkConfig().serverSecret());
|
||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().serverSecret().value());
|
||||
GenericServerSecretParams genericZkSecretParams = new GenericServerSecretParams(config.getGenericZkConfig().serverSecret().value());
|
||||
ServerZkProfileOperations zkProfileOperations = new ServerZkProfileOperations(zkSecretParams);
|
||||
ServerZkAuthOperations zkAuthOperations = new ServerZkAuthOperations(zkSecretParams);
|
||||
ServerZkReceiptOperations zkReceiptOperations = new ServerZkReceiptOperations(zkSecretParams);
|
||||
|
@ -720,10 +737,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
|
||||
registrationLockVerificationManager, rateLimiters),
|
||||
new ArtController(rateLimiters, artCredentialsGenerator),
|
||||
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getRegion(), config.getAwsAttachmentsConfiguration().getBucket()),
|
||||
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()),
|
||||
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().accessKey().value(), config.getAwsAttachmentsConfiguration().accessSecret().value(), config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
|
||||
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().domain(), config.getGcpAttachmentsConfiguration().email(), config.getGcpAttachmentsConfiguration().maxSizeInBytes(), config.getGcpAttachmentsConfiguration().pathPrefix(), config.getGcpAttachmentsConfiguration().rsaSigningKey().value()),
|
||||
new CallLinkController(rateLimiters, genericZkSecretParams),
|
||||
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations, genericZkSecretParams, clock),
|
||||
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().certificate().value(), config.getDeliveryCertificate().ecPrivateKey(), config.getDeliveryCertificate().expiresDays()), zkAuthOperations, genericZkSecretParams, clock),
|
||||
new ChallengeController(rateLimitChallengeManager),
|
||||
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keys, rateLimiters, config.getMaxDevices()),
|
||||
new DirectoryV2Controller(directoryV2CredentialsGenerator),
|
||||
|
@ -735,19 +752,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
||||
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
|
||||
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
|
||||
config.getCdnConfiguration().getBucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
||||
config.getCdnConfiguration().bucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
||||
new ProvisioningController(rateLimiters, provisioningManager),
|
||||
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
|
||||
rateLimiters),
|
||||
new RemoteConfigController(remoteConfigsManager, adminEventLogger,
|
||||
config.getRemoteConfigConfiguration().getAuthorizedTokens(),
|
||||
config.getRemoteConfigConfiguration().getGlobalConfig()),
|
||||
config.getRemoteConfigConfiguration().authorizedTokens().value(),
|
||||
config.getRemoteConfigConfiguration().globalConfig()),
|
||||
new SecureBackupController(backupCredentialsGenerator, accountsManager),
|
||||
new SecureStorageController(storageCredentialsGenerator),
|
||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager, config.getSvr2Configuration()),
|
||||
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
||||
config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(),
|
||||
config.getCdnConfiguration().getBucket()),
|
||||
new StickerController(rateLimiters, config.getCdnConfiguration().accessKey().value(),
|
||||
config.getCdnConfiguration().accessSecret().value(), config.getCdnConfiguration().region(),
|
||||
config.getCdnConfiguration().bucket()),
|
||||
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
||||
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
||||
accountsManager, clock)
|
||||
|
|
|
@ -10,6 +10,7 @@ import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256ToHexString
|
|||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256TruncatedToHexString;
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmacHexStringsEqual;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
@ -17,6 +18,7 @@ import java.util.UUID;
|
|||
import java.util.function.Function;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class ExternalServiceCredentialsGenerator {
|
||||
|
||||
|
@ -40,6 +42,12 @@ public class ExternalServiceCredentialsGenerator {
|
|||
|
||||
private final int derivedUsernameTruncateLength;
|
||||
|
||||
|
||||
public static ExternalServiceCredentialsGenerator.Builder builder(final SecretBytes key) {
|
||||
return builder(key.value());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static ExternalServiceCredentialsGenerator.Builder builder(final byte[] key) {
|
||||
return new Builder(key);
|
||||
}
|
||||
|
@ -240,6 +248,10 @@ public class ExternalServiceCredentialsGenerator {
|
|||
this.key = requireNonNull(key);
|
||||
}
|
||||
|
||||
public Builder withUserDerivationKey(final SecretBytes userDerivationKey) {
|
||||
return withUserDerivationKey(userDerivationKey.value());
|
||||
}
|
||||
|
||||
public Builder withUserDerivationKey(final byte[] userDerivationKey) {
|
||||
Validate.isTrue(requireNonNull(userDerivationKey).length > 0, "userDerivationKey must not be empty");
|
||||
this.userDerivationKey = userDerivationKey;
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public record AdminEventLoggingConfiguration(
|
||||
@NotEmpty String credentials,
|
||||
@NotBlank String credentials,
|
||||
@NotEmpty String projectId,
|
||||
@NotEmpty String logName) {
|
||||
}
|
||||
|
|
|
@ -1,51 +1,17 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
|
||||
public class ApnConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String teamId;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String keyId;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String signingKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bundleId;
|
||||
|
||||
@JsonProperty
|
||||
private boolean sandbox = false;
|
||||
|
||||
public String getTeamId() {
|
||||
return teamId;
|
||||
}
|
||||
|
||||
public String getKeyId() {
|
||||
return keyId;
|
||||
}
|
||||
|
||||
public String getSigningKey() {
|
||||
return signingKey;
|
||||
}
|
||||
|
||||
public String getBundleId() {
|
||||
return bundleId;
|
||||
}
|
||||
|
||||
public boolean isSandboxEnabled() {
|
||||
return sandbox;
|
||||
}
|
||||
public record ApnConfiguration(@NotBlank String teamId,
|
||||
@NotBlank String keyId,
|
||||
@NotNull SecretString signingKey,
|
||||
@NotBlank String bundleId,
|
||||
boolean sandbox) {
|
||||
}
|
||||
|
|
|
@ -5,35 +5,17 @@
|
|||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import static org.apache.commons.lang3.ObjectUtils.firstNonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HexFormat;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public class ArtServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenUserIdSecret;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Duration tokenExpiration = Duration.ofDays(1);
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
|
||||
public byte[] getUserAuthenticationTokenUserIdSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenUserIdSecret);
|
||||
}
|
||||
|
||||
public Duration getTokenExpiration() {
|
||||
return tokenExpiration;
|
||||
public record ArtServiceConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotNull SecretBytes userAuthenticationTokenUserIdSecret,
|
||||
@NotNull Duration tokenExpiration) {
|
||||
public ArtServiceConfiguration {
|
||||
tokenExpiration = firstNonNull(tokenExpiration, Duration.ofDays(1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +1,15 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class AwsAttachmentsConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bucket;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String region;
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public String getAccessSecret() {
|
||||
return accessSecret;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
public record AwsAttachmentsConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import javax.validation.Valid;
|
|||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
/**
|
||||
* @param merchantId the Braintree merchant ID
|
||||
|
@ -24,7 +25,7 @@ import javax.validation.constraints.NotNull;
|
|||
*/
|
||||
public record BraintreeConfiguration(@NotBlank String merchantId,
|
||||
@NotBlank String publicKey,
|
||||
@NotBlank String privateKey,
|
||||
@NotNull SecretString privateKey,
|
||||
@NotBlank String environment,
|
||||
@NotEmpty Set<@NotBlank String> supportedCurrencies,
|
||||
@NotBlank String graphqlUrl,
|
||||
|
|
|
@ -1,44 +1,16 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public class CdnConfiguration {
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bucket;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String region;
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public String getAccessSecret() {
|
||||
return accessSecret;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record CdnConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -7,16 +7,17 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.micrometer.datadog.DatadogConfig;
|
||||
import java.time.Duration;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.Duration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class DatadogConfiguration implements DatadogConfig {
|
||||
|
||||
@JsonProperty
|
||||
@NotBlank
|
||||
private String apiKey;
|
||||
@NotNull
|
||||
private SecretString apiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
|
@ -32,7 +33,7 @@ public class DatadogConfiguration implements DatadogConfig {
|
|||
|
||||
@Override
|
||||
public String apiKey() {
|
||||
return apiKey;
|
||||
return apiKey.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* Copyright 2013-2023 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public record DirectoryV2ClientConfiguration(@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret) {
|
||||
public record DirectoryV2ClientConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userIdTokenSharedSecret) {
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* Copyright 2013-2022 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record FcmConfiguration(@NotBlank String credentials) {
|
||||
public record FcmConfiguration(@NotNull SecretString credentials) {
|
||||
}
|
||||
|
|
|
@ -1,57 +1,22 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.dropwizard.util.Strings;
|
||||
import io.dropwizard.validation.ValidationMethod;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public class GcpAttachmentsConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String domain;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String email;
|
||||
|
||||
@JsonProperty
|
||||
@Min(1)
|
||||
private int maxSizeInBytes;
|
||||
|
||||
@JsonProperty
|
||||
private String pathPrefix;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String rsaSigningKey;
|
||||
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public int getMaxSizeInBytes() {
|
||||
return maxSizeInBytes;
|
||||
}
|
||||
|
||||
public String getPathPrefix() {
|
||||
return pathPrefix;
|
||||
}
|
||||
|
||||
public String getRsaSigningKey() {
|
||||
return rsaSigningKey;
|
||||
}
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record GcpAttachmentsConfiguration(@NotBlank String domain,
|
||||
@NotBlank String email,
|
||||
@Min(1) int maxSizeInBytes,
|
||||
String pathPrefix,
|
||||
@NotNull SecretString rsaSigningKey) {
|
||||
@SuppressWarnings("unused")
|
||||
@ValidationMethod(message = "pathPrefix must be empty or start with /")
|
||||
public boolean isPathPrefixValid() {
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public record GenericZkConfig (
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
byte[] serverSecret
|
||||
) {}
|
||||
public record GenericZkConfig(@NotNull SecretBytes serverSecret) {
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* Copyright 2021-2022 Signal Messenger, LLC
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record HCaptchaConfiguration(@NotBlank String apiKey) {
|
||||
public record HCaptchaConfiguration(@NotNull SecretString apiKey) {
|
||||
}
|
||||
|
|
|
@ -1,56 +1,21 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class PaymentsServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotBlank
|
||||
@JsonProperty
|
||||
private String coinMarketCapApiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private Map<@NotBlank String, Integer> coinMarketCapCurrencyIds;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String fixerApiKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private List<String> paymentCurrencies;
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
|
||||
public String getCoinMarketCapApiKey() {
|
||||
return coinMarketCapApiKey;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getCoinMarketCapCurrencyIds() {
|
||||
return coinMarketCapCurrencyIds;
|
||||
}
|
||||
|
||||
public String getFixerApiKey() {
|
||||
return fixerApiKey;
|
||||
}
|
||||
|
||||
public List<String> getPaymentCurrencies() {
|
||||
return paymentCurrencies;
|
||||
}
|
||||
public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotNull SecretString coinMarketCapApiKey,
|
||||
@NotNull SecretString fixerApiKey,
|
||||
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds,
|
||||
@NotEmpty List<String> paymentCurrencies) {
|
||||
}
|
||||
|
|
|
@ -1,33 +1,14 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretStringList;
|
||||
|
||||
public class RemoteConfigConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private List<String> authorizedTokens = new LinkedList<>();
|
||||
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private Map<String, String> globalConfig = new HashMap<>();
|
||||
|
||||
public List<String> getAuthorizedTokens() {
|
||||
return authorizedTokens;
|
||||
}
|
||||
|
||||
public Map<String, String> getGlobalConfig() {
|
||||
return globalConfig;
|
||||
}
|
||||
public record RemoteConfigConfiguration(@NotNull SecretStringList authorizedTokens,
|
||||
@NotNull Map<String, String> globalConfig) {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -7,18 +7,18 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class SecureBackupServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
private SecretBytes userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotBlank
|
||||
@JsonProperty
|
||||
|
@ -38,8 +38,8 @@ public class SecureBackupServiceConfiguration {
|
|||
@JsonProperty
|
||||
private RetryConfiguration retry = new RetryConfiguration();
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
public SecretBytes userAuthenticationTokenSharedSecret() {
|
||||
return userAuthenticationTokenSharedSecret;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
|
@ -5,18 +5,18 @@
|
|||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticationTokenSharedSecret,
|
||||
public record SecureStorageServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotBlank String uri,
|
||||
@NotEmpty List<@NotBlank String> storageCaCertificates,
|
||||
@Valid CircuitBreakerConfiguration circuitBreaker,
|
||||
@Valid RetryConfiguration retry) {
|
||||
|
||||
public SecureStorageServiceConfiguration {
|
||||
if (circuitBreaker == null) {
|
||||
circuitBreaker = new CircuitBreakerConfiguration();
|
||||
|
@ -25,8 +25,4 @@ public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticat
|
|||
retry = new RetryConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decodeUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,14 @@ import javax.validation.Valid;
|
|||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public record SecureValueRecovery2Configuration(
|
||||
boolean enabled,
|
||||
@NotBlank String uri,
|
||||
@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
|
||||
@NotEmpty List<@NotBlank String> svrCaCertificates,
|
||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||
@NotNull @Valid RetryConfiguration retry) {
|
||||
|
|
|
@ -8,10 +8,12 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||
import java.util.Set;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record StripeConfiguration(@NotBlank String apiKey,
|
||||
@NotEmpty byte[] idempotencyKeyGenerator,
|
||||
public record StripeConfiguration(@NotNull SecretString apiKey,
|
||||
@NotNull SecretBytes idempotencyKeyGenerator,
|
||||
@NotBlank String boostDescription,
|
||||
@NotEmpty Set<@NotBlank String> supportedCurrencies) {
|
||||
|
||||
}
|
||||
|
|
|
@ -1,48 +1,21 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class UnidentifiedDeliveryConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] certificate;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
@Size(min = 32, max = 32)
|
||||
private byte[] privateKey;
|
||||
|
||||
@NotNull
|
||||
private int expiresDays;
|
||||
|
||||
public byte[] getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public ECPrivateKey getPrivateKey() throws InvalidKeyException {
|
||||
return Curve.decodePrivatePoint(privateKey);
|
||||
}
|
||||
|
||||
public int getExpiresDays() {
|
||||
return expiresDays;
|
||||
public record UnidentifiedDeliveryConfiguration(@NotNull SecretBytes certificate,
|
||||
@ExactlySize(32) SecretBytes privateKey,
|
||||
int expiresDays) {
|
||||
public ECPrivateKey ecPrivateKey() throws InvalidKeyException {
|
||||
return Curve.decodePrivatePoint(privateKey.value());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +1,14 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class ZkConfig {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] serverSecret;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] serverPublic;
|
||||
|
||||
public byte[] getServerSecret() {
|
||||
return serverSecret;
|
||||
}
|
||||
|
||||
public byte[] getServerPublic() {
|
||||
return serverPublic;
|
||||
}
|
||||
public record ZkConfig(@NotNull SecretBytes serverSecret,
|
||||
@NotEmpty byte[] serverPublic) {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public abstract class BaseSecretValidator<A extends Annotation, T, S extends Secret<? extends T>> implements ConstraintValidator<A, S> {
|
||||
|
||||
private final ConstraintValidator<A, T> validator;
|
||||
|
||||
|
||||
protected BaseSecretValidator(final ConstraintValidator<A, T> validator) {
|
||||
this.validator = requireNonNull(validator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final A constraintAnnotation) {
|
||||
validator.initialize(constraintAnnotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(final S value, final ConstraintValidatorContext context) {
|
||||
return validator.isValid(value.value(), context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
public class Secret<T> {
|
||||
|
||||
private final T value;
|
||||
|
||||
|
||||
public Secret(final T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public T value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[REDACTED]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class SecretBytes extends Secret<byte[]> {
|
||||
|
||||
public SecretBytes(final byte[] value) {
|
||||
super(requireNotEmpty(value));
|
||||
}
|
||||
|
||||
private static byte[] requireNotEmpty(final byte[] value) {
|
||||
Validate.isTrue(value.length > 0, "SecretBytes value must not be empty");
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCollection;
|
||||
|
||||
public class SecretBytesList extends Secret<List<byte[]>> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class ValidatorNotEmpty extends BaseSecretValidator<NotEmpty, Collection, SecretBytesList> {
|
||||
public ValidatorNotEmpty() {
|
||||
super(new NotEmptyValidatorForCollection());
|
||||
}
|
||||
}
|
||||
|
||||
public SecretBytesList(final List<byte[]> value) {
|
||||
super(ImmutableList.copyOf(value));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
public class SecretStore {
|
||||
|
||||
private final Map<String, Secret<?>> secrets;
|
||||
|
||||
|
||||
public static SecretStore fromYamlFileSecretsBundle(final String filename) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> secretsBundle = SystemMapper.yamlMapper().readValue(new File(filename), Map.class);
|
||||
return fromSecretsBundle(secretsBundle);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Failed to parse YAML file [%s]".formatted(filename), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public SecretStore(final Map<String, Secret<?>> secrets) {
|
||||
this.secrets = Map.copyOf(secrets);
|
||||
}
|
||||
|
||||
public SecretString secretString(final String reference) {
|
||||
return fromStore(reference, SecretString.class);
|
||||
}
|
||||
|
||||
public SecretBytes secretBytesFromBase64String(final String reference) {
|
||||
final SecretString secret = fromStore(reference, SecretString.class);
|
||||
return new SecretBytes(decodeBase64(secret.value()));
|
||||
}
|
||||
|
||||
public SecretStringList secretStringList(final String reference) {
|
||||
return fromStore(reference, SecretStringList.class);
|
||||
}
|
||||
|
||||
public SecretBytesList secretBytesListFromBase64Strings(final String reference) {
|
||||
final List<String> secrets = secretStringList(reference).value();
|
||||
final List<byte[]> byteSecrets = secrets.stream().map(SecretStore::decodeBase64).toList();
|
||||
return new SecretBytesList(byteSecrets);
|
||||
}
|
||||
|
||||
private <T extends Secret<?>> T fromStore(final String name, final Class<T> expected) {
|
||||
final Secret<?> secret = secrets.get(name);
|
||||
if (secret == null) {
|
||||
throw new IllegalArgumentException("Secret [%s] is not present in the secrets bundle".formatted(name));
|
||||
}
|
||||
if (!expected.isInstance(secret)) {
|
||||
throw new IllegalArgumentException("Secret [%s] is of type [%s] but caller expects type [%s]".formatted(
|
||||
name, secret.getClass().getSimpleName(), expected.getSimpleName()));
|
||||
}
|
||||
return expected.cast(secret);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static SecretStore fromYamlStringSecretsBundle(final String secretsBundleYaml) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> secretsBundle = SystemMapper.yamlMapper().readValue(secretsBundleYaml, Map.class);
|
||||
return fromSecretsBundle(secretsBundle);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Failed to parse JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretStore fromSecretsBundle(final Map<String, Object> secretsBundle) {
|
||||
final Map<String, Secret<?>> store = new HashMap<>();
|
||||
secretsBundle.forEach((k, v) -> {
|
||||
if (v instanceof final String str) {
|
||||
store.put(k, new SecretString(str));
|
||||
return;
|
||||
}
|
||||
if (v instanceof final List<?> list) {
|
||||
final List<String> secrets = list.stream().map(o -> {
|
||||
if (o instanceof final String s) {
|
||||
return s;
|
||||
}
|
||||
throw new IllegalArgumentException("Secrets bundle JSON object is only supposed to have values of types String and list of Strings");
|
||||
}).toList();
|
||||
store.put(k, new SecretStringList(secrets));
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("Secrets bundle JSON object is only supposed to have values of types String and list of Strings");
|
||||
});
|
||||
return new SecretStore(store);
|
||||
}
|
||||
|
||||
private static byte[] decodeBase64(final String str) {
|
||||
return Base64.getDecoder().decode(str);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class SecretString extends Secret<String> {
|
||||
public SecretString(final String value) {
|
||||
super(Validate.notBlank(value, "SecretString value must not be blank"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCollection;
|
||||
|
||||
public class SecretStringList extends Secret<List<String>> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class ValidatorNotEmpty extends BaseSecretValidator<NotEmpty, Collection, SecretStringList> {
|
||||
public ValidatorNotEmpty() {
|
||||
super(new NotEmptyValidatorForCollection());
|
||||
}
|
||||
}
|
||||
|
||||
public SecretStringList(final List<String> value) {
|
||||
super(ImmutableList.copyOf(value));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class SecretsModule extends SimpleModule {
|
||||
|
||||
public static final SecretsModule INSTANCE = new SecretsModule();
|
||||
|
||||
public static final String PREFIX = "secret://";
|
||||
|
||||
private final AtomicReference<SecretStore> secretStoreHolder = new AtomicReference<>(null);
|
||||
|
||||
|
||||
private SecretsModule() {
|
||||
addDeserializer(SecretString.class, createDeserializer(SecretStore::secretString));
|
||||
addDeserializer(SecretBytes.class, createDeserializer(SecretStore::secretBytesFromBase64String));
|
||||
addDeserializer(SecretStringList.class, createDeserializer(SecretStore::secretStringList));
|
||||
addDeserializer(SecretBytesList.class, createDeserializer(SecretStore::secretBytesListFromBase64Strings));
|
||||
}
|
||||
|
||||
public void setSecretStore(final SecretStore secretStore) {
|
||||
this.secretStoreHolder.set(requireNonNull(secretStore));
|
||||
}
|
||||
|
||||
private <T> JsonDeserializer<T> createDeserializer(final BiFunction<SecretStore, String, T> constructor) {
|
||||
return new JsonDeserializer<>() {
|
||||
@Override
|
||||
public T deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JacksonException {
|
||||
final SecretStore secretStore = secretStoreHolder.get();
|
||||
if (secretStore == null) {
|
||||
throw new IllegalStateException(
|
||||
"An instance of a SecretStore must be set for the SecretsModule via setSecretStore() method");
|
||||
}
|
||||
final String reference = p.getValueAsString();
|
||||
if (!reference.startsWith(PREFIX) || reference.length() <= PREFIX.length()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Value of a secret field must start with a [%s] prefix and refer to an entry in a secrets bundle".formatted(PREFIX));
|
||||
}
|
||||
return constructor.apply(secretStore, reference.substring(PREFIX.length()));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -27,15 +27,15 @@ public class ArtController {
|
|||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final ArtServiceConfiguration cfg) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.getUserAuthenticationTokenUserIdSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.userAuthenticationTokenUserIdSecret())
|
||||
.prependUsername(false)
|
||||
.truncateSignature(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
public ArtController(RateLimiters rateLimiters,
|
||||
ExternalServiceCredentialsGenerator artServiceCredentialsGenerator) {
|
||||
public ArtController(final RateLimiters rateLimiters,
|
||||
final ExternalServiceCredentialsGenerator artServiceCredentialsGenerator) {
|
||||
this.artServiceCredentialsGenerator = artServiceCredentialsGenerator;
|
||||
this.rateLimiters = rateLimiters;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class ArtController {
|
|||
@GET
|
||||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ExternalServiceCredentials getAuth(@Auth AuthenticatedAccount auth)
|
||||
public ExternalServiceCredentials getAuth(final @Auth AuthenticatedAccount auth)
|
||||
throws RateLimitExceededException {
|
||||
final UUID uuid = auth.getAccount().getUuid();
|
||||
rateLimiters.getArtPackLimiter().validate(uuid);
|
||||
|
|
|
@ -41,7 +41,7 @@ public class DirectoryV2Controller {
|
|||
return credentialsGenerator(cfg, Clock.systemUTC());
|
||||
}
|
||||
|
||||
public DirectoryV2Controller(ExternalServiceCredentialsGenerator userTokenGenerator) {
|
||||
public DirectoryV2Controller(final ExternalServiceCredentialsGenerator userTokenGenerator) {
|
||||
this.directoryServiceTokenGenerator = userTokenGenerator;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ public class DirectoryV2Controller {
|
|||
@GET
|
||||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getAuthToken(@Auth AuthenticatedAccount auth) {
|
||||
public Response getAuthToken(final @Auth AuthenticatedAccount auth) {
|
||||
final UUID uuid = auth.getAccount().getUuid();
|
||||
final ExternalServiceCredentials credentials = directoryServiceTokenGenerator.generateForUuid(uuid);
|
||||
return Response.ok().entity(credentials).build();
|
||||
|
|
|
@ -29,7 +29,7 @@ public class PaymentsController {
|
|||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final PaymentsServiceConfiguration cfg) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.AuthCheckResponse;
|
||||
|
@ -56,7 +56,7 @@ public class SecureBackupController {
|
|||
final SecureBackupServiceConfiguration cfg,
|
||||
final Clock clock) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.withClock(clock)
|
||||
.build();
|
||||
|
|
|
@ -25,7 +25,7 @@ public class SecureStorageController {
|
|||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureStorageServiceConfiguration cfg) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.decodeUserAuthenticationTokenSharedSecret())
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,11 @@ import io.dropwizard.auth.Auth;
|
|||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -31,14 +36,6 @@ import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
|
|||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Path("/v2/backup")
|
||||
@Tag(name = "Secure Value Recovery")
|
||||
|
@ -54,7 +51,7 @@ public class SecureValueRecovery2Controller {
|
|||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery2Configuration cfg, final Clock clock) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
||||
.prependUsername(false)
|
||||
.withDerivedUsernameTruncateLength(16)
|
||||
.withClock(clock)
|
||||
|
|
|
@ -22,17 +22,27 @@ import io.dropwizard.logging.filter.LevelFilterFactory;
|
|||
import io.dropwizard.logging.layout.LayoutFactory;
|
||||
import java.time.Duration;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import net.logstash.logback.appender.LogstashTcpSocketAppender;
|
||||
import net.logstash.logback.encoder.LogstashEncoder;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
||||
|
||||
@JsonTypeName("logstashtcpsocket")
|
||||
public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<ILoggingEvent> {
|
||||
|
||||
@JsonProperty
|
||||
private String destination;
|
||||
|
||||
@JsonProperty
|
||||
private Duration keepAlive = Duration.ofSeconds(20);
|
||||
private String apiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private SecretString apiKey;
|
||||
|
||||
@JsonProperty
|
||||
private String environment;
|
||||
|
||||
@JsonProperty
|
||||
|
@ -47,8 +57,7 @@ public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<IL
|
|||
}
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
public String getApiKey() {
|
||||
public SecretString getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
|
@ -84,7 +93,7 @@ public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory<IL
|
|||
encoder.setCustomFields(customFieldsNode.toString());
|
||||
final LayoutWrappingEncoder<ILoggingEvent> prefix = new LayoutWrappingEncoder<>();
|
||||
final PatternLayout layout = new PatternLayout();
|
||||
layout.setPattern(String.format("%s ", apiKey));
|
||||
layout.setPattern(String.format("%s ", apiKey.value()));
|
||||
prefix.setLayout(layout);
|
||||
encoder.setPrefix(prefix);
|
||||
appender.setEncoder(encoder);
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is derived from Coursera's dropwizard datadog reporter.
|
||||
* https://github.com/coursera/metrics-datadog
|
||||
|
@ -10,6 +15,7 @@ import com.codahale.metrics.ScheduledReporter;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.dropwizard.metrics.BaseReporterFactory;
|
||||
import io.dropwizard.util.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
@ -20,8 +26,9 @@ import org.coursera.metrics.datadog.DatadogReporter.Expansion;
|
|||
import org.coursera.metrics.datadog.DefaultMetricNameFormatterFactory;
|
||||
import org.coursera.metrics.datadog.DynamicTagsCallbackFactory;
|
||||
import org.coursera.metrics.datadog.MetricNameFormatterFactory;
|
||||
import org.coursera.metrics.datadog.transport.AbstractTransportFactory;
|
||||
import org.coursera.metrics.datadog.transport.HttpTransport;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
||||
|
||||
@JsonTypeName("signal-datadog")
|
||||
|
@ -44,8 +51,8 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
|||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private AbstractTransportFactory transport = null;
|
||||
@JsonProperty("transport")
|
||||
private HttpTransportConfig httpTransportConfig;
|
||||
|
||||
private static final EnumSet<Expansion> EXPANSIONS = EnumSet.of(
|
||||
Expansion.COUNT,
|
||||
|
@ -59,7 +66,7 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
|||
Expansion.P999
|
||||
);
|
||||
|
||||
public ScheduledReporter build(MetricRegistry registry) {
|
||||
public ScheduledReporter build(final MetricRegistry registry) {
|
||||
final List<String> tagsWithVersion;
|
||||
|
||||
{
|
||||
|
@ -74,7 +81,7 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
|||
}
|
||||
|
||||
return DatadogReporter.forRegistry(registry)
|
||||
.withTransport(transport.build())
|
||||
.withTransport(httpTransportConfig.httpTransport())
|
||||
.withHost(HostnameUtil.getLocalHostname())
|
||||
.withTags(tagsWithVersion)
|
||||
.withPrefix(prefix)
|
||||
|
@ -86,4 +93,26 @@ public class SignalDatadogReporterFactory extends BaseReporterFactory {
|
|||
.convertRatesTo(getRateUnit())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class HttpTransportConfig {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private SecretString apiKey;
|
||||
|
||||
@JsonProperty
|
||||
private Duration connectTimeout = Duration.seconds(5);
|
||||
|
||||
@JsonProperty
|
||||
private Duration socketTimeout = Duration.seconds(5);
|
||||
|
||||
|
||||
public HttpTransport httpTransport() {
|
||||
return new HttpTransport.Builder()
|
||||
.withApiKey(apiKey.value())
|
||||
.withConnectTimeout((int) connectTimeout.toMilliseconds())
|
||||
.withSocketTimeout((int) socketTimeout.toMilliseconds())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.push;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
import com.eatthepath.pushy.apns.ApnsClient;
|
||||
import com.eatthepath.pushy.apns.ApnsClientBuilder;
|
||||
import com.eatthepath.pushy.apns.DeliveryPriority;
|
||||
|
@ -13,6 +15,8 @@ import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder;
|
|||
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
|
@ -21,12 +25,8 @@ import java.time.Duration;
|
|||
import java.time.Instant;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
public class APNSender implements Managed, PushNotificationSender {
|
||||
|
||||
private final ExecutorService executor;
|
||||
|
@ -61,12 +61,12 @@ public class APNSender implements Managed, PushNotificationSender {
|
|||
throws IOException, NoSuchAlgorithmException, InvalidKeyException
|
||||
{
|
||||
this.executor = executor;
|
||||
this.bundleId = configuration.getBundleId();
|
||||
this.bundleId = configuration.bundleId();
|
||||
this.apnsClient = new ApnsClientBuilder().setSigningKey(
|
||||
ApnsSigningKey.loadFromInputStream(new ByteArrayInputStream(configuration.getSigningKey().getBytes()),
|
||||
configuration.getTeamId(), configuration.getKeyId()))
|
||||
ApnsSigningKey.loadFromInputStream(new ByteArrayInputStream(configuration.signingKey().value().getBytes()),
|
||||
configuration.teamId(), configuration.keyId()))
|
||||
.setTrustedServerCertificateChain(getClass().getResourceAsStream(APNS_CA_FILENAME))
|
||||
.setApnsServer(configuration.isSandboxEnabled() ? ApnsClientBuilder.DEVELOPMENT_APNS_HOST : ApnsClientBuilder.PRODUCTION_APNS_HOST)
|
||||
.setApnsServer(configuration.sandbox() ? ApnsClientBuilder.DEVELOPMENT_APNS_HOST : ApnsClientBuilder.PRODUCTION_APNS_HOST)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -25,12 +25,12 @@ import javax.validation.Payload;
|
|||
ExactlySizeValidatorForString.class,
|
||||
ExactlySizeValidatorForArraysOfByte.class,
|
||||
ExactlySizeValidatorForCollection.class,
|
||||
ExactlySizeValidatorForSecretBytes.class,
|
||||
})
|
||||
@Documented
|
||||
public @interface ExactlySize {
|
||||
|
||||
String message() default "{org.whispersystems.textsecuregcm.util.ExactlySize." +
|
||||
"message}";
|
||||
String message() default "{org.whispersystems.textsecuregcm.util.ExactlySize.message}";
|
||||
|
||||
Class<?>[] groups() default { };
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class ExactlySizeValidatorForSecretBytes extends ExactlySizeValidator<SecretBytes> {
|
||||
@Override
|
||||
protected int size(final SecretBytes value) {
|
||||
return value == null ? 0 : value.value().length;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
|
|||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretsModule;
|
||||
|
||||
public class SystemMapper {
|
||||
|
||||
|
@ -37,6 +38,7 @@ public class SystemMapper {
|
|||
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
|
||||
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
|
||||
.registerModules(
|
||||
SecretsModule.INSTANCE,
|
||||
new JavaTimeModule(),
|
||||
new Jdk8Module());
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
private static final String SECRETS_BUNDLE_FILENAME = "sample-secrets-bundle.yml";
|
||||
|
||||
private void checkConfiguration(final File configDirectory) {
|
||||
|
||||
final File[] configFiles = configDirectory.listFiles(f ->
|
||||
!f.isDirectory()
|
||||
&& f.getPath().endsWith(".yml"));
|
||||
&& f.getPath().endsWith(".yml")
|
||||
&& !f.getPath().endsWith(SECRETS_BUNDLE_FILENAME));
|
||||
|
||||
if (configFiles == null || configFiles.length == 0) {
|
||||
throw new IllegalArgumentException("No .yml configuration files found at " + configDirectory.getPath());
|
||||
}
|
||||
|
||||
for (File configFile : configFiles) {
|
||||
String[] args = new String[]{"check", configFile.getAbsolutePath()};
|
||||
final File[] secretsBundle = configDirectory.listFiles(f -> !f.isDirectory() && f.getName().equals(SECRETS_BUNDLE_FILENAME));
|
||||
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 {
|
||||
new WhisperServerService().run(args);
|
||||
} 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) {
|
||||
throw new IllegalArgumentException("Expected single argument with config directory: " + Arrays.toString(args));
|
||||
}
|
||||
|
@ -52,5 +59,4 @@ public class CheckServiceConfigurations {
|
|||
|
||||
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.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
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.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
|
||||
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.ReserveUsernameHashRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||
import org.whispersystems.textsecuregcm.entities.UsernameHashResponse;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimitByIpFilter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
|
@ -203,13 +202,13 @@ class AccountControllerTest {
|
|||
|
||||
private static final SecureBackupServiceConfiguration SVR1_CFG = MockUtils.buildMock(
|
||||
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(
|
||||
SecureValueRecovery2Configuration.class,
|
||||
cfg -> {
|
||||
when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(new byte[32]);
|
||||
when(cfg.userIdTokenSharedSecret()).thenReturn(new byte[32]);
|
||||
when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(randomSecretBytes(32));
|
||||
when(cfg.userIdTokenSharedSecret()).thenReturn(randomSecretBytes(32));
|
||||
});
|
||||
|
||||
private static final ExternalServiceCredentialsGenerator svr1CredentialsGenerator = SecureBackupController.credentialsGenerator(
|
||||
|
|
|
@ -5,32 +5,17 @@
|
|||
|
||||
package org.whispersystems.textsecuregcm.controllers;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
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.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.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.AuthCheckResponse;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.MockUtils;
|
||||
|
@ -40,11 +25,11 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
|
|||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
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(
|
||||
SecureBackupServiceConfiguration.class,
|
||||
cfg -> Mockito.when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(SECRET)
|
||||
cfg -> Mockito.when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(SECRET)
|
||||
);
|
||||
|
||||
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.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||
|
||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
|
@ -26,8 +26,8 @@ public class SecureValueRecovery2ControllerTest extends SecureValueRecoveryContr
|
|||
private static final SecureValueRecovery2Configuration CFG = new SecureValueRecovery2Configuration(
|
||||
true,
|
||||
"",
|
||||
RandomUtils.nextBytes(32),
|
||||
RandomUtils.nextBytes(32),
|
||||
randomSecretBytes(32),
|
||||
randomSecretBytes(32),
|
||||
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.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
||||
import java.security.cert.CertificateException;
|
||||
|
@ -53,7 +54,7 @@ class SecureStorageClientTest {
|
|||
httpExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
final SecureStorageServiceConfiguration config = new SecureStorageServiceConfiguration(
|
||||
"not_used",
|
||||
randomSecretBytes(32),
|
||||
"http://localhost:" + wireMock.getPort(),
|
||||
List.of("""
|
||||
-----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.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
||||
import java.security.cert.CertificateException;
|
||||
|
@ -53,7 +54,8 @@ class SecureValueRecovery2ClientTest {
|
|||
|
||||
final SecureValueRecovery2Configuration config = new SecureValueRecovery2Configuration(true,
|
||||
"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
|
||||
List.of("""
|
||||
-----BEGIN CERTIFICATE-----
|
||||
|
|
|
@ -8,15 +8,16 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||
import java.time.Duration;
|
||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
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.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.MockUtils;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class ArtControllerTest {
|
||||
|
||||
private static final ArtServiceConfiguration ART_SERVICE_CONFIGURATION = MockUtils.buildMock(
|
||||
ArtServiceConfiguration.class,
|
||||
cfg -> {
|
||||
Mockito.when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]);
|
||||
Mockito.when(cfg.getUserAuthenticationTokenUserIdSecret()).thenReturn(new byte[32]);
|
||||
});
|
||||
private static final ArtServiceConfiguration ART_SERVICE_CONFIGURATION = new ArtServiceConfiguration(
|
||||
randomSecretBytes(32), randomSecretBytes(32), Duration.ofDays(1));
|
||||
private static final ExternalServiceCredentialsGenerator artCredentialsGenerator = ArtController.credentialsGenerator(ART_SERVICE_CONFIGURATION);
|
||||
private static final RateLimiter rateLimiter = mock(RateLimiter.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.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.whispersystems.textsecuregcm.util.MockUtils.secretBytesOf;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
|
@ -28,7 +29,7 @@ class DirectoryControllerV2Test {
|
|||
@Test
|
||||
void testAuthToken() {
|
||||
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"))
|
||||
);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
|||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||
|
@ -31,7 +32,7 @@ class SecureStorageControllerTest {
|
|||
|
||||
private static final SecureStorageServiceConfiguration STORAGE_CFG = MockUtils.buildMock(
|
||||
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
|
||||
.credentialsGenerator(STORAGE_CFG);
|
||||
|
|
|
@ -12,7 +12,9 @@ import static org.mockito.Mockito.doThrow;
|
|||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.mockito.Mockito;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
|
@ -70,4 +72,16 @@ public final class MockUtils {
|
|||
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