Compare commits

...

444 Commits

Author SHA1 Message Date
Chris Eager
102992b095 Set off_session=true when creating subscriptions 2022-11-02 11:30:29 -05:00
Chris Eager
bd69905f2e Remove obsolete donation endpoint 2022-11-02 11:29:03 -05:00
Chris Eager
ce5a4bd94a Update wiremock to 2.34.0 2022-11-02 11:24:54 -05:00
Chris Eager
f65a613815 Update jackson to 2.13.4 2022-11-02 11:24:54 -05:00
sergey-signal
d87c8468bd Update to the latest version of the abusive message filter (#1138) 2022-11-02 09:23:38 -07:00
Chris Eager
aa829af43b Handle expected case of empty flux in message deletion 2022-10-31 12:29:25 -05:00
Chris Eager
c10fda8363 Use reactive streams for WebSocket message queue
Initially, uses `ExperimentEnrollmentManager` to do a safe rollout.
2022-10-31 10:35:37 -05:00
Jon Chambers
4252284405 Update to the latest version of the abusive message filter 2022-10-28 10:50:49 -04:00
Jon Chambers
74d65b37a8 Discard old Twilio machinery and rely entirely on the stand-alone registration service 2022-10-28 10:40:37 -04:00
sergey-signal
78f95e4859 Update to the latest version of the abusive message filter (#1132) 2022-10-27 14:01:16 -07:00
Jon Chambers
91626dea45 Count accounts rather than devices that are stories-capable 2022-10-25 16:36:05 -04:00
sergey-signal
5868d9969a minor changes to utility classes (#1127) 2022-10-25 08:48:56 -07:00
erik-signal
90490c9c84 Clean up the TestClock code a bit more. 2022-10-21 15:27:15 -04:00
Chris Eager
8ea794baef Add additional handling for nullable field in recurring donation record 2022-10-21 12:56:39 -05:00
Chris Eager
70a6c3e8e5 Update to libsignal-server 0.21.1 2022-10-21 12:54:18 -05:00
Jon Chambers
4813803c49 Add .java-version to .gitignore 2022-10-21 12:40:11 -04:00
erik-signal
fe60cf003f Clean up testing with clocks. 2022-10-21 12:39:47 -04:00
erik-signal
0c357bc340 Add metrics tracking story capability adoption. 2022-10-20 12:25:03 -04:00
Chris Eager
b711288faa Run GitHub Action in a container 2022-10-18 16:59:35 -05:00
Jon Chambers
44a5d86641 Revert "Update to libsignal-server 0.21.0"
This reverts commit cccccb4dd6.
2022-10-18 11:44:50 -04:00
Jon Chambers
e7048aa9cf Allow the reconciliation client to trust multiple CA certificates to facilitate certificate rotation 2022-10-18 11:17:47 -04:00
Jon Chambers
0120a85c39 Allow HTTP clients to trust multiple certificates to support certificate rollover 2022-10-18 11:17:47 -04:00
Jon Chambers
a41d047f58 Retire CertificateExpirationGauge in favor of other expiration monitoring tools 2022-10-18 11:17:47 -04:00
Chris Eager
cccccb4dd6 Update to libsignal-server 0.21.0 2022-10-18 11:17:29 -04:00
Jon Chambers
0a64e31625 Check verification codes for changing phone numbers against the stand-alone registration service when possible 2022-10-18 11:17:15 -04:00
Jon Chambers
3c6c6c3706 Use the gRPC BOM instead of calling out dependencies individually 2022-10-18 11:16:56 -04:00
Jon Chambers
8088b58b3b Clarify default value for includeE164 2022-10-18 11:16:06 -04:00
erik-signal
a7d5d51fb4 Improve testing of MultiRecipientMessageProvider 2022-10-17 16:50:39 -04:00
Chris Eager
378d7987a8 device capabilities: prevent stories downgrade 2022-10-17 15:25:13 -04:00
erik-signal
3e0baf82a4 Filter unknown UUIDs for /multi_recipient&story=true. 2022-10-13 15:33:51 -04:00
Chris Eager
7a2683a06b Remove /.tx/config from .gitignore 2022-10-11 15:04:50 -05:00
erik-signal
17a3c90286 Add "urgent" query parameter to /v1/messages/multi_recipient endpoint. 2022-10-11 11:10:11 -04:00
Chris Eager
6341770768 Update SubscriptionManager to store processor+customerId in a single attribute and a map
- add `type` query parameter to `/v1/subscription/{subscriberId}/create_payment_method`
2022-10-07 14:26:17 -05:00
Jon Chambers
308437ec93 Resolve gRPC/Netty version conflicts 2022-10-06 16:23:47 -04:00
Jon Chambers
d3d4916d6c Update to the latest version of the abusive message filter 2022-10-06 15:43:37 -04:00
Jon Chambers
d2fa00f0c6 Add experiment to test standalone registration service 2022-10-06 15:42:53 -04:00
erik-signal
d6c9652a70 Fix internal server error when sending stories to unknown recipient. 2022-10-06 13:53:57 -04:00
Jon Chambers
0d20b73e76 Update to the latest version of the abusive message filter 2022-10-05 15:20:49 -04:00
Jon Chambers
3c655cdd5a Migrate to "regionCode" instead of "region" to avoid tag name conflicts 2022-10-05 15:15:46 -04:00
Jon Chambers
fc5cd3a9ca Update to protobuf-java 3.21.7 2022-10-05 15:15:34 -04:00
Jon Chambers
83ab926f96 Add a dimension for story messages 2022-10-05 15:15:22 -04:00
erik-signal
56e54e0724 Update to the latest version of the abusive message filter 2022-10-05 13:19:47 -04:00
erik-signal
544e4fb89a Adjust routing for stories. 2022-10-05 12:20:42 -04:00
erik-signal
966c3a8f47 Add routing for stories. 2022-10-05 10:44:50 -04:00
Ravi Khadiwala
c2ab72c77e Update to the latest version of the abusive message filter 2022-09-30 12:57:21 -05:00
Ravi Khadiwala
4468ee3142 Update to the latest version of the abusive message filter 2022-09-30 12:10:02 -05:00
Ravi Khadiwala
c82c2c0ba4 Add country tag to twilio failures 2022-09-30 12:03:46 -05:00
Ravi Khadiwala
6e595a0959 add an optionals utility and fix push challenge metric 2022-09-30 12:02:47 -05:00
Ravi Khadiwala
a79d709039 Return 403 when a push challenge is incorrect 2022-09-30 12:02:47 -05:00
Ravi Khadiwala
538a07542e Update to the latest version of the abusive message filter 2022-09-22 11:20:48 -05:00
Ravi Khadiwala
07ed765250 Update abusive message filter and filter account creates 2022-09-20 14:52:18 -05:00
Ravi Khadiwala
2e497b5834 Fix operator order in metric calculation 2022-09-15 14:04:18 -05:00
Ravi Khadiwala
61b3cecd17 Fix missing increment on recaptcha counter 2022-09-14 17:07:26 -05:00
Ravi Khadiwala
a4a666bb80 Add metrics for recaptcha reasons 2022-09-14 16:00:11 -05:00
Ravi Khadiwala
c14621a09f Add metrics for captcha scores 2022-09-14 16:00:11 -05:00
Ravi Khadiwala
d0a8899daf Change discriminator seperator and default width 2022-09-14 15:53:15 -05:00
Chris Eager
65dbcb3e5f Remove duplicate bom from dependencyManagement 2022-09-12 16:54:31 -05:00
Chris Eager
7f725b67c4 Update to the latest version of the abusive message filter 2022-09-12 11:24:37 -05:00
Chris Eager
e25252dc69 Remove unused exception 2022-09-12 11:19:15 -05:00
Chris Eager
8b65c11e1e Update batch check entities from two optional fields to a single field 2022-09-12 11:19:01 -05:00
Chris Eager
320c5eac53 Add support for PNIs at v1/profile/identity_check/batch 2022-09-09 10:55:34 -05:00
Ehren Kret
8199e0d2d5 Set resource field on log entry 2022-09-07 19:37:26 -05:00
Ehren Kret
53387f5a0c Register polymorphic serialization 2022-09-07 19:37:26 -05:00
Ehren Kret
7d171a79d7 Remove redundant @NotNull annotation 2022-09-07 19:37:26 -05:00
Ehren Kret
3b99bb9e78 Log remote config delete events 2022-09-07 19:37:26 -05:00
Ehren Kret
132f026c75 Improve readability of event code 2022-09-07 19:37:26 -05:00
Ehren Kret
abd0f9630c Create GCP Logging implementation of AdminEventLogger 2022-09-07 19:37:26 -05:00
Ehren Kret
a4508ec84f Add new event logging module 2022-09-07 19:37:26 -05:00
Ehren Kret
6119b6ab89 Upgrade java-uuid-generator dependency 2022-09-07 19:37:26 -05:00
Ehren Kret
307ac47ce0 Update DynamoDBLocal dependency version 2022-09-07 19:37:26 -05:00
Ravi Khadiwala
4032ddd4fd Add reserve/confirm for usernames 2022-09-07 11:49:49 -05:00
Chris Eager
98c8dc05f1 Update to the latest version of the abusive message filter 2022-09-07 11:49:01 -05:00
Chris Eager
4c677ec2da Remove deprecated /v1/attachments 2022-09-07 11:48:16 -05:00
Chris Eager
c05692e417 Update deprecated CircuitBreakerConfig usage 2022-09-07 11:47:15 -05:00
Chris Eager
1e7aa89664 Update resilience4j to 1.7.0 2022-09-07 11:47:15 -05:00
gram-signal
ae1edf3c5c Remove experiment associated with auth1->auth2 rollout. 2022-08-31 12:10:46 -06:00
gram-signal
b17f41c3e8 Check if dashes work in dynamic configuration keys. 2022-08-29 15:51:37 -06:00
gram-signal
08db4ba54b Update authentication to use HKDF_SHA256. 2022-08-29 14:20:47 -06:00
gram-signal
cb6cc39679 Ignore null identity key. 2022-08-29 13:26:49 -06:00
Jon Chambers
b6bf6c994c Remove a spurious @Nullable annotation 2022-08-26 15:22:23 -04:00
Jon Chambers
3bb4709563 Add CLDR region as a dimension 2022-08-26 12:41:51 -04:00
Jon Chambers
b280c768a4 Allow signup captchas to target CLDR two-letter region codes 2022-08-26 12:41:51 -04:00
Chris Eager
d23e89fb9c Update micrometer to 1.9.3 2022-08-25 13:46:36 -07:00
Chris Eager
3a27bd0318 Update test dependencies 2022-08-25 13:40:46 -07:00
Chris Eager
616513edaf Remove unused jdbi dependency 2022-08-25 13:40:46 -07:00
Chris Eager
09a51020e9 Update stripe-java to 21.2.0 2022-08-25 13:40:46 -07:00
Chris Eager
cb8cb94d1a Update aws java v1 SDK to 1.12.287 2022-08-25 13:40:46 -07:00
Chris Eager
2440dc0089 Update netty to 4.1.79.Final 2022-08-25 13:40:46 -07:00
Chris Eager
2336eef333 Update aws java v2 SDK to 2.17.258 2022-08-25 13:40:46 -07:00
Chris Eager
a0e948627c Update jackson to 2.13.3 2022-08-25 13:40:46 -07:00
Chris Eager
88159af588 Update dropwizard to 2.0.32 2022-08-25 13:40:46 -07:00
Chris Eager
38b77bb550 Update libphonenumber to 8.12.54 2022-08-25 13:40:32 -07:00
Jon Chambers
e72d1d0b6f Stop reading attribute-based messages from the messages table 2022-08-22 13:37:39 -07:00
Ravi Khadiwala
1891622e69 Zero-pad discriminators less than initial width 2022-08-22 13:36:38 -07:00
Chris Eager
628a112b38 Include country code for verify failure 2022-08-19 12:21:05 -07:00
Jon Chambers
50f5d760c9 Use existing tagging tools for keepalive counters 2022-08-16 13:16:19 -07:00
Jon Chambers
7292a88ea3 Record table performance metrics around reported messages 2022-08-16 13:15:30 -07:00
Jon Chambers
07cb3ab576 Add a "sealed sender" dimension to the sent message counter 2022-08-16 13:11:12 -07:00
Chris Eager
27b749abbd Filter expired items from Dynamo 2022-08-16 13:09:47 -07:00
Chris Eager
27f67a077c Add metrics for report-verification-succeeded response 2022-08-16 13:08:16 -07:00
Ravi Khadiwala
393e15815b Rename secondary account key namespace for usernames 2022-08-15 10:51:52 -05:00
Ravi Khadiwala
a7f1cd25b9 Remove UAK normalization code
All accounts now have UAKs in top-level attributes
2022-08-15 10:47:52 -05:00
Ravi Khadiwala
953cd2ae0c Revert "Delete any leftover usernames in the accounts db"
This reverts commit a44c18e9b7.

Old username cleanup is finished.
2022-08-15 10:45:38 -05:00
ravi-signal
a84a7dbc3d Add support for generating discriminators
- adds `PUT accounts/username` endpoint
- adds `GET accounts/username/{username}` to lookup aci by username
- deletes `PUT accounts/username/{username}`, `GET profile/username/{username}`
- adds randomized discriminator generation
2022-08-15 10:44:36 -05:00
Chris Eager
24d01f1ab2 Revert "device capabilities: prevent stories downgrade"
This reverts commit 1c67233eb0.
2022-08-12 14:21:27 -05:00
Chris Eager
06eb890761 Improve e164 normalization check by re-parsing without country code 2022-08-12 10:52:55 -07:00
Chris Eager
6d0345d327 Clean up Util 2022-08-12 10:52:55 -07:00
Chris Eager
1c67233eb0 device capabilities: prevent stories downgrade 2022-08-12 10:51:16 -07:00
Jon Chambers
b4281c5a70 Send non-urgent push notifications with lower priority 2022-08-12 11:06:31 -04:00
Jon Chambers
5f6b66dad6 Add support for scheduling background push notifications 2022-08-12 10:57:59 -04:00
Jon Chambers
c2be0af9d9 Refactor ApnPushNotificationSchedulerTest to use a Clock 2022-08-12 10:57:59 -04:00
Jon Chambers
c111e9a35a Update to the latest version of the abusive message filter 2022-08-12 10:50:53 -04:00
Jon Chambers
a53a85d788 Refactor scheduled APNs notifications in preparation for future development 2022-08-12 10:47:49 -04:00
Ravi Khadiwala
a44c18e9b7 Delete any leftover usernames in the accounts db
The account username field should not currently be populated
2022-08-11 16:23:51 -05:00
Jon Chambers
4d78437fe4 Add a country code dimension to the non-normalized number counter 2022-08-10 15:03:01 -04:00
Jon Chambers
2bfe2c8ff8 Add an "urgent" dimension to the "sent messages" counter 2022-08-10 15:00:46 -04:00
Chris Eager
65da844d70 Small test cleanup 2022-08-09 15:32:44 -05:00
Chris Eager
5275c27ee1 Fix incorrect test Javadoc 2022-08-09 13:06:15 -07:00
Chris Eager
390580a19d Count cases when the a message’s destination UUID doesn’t match the account’s PNI 2022-08-09 13:06:15 -07:00
Jon Chambers
147917454f Measure the depth of the queue for the FCM executor 2022-08-04 15:53:26 -04:00
Jon Chambers
39562775d9 Use a fixed-size thread pool for sending FCM notifications 2022-08-04 15:37:22 -04:00
Jon Chambers
4a0ef1f834 Measure the time taken to send APNs push notifications 2022-08-04 10:43:07 -04:00
Jon Chambers
85b16b674d Measure the time taken to send FCM push notifications 2022-08-04 10:43:07 -04:00
Jon Chambers
ab5d8ba120 Use ApiFutures#addCallback for FCM futures 2022-08-04 10:43:07 -04:00
Jon Chambers
28076335e0 Generate APNs payloads using a payload builder 2022-08-04 10:37:30 -04:00
Jon Chambers
9e9333424f Retire RetryingApnsClient 2022-08-04 09:59:18 -04:00
Jon Chambers
6f0faae4ce Introduce common push notification interfaces/pathways 2022-08-03 10:07:53 -04:00
Jon Chambers
0d24828539 Drop the gcm-sender-async module 2022-08-02 17:31:35 -04:00
Jon Chambers
0a6d724f2c Remove GCMSender 2022-08-02 17:31:35 -04:00
Jon Chambers
8956e1e0cf Check for null FCM error codes 2022-08-02 17:29:31 -04:00
Jon Chambers
c9ae991aa3 Add an experiment to allow a phased transition from the old GCM API to the current FCM API 2022-08-02 15:34:09 -04:00
Jon Chambers
421d594507 Introduce an FcmSender 2022-08-02 15:34:09 -04:00
Jon Chambers
9c03f2e468 Add support for receiving, storing, and returning urgent flags on messages 2022-08-02 12:05:23 -04:00
Chris Eager
1175ff5867 Log cause when queue processing hits max retries 2022-08-02 08:36:16 -07:00
Chris Eager
55df593561 Clean up MessageAvailabilityListener if the websocket client is closed 2022-08-02 08:35:16 -07:00
Chris Eager
a06a663b94 Use the envelope’s destination UUID for receipt source UUID 2022-08-02 08:34:20 -07:00
Chris Eager
3d2f7e731f Remove Envelope.source 2022-08-02 08:34:20 -07:00
Chris Eager
2575372639 Add missing increment() to displacement counter 2022-08-02 08:30:54 -07:00
Jon Chambers
faa6e8324a Fix a test issue where we were ignoring some test parameters 2022-08-01 11:02:33 -04:00
Jon Chambers
d0e3fb1901 Unconditionally write messages to the messages table as envelopes 2022-08-01 10:44:27 -04:00
Jon Chambers
04287c5073 Optionally write messages as envelopes to the messages table 2022-07-29 11:10:50 -04:00
Jon Chambers
0c76fdd36c Read bare envelopes from the messages table if possible 2022-07-29 11:10:50 -04:00
Jon Chambers
d582942244 Update to the latest version of the abusive message filter 2022-07-29 10:59:02 -04:00
Jon Chambers
3636626e09 Make Envelope the main unit of currency when working with stored messages 2022-07-29 10:59:02 -04:00
Jon Chambers
3e0919106d Add a method to build an OutgoingMessageEntity from an Envelope 2022-07-29 10:59:02 -04:00
Jon Chambers
d385838dc1 Add methods to convert IncomingMessage/OutgoingMessageEntity instances into Envelope entities 2022-07-29 10:59:02 -04:00
Jon Chambers
e28f1e8ceb Remove the unused destination property from IncomingMessage 2022-07-29 10:59:02 -04:00
Jon Chambers
3d875f1ce5 Convert incoming/outgoing message entities to records 2022-07-29 10:59:02 -04:00
Jon Chambers
c4c5397b44 Convert group credential redemption time to a long 2022-07-28 10:08:25 -04:00
Jon Chambers
6b6f9b2405 Add PNI to GroupCredentials responses that contain AuthCredentialWithPni instances 2022-07-28 10:08:25 -04:00
Jon Chambers
7d4a8d03a4 Mark old group credential getter as @Deprecated 2022-07-28 10:08:25 -04:00
Jon Chambers
e9119da040 Retire the (unused!) binary message format 2022-07-28 09:59:00 -04:00
Chris Eager
aa36dc95ef Add MicrometerCommandLatencyRecorder to Redis clusters 2022-07-27 14:23:14 -07:00
Chris Eager
a6f9409a39 Remove dynamic configuration feature flags; add DynamicMessagePersisterConfiguration 2022-07-27 14:19:10 -07:00
Chris Eager
41a113e22c Stop queue persistence attempt if items aren’t removed from cache 2022-07-27 14:19:10 -07:00
Chris Eager
4cfcdb0c96 editorconfig formatting 2022-07-27 14:19:10 -07:00
Ravi Khadiwala
36050f580e Handle duplicate device ids more gracefully 2022-07-27 11:15:32 -05:00
Jon Chambers
98760b631b Don't wrap "change number" arguments in Optional 2022-07-27 10:55:37 -04:00
Jon Chambers
d00aa1e77a Fix an inconsistent check for the presence/absence of "change number" arguments 2022-07-27 10:55:37 -04:00
Jon Chambers
dce391a248 Add support for setting PNI-associated registration IDs and identity keys when changing numbers 2022-07-26 15:19:27 -04:00
Antonin Tritz
c252118cfc External links in comments from http to https 2022-07-26 15:17:58 -04:00
Chris Eager
e9fd32de79 Only attempt to unsubscribe if the queue name is not null 2022-07-26 10:20:10 -07:00
Chris Eager
788246a56f Update Lettuce to 6.1.9 2022-07-26 09:22:13 -07:00
Chris Eager
bc02fe3831 Only unsubscribe from keyspace notifications if the node has the slot 2022-07-26 09:21:50 -07:00
Jon Chambers
d290aad27b Associate source/destination country codes for reported messages 2022-07-14 09:31:12 -04:00
Jon Chambers
6754ec5e10 Pass disconnection reason (if known) to clients 2022-07-13 15:30:03 -04:00
Jon Chambers
1ba00a66eb Pass the reason for displacement to presence displacement listeners 2022-07-13 15:30:03 -04:00
Jon Chambers
1dd7d33e23 Simplify Device entity 2022-07-13 13:55:20 -04:00
Jon Chambers
e200548e35 Introduce an account change validator 2022-07-13 12:24:39 -04:00
Jon Chambers
fdf7b69996 Remove a temporary workaround for incorrect envelope types from iOS clients 2022-07-13 11:30:52 -04:00
Jon Chambers
92d36b725f Allow presence keys to expire if not periodically renewed 2022-07-13 11:28:55 -04:00
Jon Chambers
4e131858ca Generalize scope of and expand size of websocket scheduled executor service 2022-07-13 11:28:55 -04:00
Ravi Khadiwala
a45d95905e Be permissive in account-create accept-language
Currently, if we fail to parse a user's accept-language in account
creation, creation will fail. While it's a suboptimal experience to get
a verify code in the wrong language, it might be better than not being
able to sign up at all.
2022-07-13 11:22:31 -04:00
Ehren Kret
0fdfdabf2a merge GroupController into CertificateController 2022-07-01 13:04:24 -05:00
Jon Chambers
a25e967978 Remove spurious mocking 2022-07-01 12:26:22 -05:00
Jon Chambers
38e30c7513 Allow callers to get an expiring profile key credential 2022-07-01 12:26:22 -05:00
Jon Chambers
e38e5fa17d Allow callers to request a combined group auth credential 2022-07-01 12:26:22 -05:00
Jon Chambers
c1f9bedf2f Update to libsignal-server 0.18 2022-06-23 14:25:28 -04:00
Jon Chambers
dd5d0ea2b3 Update to the latest version of the abusive message filter 2022-06-23 08:37:27 -04:00
Ehren Kret
42fd29d38b Update badge description text to remove boost & sustainer language 2022-06-21 13:36:12 -05:00
Ehren Kret
bf6d3aa324 Fix batching issue that was causing duplication in large queries 2022-06-20 12:41:10 -05:00
Ehren Kret
023ccc6563 Add a unit test for the batch identity check endpoint 2022-06-20 12:32:31 -05:00
Ehren Kret
da49db5b9e Move batch identity checks off the common fork join pool 2022-06-20 11:07:33 -05:00
Ehren Kret
cc8dda28cc Simplify logic for batching bulk identity check request 2022-06-20 10:28:20 -05:00
Ehren Kret
47300c1d44 Upgrade vavr to 0.10.4 from 0.10.2 2022-06-20 10:27:50 -05:00
Ehren Kret
d31550d444 Only wrap Base64 decode with the exception catch 2022-06-20 09:42:33 -05:00
Ehren Kret
51f37350eb Because one shouldn't take the size of null things 2022-06-17 15:03:25 -05:00
Ehren Kret
ecfa161da8 Validate the request 2022-06-17 14:32:38 -05:00
Ehren Kret
e3778c17ea Use POST not GET for request requiring body 2022-06-17 14:31:15 -05:00
Ehren Kret
cbc95415b7 Add endpoint to batch check identity keys 2022-06-17 12:20:30 -05:00
Ehren Kret
776c0aa488 Don't use inner class imports 2022-06-17 11:21:30 -05:00
Ravi Khadiwala
327eb0219d Bypass account registration captcha on test devices 2022-06-13 15:01:46 -07:00
Jon Chambers
8507b6a1f0 Update to libphonenumber 8.12.50 2022-06-13 14:46:57 -07:00
gram-signal
a853748303 Revert "Update ChangeNumber to allow reset of registration IDs."
This reverts commit 7001ad1445.
2022-06-09 11:51:50 -06:00
Jon Chambers
192e884e4a Update to embedded-redis 0.8.3 2022-06-03 09:31:32 -04:00
gram-signal
7001ad1445 Update ChangeNumber to allow reset of registration IDs. 2022-06-02 16:37:32 -06:00
Ravi Khadiwala
5cfb133f79 Use redis for abusive hosts autoblock
Also delete postgres dependencies that we no longer need
2022-05-31 10:08:10 -05:00
Ehren Kret
5df24edebf Remove all unused imports remaining in project 2022-05-25 17:15:20 -05:00
Ehren Kret
95d0293a96 Remove unused counter 2022-05-25 16:57:01 -05:00
Ehren Kret
f5a2efb57c Clarify hidden static method call 2022-05-25 16:51:12 -05:00
Ehren Kret
e4b9ae4eee Clear up warnings 2022-05-25 16:50:58 -05:00
Ehren Kret
bc1ac5a37f Remove unused fields 2022-05-25 16:50:43 -05:00
Ehren Kret
96ac56faac Remove unused import 2022-05-25 16:38:11 -05:00
Ehren Kret
f0bc444388 Remove unused timer 2022-05-25 16:37:51 -05:00
Ravi Khadiwala
8584f47d95 Add more metrics to createAccount captcha 2022-05-25 15:50:11 -04:00
Jon Chambers
f6235b8c08 Check for newly-expired accounts before previously-expired accounts for metric accuracy 2022-05-25 15:49:54 -04:00
Jon Chambers
d452e90470 Move AccountCleaner into its own crawler 2022-05-25 15:46:18 -04:00
Jon Chambers
418a869451 Increase max deletions per chunk to 256 2022-05-25 14:19:04 -04:00
Jon Chambers
cf89e2215c Fully delete already-expired accounts 2022-05-25 14:19:04 -04:00
Jon Chambers
a4ca1ef1a8 Move AccountCleanerTest out of the test package; reduce visibility of test-only fields 2022-05-25 14:19:04 -04:00
Ehren Kret
c38572307d Remove unused imports 2022-05-20 10:20:45 -07:00
Fedor Indutny
20902df122 Fix ZRANGE arguments in get_items.lua 2022-05-19 12:14:02 -07:00
Fedor Indutny
d31ddb72f3 Optimize message deletion by skipping lookup 2022-05-18 13:02:21 -07:00
Ehren Kret
d5f2d86bd2 Add Maven Wrapper 2022-05-13 14:14:37 -05:00
Jon Chambers
2ce8bcd565 Update to the latest version of the abusive message filter 2022-05-11 14:27:32 -04:00
Jon Chambers
75c92eaa93 Drop high-cardinality distribution summaries 2022-05-11 14:26:38 -04:00
Chris Eager
0445adcac3 Conclude ReportMessageManager ACI migration 2022-05-11 11:26:24 -07:00
Sgn-32
c45ff61954 Update libphonenumber to 8.12.48 2022-05-11 14:17:13 -04:00
gram-signal
06dd4c5026 Derive username from ACI for CDS{H,I} (#989)
* Derive username from ACI for CDS{H,I}

* Update sample YAML.
2022-05-02 08:41:38 -06:00
Ehren Kret
058caadf4f Use BigDecimal#compareTo for numeric equality testing 2022-04-29 14:20:09 -05:00
Ehren Kret
7b7d309105 Apply stripe conversion factor to gift badge amount check 2022-04-29 13:48:10 -05:00
Ehren Kret
63be7b93ce Record level on boost payment intent 2022-04-29 12:06:15 -05:00
Ehren Kret
578ea12b59 Add gift badges to user capabilities 2022-04-29 11:08:36 -05:00
Ehren Kret
364e59be57 Add shape to duration to ensure it's render as an integer 2022-04-29 10:14:39 -05:00
Ehren Kret
fece4dac9e Add duration to boost badges
Lets clients know how long the badge will last for after purchase.
2022-04-29 10:14:39 -05:00
Ehren Kret
ce85c1aabc Add name and description for gift badge 2022-04-29 10:14:39 -05:00
Ehren Kret
0ac2ce5e72 Add gift badge to the level output for boost badges 2022-04-29 10:14:39 -05:00
Ehren Kret
391c800bf5 Add gift configuration to subscription controller 2022-04-29 10:14:39 -05:00
Ehren Kret
9c27b58194 Update sample configuration file 2022-04-29 10:14:39 -05:00
Ehren Kret
f6471cf8f9 Add gift configuration source 2022-04-29 10:14:39 -05:00
Ehren Kret
f21e9bcc4d Upgrade jackson dependencies 2022-04-29 10:14:39 -05:00
Jon Chambers
1eaff753a6 Count "forbidden identity key change" events 2022-04-22 15:53:43 -04:00
JanLukasGithub
9b3a8897cd Change copyright to 2022 2022-04-22 14:20:34 -04:00
Jon Chambers
40f8cddfb2 Update to libsignal-server 0.16.0 2022-04-20 16:00:12 -04:00
Ehren Kret
c29d5de1eb Refactor two more switch statements to new switch style 2022-04-19 13:39:38 -05:00
Ehren Kret
d94c171d63 Use new style switch statement 2022-04-19 13:35:26 -05:00
Ehren Kret
2717967d61 Revert submodule change from 473ecbdf2d 2022-04-19 10:53:58 -05:00
Ehren Kret
53203dbcef Refactor common pattern for checking Account capabilities 2022-04-19 10:33:54 -05:00
Ehren Kret
9e66f8ac11 Add gift badges device capability 2022-04-19 10:33:54 -05:00
Ehren Kret
796fb3b4cd Refactor Device#equals method 2022-04-19 10:33:54 -05:00
gram-signal
473ecbdf2d Allow primary to set and provide new signed prekeys for linked devices (#950) 2022-04-15 12:39:47 -06:00
Chris Eager
7b3703506b Remove number from ReportMessageManager#store 2022-04-13 16:41:32 -04:00
Jon Chambers
5816f76bbe Add support for getting (limited) profiles by phone number identifier 2022-04-13 16:27:57 -04:00
Ehren Kret
355996bafc Add outcome type to subscription information endpoint 2022-04-08 12:31:31 -05:00
Ehren Kret
c2bb46f41d Add outcome network status and outcome reason to subscription info 2022-04-08 12:31:31 -05:00
Ehren Kret
12f76c24b1 Add failure information for the latest charge on a subscription 2022-04-08 12:31:31 -05:00
amit
4b8ebc9a17 Revert "newlines for a test."
I had expected this to be rejected -- whoops!

This reverts commit 42a109e593.
2022-03-29 12:47:34 -07:00
amit
42a109e593 newlines for a test. 2022-03-29 11:29:43 -07:00
Jon Chambers
8064e68873 Update libsignal-server to 0.15.1 to resolve a glibc version conflict 2022-03-29 12:24:45 -04:00
Jon Chambers
3dc0d0bb92 Revert "Revert "Replace curve25519-java with libsignal-server""
This reverts commit c06a5ac96c.
2022-03-29 12:24:45 -04:00
Jon Chambers
2bb8f92af1 Revert "Revert "Replace zkgroup with libsignal-server""
This reverts commit fa3a9570d6.
2022-03-29 12:24:45 -04:00
Jon Chambers
5b7d5d2b93 Reduce "unrecoverable error" messages when sending GCM notifications from WARN to DEBUG 2022-03-25 14:34:44 -07:00
Jon Chambers
2b27db18d8 Count GCM failures by error code 2022-03-25 14:34:44 -07:00
Jon Chambers
f3c811cc03 Move "no local subscription" warnings from WARN to DEBUG 2022-03-25 14:34:44 -07:00
Chris Eager
df415208a4 Update to the latest version of the abusive message filter 2022-03-25 14:32:55 -07:00
Chris Eager
77fd01bd9f Accept source ACI at /v1/messages/report 2022-03-25 14:27:09 -07:00
Jon Chambers
fa3a9570d6 Revert "Replace zkgroup with libsignal-server"
This reverts commit 86a09b16ff.
2022-03-24 12:11:46 -04:00
Jon Chambers
c06a5ac96c Revert "Replace curve25519-java with libsignal-server"
This reverts commit 06a57ef811.
2022-03-24 12:11:46 -04:00
Jon Chambers
33467b42da Remove a deprecated/unused field 2022-03-24 10:50:49 -04:00
Jon Chambers
13fb641113 Make field name casing consistent 2022-03-24 10:50:49 -04:00
Jon Chambers
53f17c2baa Drop the legacy message and relay fields from message entities 2022-03-24 10:50:49 -04:00
Jordan Rose
06a57ef811 Replace curve25519-java with libsignal-server
These APIs stemmed from a common source long ago, so there's not much
to change!
2022-03-24 10:50:18 -04:00
Jordan Rose
86a09b16ff Replace zkgroup with libsignal-server 2022-03-24 10:50:18 -04:00
Ravi Khadiwala
c70d7535b9 Make TURN configuration dynamic
Also enables conditionally including more TURN servers for gradual
rollouts
2022-03-23 14:38:02 -05:00
Ravi Khadiwala
8541360bf3 Update to the latest version of the abusive message filter 2022-03-23 14:32:55 -05:00
Jon Chambers
2a832d36d7 Remove AcceptNumericOnlineFlagRequestFilter 2022-03-23 14:31:01 -05:00
gram-signal
1578c89475 Only allow primary device to update identity key. 2022-03-22 14:39:04 -06:00
Chris Eager
5c13e54149 Various dependency updates
- protobuf-java to 3.19.4
- libphonenumber to 8.12.45
- logstash-logback-encoder to 7.0.1
- mockito to 4.3.1
2022-03-22 09:58:08 -04:00
Jon Chambers
8e74cf6633 Update to the latest version of the abusive message filter 2022-03-22 09:56:27 -04:00
Jon Chambers
bab6b36e4d Count reported messages by destination country code in addition to source country code 2022-03-22 09:56:27 -04:00
Jon Chambers
f75e616397 Introduce a listener pattern for reported messages 2022-03-22 09:56:27 -04:00
Jon Chambers
941a9c3b39 Update to the latest version of the abusive message filter 2022-03-17 16:08:20 -04:00
Jon Chambers
7ba0f604e6 Tag the push challenge request counter by country 2022-03-17 16:07:48 -04:00
Chris Eager
cf8a4cc939 Decrease receipt sender executor thread pool 2022-03-17 13:07:03 -07:00
Jon Chambers
ee78daeeef Update to the latest version of the abusive message filter 2022-03-16 15:30:43 -04:00
Jon Chambers
2f6b0b1a55 Tag push challenge attempt metrics by country code 2022-03-16 15:00:04 -04:00
Jon Chambers
c048074c31 Tag captcha attempt metrics with UA platform 2022-03-16 15:00:04 -04:00
Ravi Khadiwala
5ca89709e3 Update to the latest version of the abusive message filter 2022-03-14 14:15:51 -05:00
Ravi Khadiwala
5a88ff0811 Use the async dynamo client to batch uak updates 2022-03-14 14:02:16 -05:00
Chris Eager
de68c251f8 Instrument the receipt sender executor 2022-03-11 17:20:52 -08:00
Chris Eager
7c9ae3561d Send delivery receipts asynchronously 2022-03-11 16:34:22 -08:00
Chris Eager
b608ece57e Remove supportsAnnouncementGroups metric 2022-03-11 16:18:00 -08:00
Chris Eager
8dfffebaf1 Remove unnecessary check for destination UUID 2022-03-11 16:17:54 -08:00
Jon Chambers
109a3bb2b9 Update to Pushy 0.15.1 2022-03-10 11:08:53 -05:00
Chris Eager
fef37f739b Remove unused classes and methods 2022-03-10 11:08:38 -05:00
Jon Chambers
7a5615182a Update to the latest version of the abusive message filter 2022-03-10 11:06:04 -05:00
Ravi Khadiwala
02a7003ffe Update to the latest version of the abusive message filter 2022-03-09 14:14:47 -06:00
Ravi Khadiwala
1571f14815 Add a feature flag to disable account normalization 2022-03-09 14:03:21 -06:00
Ravi Khadiwala
9cb098ad8a Add a top-level uak to existing items
Items wirtten before we started storing the uak at
the top level only store the uak in the
account blob. The will be updated on account
crawl
2022-03-09 14:03:21 -06:00
Jon Chambers
6283f5952d Update to the latest version of the abusive message filter 2022-03-08 10:03:38 -05:00
Jon Chambers
9b9edbae0e Drop DeadLetterHandler (which is functionally unused) 2022-03-08 10:03:06 -05:00
Chris Eager
491155d1cf Remove @Consumes from GET/DELETE in /v1/subscriptions 2022-03-04 15:36:02 -08:00
Chris Eager
54207254f1 Remove unused configuration 2022-03-04 11:44:17 -08:00
Chris Eager
1395dcc0be Make the enterprise client canonical 2022-03-04 11:44:17 -08:00
Chris Eager
2a68d9095d Remove transitional and legacy client 2022-03-04 11:44:17 -08:00
Chris Eager
a984b3640e Further refine score distribution summary 2022-03-04 11:44:17 -08:00
Jon Chambers
f9c1e411aa Remove netty-tcnative as a dependency 2022-03-04 10:45:18 -05:00
Jon Chambers
f6cbc32ee7 Align "link device" and "fetch pre-keys" rate limits 2022-03-04 10:45:06 -05:00
Chris Eager
602614acf6 Refine assessment metrics 2022-03-03 16:09:49 -08:00
Chris Eager
3854b7d472 Remove @Consumes from GET /v1/config 2022-03-03 16:08:55 -08:00
Jon Chambers
5e25481088 Fix a trivial typo in a constant's name 2022-03-03 10:59:19 -05:00
Jon Chambers
fe86e15d80 Remove PNI repair code 2022-03-03 10:25:11 -05:00
Jon Chambers
179b4a69eb Clear one-time pre-keys when PNIs are assigned 2022-03-03 10:24:54 -05:00
Chris Eager
eee6307789 Move score floor to dynamic configuration, add distribution summary 2022-03-02 15:18:33 -08:00
Chris Eager
9fc5002619 Add stories capability 2022-03-02 15:16:21 -08:00
Ravi Khadiwala
faa6ae284a Add uak as a top level attribute 2022-03-02 10:41:09 -06:00
Jon Chambers
8b4355b21d Add a "challenge issued" counter tagged by country and platform 2022-03-02 10:40:47 -06:00
Jon Chambers
e8835da740 Tag captcha success rate counter by country 2022-03-02 10:40:47 -06:00
Dimitris Apostolou
75854e104e Fix typo
Co-authored-by: Chris Eager <79161849+eager-signal@users.noreply.github.com>
2022-03-01 14:14:52 -08:00
Dimitris Apostolou
93d06e3f4d Fix typos 2022-03-01 14:14:52 -08:00
Chris Eager
c560b9229c Update to the latest version of the abusive message filter 2022-03-01 10:43:12 -08:00
Chris Eager
935e268dec Parameterize sitekey 2022-03-01 10:40:42 -08:00
Jon Chambers
3a1c716c73 Remove an unused rate limiter 2022-02-25 13:50:17 -08:00
Chris Eager
f3457502a6 Support different v2 captcha actions 2022-02-25 13:49:47 -08:00
Ravi Khadiwala
7ded802df4 Update to the latest version of the abusive message filter 2022-02-24 17:30:02 -06:00
Ravi Khadiwala
d3cd1d1b15 Use GetLatestConfiguration in config manager
Use StartConfigurationSession/GetLatestConfiguration instead of
GetConfiguration since the latter has been deprecated
2022-02-23 15:36:33 -06:00
Ravi Khadiwala
f5a75c6319 Simplify RateLimitExceeded with no retry-duration
- Avoid passing negative durations in error cases
- Drop unused message
- Return a duration for a bad forwarded-for
2022-02-23 15:25:24 -06:00
Ravi Khadiwala
ae3a5c5f5e Return a Retry-After on rate-limited responses
Previously, only endpoints throwing a RetryLaterException would include
a Retry-After header in the 413 response. Now, by default, all
RateLimitExceededExceptions will be marshalled into a 413 with a
Retry-After included if possible.
2022-02-23 15:25:24 -06:00
Jon Chambers
43792e2426 Update to the latest version of the abusive message filter 2022-02-22 11:03:41 -05:00
Jon Chambers
551d639951 Update Postgres driver to 42.3.3 2022-02-18 10:41:29 -05:00
Jordan Rose
c367a71223 APNS: include a collapse-id for non-VOIP notifications
This has two benefits:

- The APNS server should only send an iOS client a single push
  notification for any missed messages while the device is offline
  (server-side coalescing). Note that the client can still turn that
  into multiple "user notifications" as it pulls from its queue.

- If multiple notifications get delivered but iOS is unable to process
  them (say, because the phone just restarted and hasn't been unlocked
  yet), the user should only get one "You may have received messages"
  notification (client-side coalescing).
2022-02-18 10:41:10 -05:00
Chris Eager
d259ef0348 Update rate limit exceeded counter 2022-02-17 13:23:48 -08:00
Chris Eager
288cbf4a80 Clean up null-ability of incoming message entity fields 2022-02-17 13:23:48 -08:00
Chris Eager
ba5e5a780f Throw an exception instead of using Optional<Resposne> 2022-02-17 13:23:48 -08:00
Chris Eager
73fa3c3fe4 Add test for content length validation 2022-02-17 13:23:48 -08:00
Chris Eager
579eb85175 Reject invalid envelope types 2022-02-17 13:23:48 -08:00
Chris Eager
b2b20072ae Add MicrometerRegistryManager 2022-02-17 13:18:30 -08:00
Chris Eager
a2c4d3fe95 Use a strong reference to the application shutdown gauge 2022-02-17 13:18:30 -08:00
Ravi Khadiwala
31e2be2e4d Fixup invalid accept-language counter
- Fix name
- Add platform/version tags to the counter
2022-02-10 11:57:25 -06:00
Ravi Khadiwala
9f5d97e1c6 Silence noisy warnings for invalid Accept-Language 2022-02-10 11:57:25 -06:00
Ravi Khadiwala
baaae6cd9f Add @NotNull to controller args where appropriate
Notably, `@Valid` doesn't imply `@NotNull`
2022-02-10 11:57:04 -06:00
Chris Eager
ed398aa7b9 Add DeviceCapabilities.pni 2022-02-09 15:56:01 -08:00
Chris Eager
6e2ae42dab Add platform to metrics 2022-02-09 15:55:07 -08:00
Chris Eager
7f832ad783 Update to AWS SDK 2.17.125, 1.12.154 2022-02-07 16:46:25 -08:00
Chris Eager
2ce6f8cb6c Update to Dropwizard 2.0.28 2022-02-07 16:46:18 -08:00
Chris Eager
2574125199 Update libphonenumber to 8.12.42 2022-02-07 16:45:48 -08:00
Jordan Rose
41bf2b2c42 Add a binary format for incoming messages
The existing, general incoming message endpoint accepts messages as
JSON strings containing base64 data, along with all the metadata as
other JSON keys. That's not very efficient, and we don't make use of
that full generality anyway. This commit introduces a new binary
format that supports everything we're using from the old format (with
the help of some query parameters like multi-recipient messages).
2022-02-07 16:05:03 -08:00
Jon Chambers
51bac394ec Update to the latest version of the abusive message filter 2022-02-02 17:32:45 -05:00
Jon Chambers
b696649c9d Update to the latest version of the abusive message filter 2022-01-31 16:24:09 -05:00
Chris Eager
b4828ad8de Update embedded-redis to 0.8.2 2022-01-31 13:01:52 -08:00
Chris Eager
639d634426 Restore displaced UUID from deleted accounts table when present 2022-01-31 13:01:36 -08:00
Chris Eager
5358fc4f43 Use setRegistrationLockFromAttributes 2022-01-31 12:56:59 -08:00
Jon Chambers
6a654ab90b Update to the latest version of the abusive message filter 2022-01-28 11:26:40 -05:00
Jon Chambers
99eda80a78 Measure rate limit challenge responses by platform 2022-01-27 16:05:52 -05:00
Chris Eager
a6182acc9c Require any transitive dependencies on log4j to use 2.17.1 2022-01-27 10:25:13 -08:00
Jon Chambers
2241e4d8ea Update to the latest version of the abusive message filter 2022-01-26 16:30:45 -05:00
Jon Chambers
cbbdea1ba4 Impose more stringent requirements on cardinality checks 2022-01-26 16:13:12 -05:00
Jon Chambers
05e7c98620 Return an AccountIdentityResponse when changing phone numbers 2022-01-26 14:44:53 -05:00
Jon Chambers
1f1d618dea Rename AccountCreationResult to AccountIdentityResponse (since it's not just for account creation any more) 2022-01-26 14:44:53 -05:00
Jon Chambers
b18117ef89 Add tests for unidentified access when requesting profiles 2022-01-25 14:07:44 -05:00
Jon Chambers
44cb796574 Add more detail to "could not get acceptable languages" logging 2022-01-25 12:21:35 -05:00
Jon Chambers
ccf60ffc4b Update to the latest version of the abusive message filter 2022-01-25 10:39:50 -05:00
Jon Chambers
f69db11f42 Drop some unused dynamic configuration properties 2022-01-21 18:46:37 -05:00
Chris Eager
96a680dcf0 Remove displaced account from directory when changing numbers 2022-01-21 18:46:11 -05:00
Jon Chambers
c8367c9b7a Update to the latest version of the abusive message filter 2022-01-14 14:48:08 -05:00
Jon Chambers
c612663490 Handle null AccountAttributes when verifying linked devices 2022-01-14 14:47:46 -05:00
Jon Chambers
de5d967d18 Track metrics for dynamic config load failures 2022-01-14 14:47:12 -05:00
Jon Chambers
7fc63f7847 Allow callers to specify one or more dynamic config classes 2022-01-14 14:45:29 -05:00
Ehren Kret
49009cbcad Apply GitHub stale application 2022-01-07 11:37:52 -06:00
Chris Eager
b5fbeffb86 Remove obsolete deployment files 2022-01-06 12:52:37 -08:00
Jordan Rose
146655e997 Add a "sameAvatar" flag to CreateProfileRequest
If sameAvatar is set (and "avatar" is also set), the avatar field for
a profile will be copied from the existing profile. This saves S3
churn and client bandwidth.
2022-01-04 13:57:41 -08:00
Chris Eager
87d66f04d8 Update to the latest abusive message filter 2022-01-03 15:16:29 -08:00
Chris Eager
bb27dd0c3b Migrate from Object[] parameters to Stream<Arguments> 2022-01-03 15:10:02 -08:00
Chris Eager
f45a1c232f Exclude junit from transitive dependencies 2022-01-03 15:10:02 -08:00
Chris Eager
d7a3c12bbe Drop unused DynamoDB class rules 2022-01-03 15:10:02 -08:00
Chris Eager
a1e84f5a88 Migrate service tests to JUnit 5 2022-01-03 15:10:02 -08:00
Chris Eager
b758737907 Migrate remaining JUnit 4 Redis cluster tests to RedisClusterExtension
* Increase redis cluster initialization wait to 10 seconds
* Move to JUnit 5 `Assumptions`
2022-01-03 14:59:39 -08:00
Chris Eager
c488c14d25 Migrate gcm-sender-async tests to JUnit 5 2021-12-21 09:39:02 -08:00
Chris Eager
5e0cca0702 Migrate redis-dispatch to JUnit 5 2021-12-21 09:39:02 -08:00
Chris Eager
8559e46e4a Use JUnit 5 by default for all sub projects 2021-12-21 09:39:02 -08:00
Chris Eager
4bc00e00e3 Update to the latest abusive message filter 2021-12-20 11:33:04 -08:00
Chris Eager
3e777df86c Timeout sendNextMessagePage after 5 minutes 2021-12-20 11:31:11 -08:00
Chris Eager
278b4e810d Add (failing) test for send message timeouts 2021-12-20 11:31:11 -08:00
Chris Eager
346c7cd743 Remove null-check of destination UUID 2021-12-20 11:31:11 -08:00
Ehren Kret
867bf97d8f Require any transitive dependencies on log4j to use 2.17.0 2021-12-19 07:21:00 -06:00
Jon Chambers
8a67949168 Trivial typo fix 2021-12-16 12:44:58 -05:00
Jon Chambers
5baa51d547 Migrate challenge-issuing configuration into the abusive message filter module 2021-12-16 12:22:19 -05:00
Jon Chambers
616db337e1 Remove an old rate limiting feature flag 2021-12-16 12:22:19 -05:00
Jon Chambers
3895871462 Repair missing PNIs in JSON blobs on account load 2021-12-16 11:17:51 -05:00
Jon Chambers
a87b84fbe2 Return an empty response if somebody requests a profile key credential with a non-existent version 2021-12-16 10:30:55 -05:00
Chris Eager
b2f0ace9db Update dropwizard to 2.0.26 2021-12-15 16:34:52 -08:00
Jon Chambers
20c95e2606 Register ApplicationShutdownMonitor last
This will make it start last and shut down first, which is pretty much what we want for shutdown state monitoring
2021-12-15 19:27:23 -05:00
Jon Chambers
22dccaeddb Count cases where we can find a given account, but not the given profile version 2021-12-15 15:53:47 -05:00
Jon Chambers
e611a70ba4 Remove usernames from profile responses 2021-12-15 15:53:47 -05:00
Jon Chambers
66845d7080 Refactor: separate the various types of profile responses 2021-12-15 15:53:47 -05:00
Jon Chambers
4ea7278c6f Remove unversioned profile properties from Account entities 2021-12-15 15:53:47 -05:00
Jon Chambers
2b2e26f14b Remove deprecated, unversioned profile setters 2021-12-15 15:53:47 -05:00
Jon Chambers
b496ef8d6f Fix an issue where the deleted accounts lock client was trying to talk to the deleted accounts table 2021-12-15 13:16:32 -05:00
Jon Chambers
7f5e83141d Consolidate DynamoDB clients/configuration 2021-12-15 12:43:57 -05:00
Jon Chambers
2d1ca98605 Consolidate DynamoDB clients/configuration 2021-12-15 12:42:41 -05:00
Jon Chambers
eaa4c318e3 Add usernames to whoami and account creation responses 2021-12-15 11:47:10 -05:00
Jon Chambers
31373fd1ba Add a command for assigning usernames to individual users 2021-12-15 11:46:33 -05:00
Ehren Kret
9086246947 Require any transitive dependencies on log4j to use 2.16.0 2021-12-15 01:16:10 -06:00
Ehren Kret
7855b70682 Require any transitive dependencies on log4j to use 2.15.0 2021-12-10 01:16:48 -06:00
Chris Eager
0ce87153e5 Remove second database for AbusiveHostRules 2021-12-08 16:03:02 -08:00
Chris Eager
dba1711e8d Convert AbusiveHostRule to a record 2021-12-08 14:43:21 -08:00
Chris Eager
a70b057e1c Add second (migration) database to AbusiveHostRules 2021-12-08 12:46:05 -08:00
Chris Eager
9a5ffea0ad Move ossrh-snapshots to project aggregate pom 2021-12-07 15:14:47 -08:00
Chris Eager
96f4b771ea Update wiremock to 2.32.0 2021-12-07 10:34:49 -08:00
Ehren Kret
3df143dd3d Switch to zonky test embedded postgres and use postgres 11.13.0 2021-12-03 16:07:07 -05:00
Jon Chambers
d78d7c726e Fix a UUID transposition when requesting profile key credentials 2021-12-03 16:06:26 -05:00
Jon Chambers
d0ad580c7d Add (failing!) tests for getting a profile key credentials for somebody else 2021-12-03 16:06:26 -05:00
Ehren Kret
4a8a2a70b5 Return 400 instead of 500 when amount is too small 2021-12-03 12:24:16 -06:00
Jon Chambers
20a71b7df2 Add tests for generating profile key credentials 2021-12-03 12:16:13 -05:00
Jon Chambers
68412b3901 Allow the "get profile" endpoint to include a PNI credential 2021-12-03 12:16:13 -05:00
Jon Chambers
93a7c60a15 Update to zkgroup 0.9.0 2021-12-03 12:16:13 -05:00
Chris Eager
31e5058b15 Make temporary iOS envelope type adaptation a little wider 2021-12-03 12:06:31 -05:00
Jon Chambers
14cff958e9 Migrate challenge-issuing rate limiters to the abusive message filter 2021-12-03 11:52:58 -05:00
Jon Chambers
9628f147f1 Separate statically- and dynamically-configured rate limiters 2021-12-03 11:01:15 -05:00
Chris Eager
13e346d4eb Distinguish local vs remote in ClientPresenceManager#disconnectPresence 2021-12-02 14:32:42 -08:00
Fedor Indutny
e507ce2f26 Include ACI and PNI in DeviceResponse 2021-12-02 17:21:05 -05:00
Jon Chambers
9c62622733 Handle cases where a message might be missing a destination UUID 2021-12-02 14:06:49 -05:00
Jon Chambers
62aa0cef39 Set destination UUIDs for receipts 2021-12-02 14:06:49 -05:00
Jon Chambers
401953313a Remove all vestiges of the relational account database 2021-12-02 12:46:43 -05:00
Chris Eager
4d2403d619 Use assertTimeoutPreemptively instead of assertTimeout 2021-12-02 12:17:08 -05:00
Jon Chambers
c5f261305d Store destination UUIDs when persisting messages to DynamoDB 2021-12-02 12:17:08 -05:00
Jon Chambers
394f58f6cc Add a (failing!) check for destination UUIDs 2021-12-02 12:17:08 -05:00
Jon Chambers
674bf1b0e0 Drop a myserious empty test 2021-12-02 12:17:08 -05:00
Jon Chambers
606ddd8a9b Populate destination UUID for messages delivered via websocket 2021-12-02 12:17:08 -05:00
Jon Chambers
e23a1fac50 Remove old Postgres-backed remote config machinery 2021-12-02 12:16:43 -05:00
Jon Chambers
342323a7e6 Use canonical usernames throughout AccountsManager 2021-12-02 12:02:29 -05:00
Jon Chambers
efb410444b Introduce a username validator 2021-12-02 12:02:29 -05:00
Jon Chambers
17c9b4c5d3 Transition from Postgres-backed remote config store to Dynamo-backed store 2021-12-02 11:13:07 -05:00
Jon Chambers
706de8e2f1 Add a command to migrate remote configuration entries from Postgres to DynamoDB 2021-12-02 11:13:07 -05:00
Jon Chambers
23bc11f3b6 Introduce a DynamoDB-backed remote config store 2021-12-02 11:13:07 -05:00
Jon Chambers
4eb7dde1c8 Migrate RemoteConfigsTest to Junit 5 2021-12-02 11:13:07 -05:00
Jon Chambers
064861b930 Consolidate profiles store tests and discard ProfilesStore interface 2021-12-02 10:49:54 -05:00
Jon Chambers
afa910bbd7 Drop relational profiles store 2021-12-02 10:49:54 -05:00
Jon Chambers
6aceb24fd2 Drop profile migration tools 2021-12-02 10:49:54 -05:00
Jon Chambers
d94e86781f Migrate username storage from a relational database to DynamoDB 2021-12-01 16:50:18 -05:00
Daniel Gospodinow
0d4a3b1ad4 Fix typo in .gitmodules 2021-12-01 13:26:32 -05:00
Jon Chambers
acfcb18f29 Allow overwriting of previously-delted profiles 2021-12-01 11:59:18 -05:00
Jon Chambers
f7ff8e3837 Add a (failing!) test for deleting, then resetting profiles 2021-12-01 11:59:18 -05:00
Jon Chambers
048e17c62b Use a memoizing supplier instead of a looping thread to cache remote config entries 2021-11-30 16:35:42 -07:00
Jon Chambers
d89b4f7e95 Compare versioned profiles synchronously; log a subset of mismatches for further investigation 2021-11-30 16:35:29 -07:00
Chris Eager
795b226b90 Mark methods that update SignedPreKeys as @ChangesDeviceEnabledState 2021-11-30 10:40:12 -07:00
Jon Chambers
e485c380e0 Change the name of the CSV file argument to avoid upstream naming collisions 2021-11-30 11:31:12 -05:00
Jon Chambers
bb4f4bc441 Modify the "migrate profiles" command to accept a list of UUIDs/versions from a CSV file 2021-11-30 11:22:51 -05:00
Jon Chambers
65b49b2d9c Use a "for each" strategy in profile migration methods 2021-11-24 16:54:30 -05:00
Jon Chambers
9e7010f185 Migrate profiles from a relational database to DynamoDB 2021-11-24 14:48:41 -05:00
Jon Chambers
3bb8e5bb00 Set a TTL for Account entries in the general cache 2021-11-24 14:47:36 -05:00
Jon Chambers
2a4d1da2ca Delete accounts from Redis after they've been deleted from Dynamo 2021-11-24 13:47:53 -05:00
Jon Chambers
6b71b66bd2 Take no action if badge list is omitted entirely 2021-11-24 13:47:39 -05:00
Jon Chambers
ebf24fb125 Add a (failing!) test for clients omitting badges in profile update requests 2021-11-24 13:47:39 -05:00
478 changed files with 23335 additions and 19454 deletions

View File

@@ -146,7 +146,7 @@ ij_java_generate_final_parameters = true
ij_java_if_brace_force = always
ij_java_imports_layout = $*,|,*
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
@@ -1135,7 +1135,7 @@ ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
ij_kotlin_imports_layout = *
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
@@ -1151,9 +1151,9 @@ ij_kotlin_method_call_chain_wrap = off
ij_kotlin_method_parameters_new_line_after_left_paren = false
ij_kotlin_method_parameters_right_paren_on_new_line = false
ij_kotlin_method_parameters_wrap = off
ij_kotlin_name_count_to_use_star_import = 5
ij_kotlin_name_count_to_use_star_import_for_members = 3
ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.**
ij_kotlin_name_count_to_use_star_import = 999
ij_kotlin_name_count_to_use_star_import_for_members = 999
ij_kotlin_packages_to_use_import_on_demand =
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true

0
.github/stale.yml vendored Normal file
View File

View File

@@ -5,14 +5,19 @@ on: [push]
jobs:
build:
runs-on: ubuntu-latest
container: ubuntu:22.04
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@3bc31aaf88e8fc94dc1e632d48af61be5ca8721c
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Set up JDK 17
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # v3.6.0
with:
distribution: 'temurin'
java-version: 17
cache: 'maven'
env:
# work around an issue with actions/runner setting an incorrect HOME in containers, which breaks maven caching
# https://github.com/actions/setup-java/issues/356
HOME: /root
- name: Build with Maven
run: mvn -e -B verify
run: ./mvnw -e -B verify

2
.gitignore vendored
View File

@@ -16,6 +16,7 @@ config/deploy.properties
/service/config/testing.yml
/service/config/deploy.properties
/service/dependency-reduced-pom.xml
.java-version
.opsmanage
put.sh
deployer-staging.properties
@@ -25,4 +26,3 @@ deployer.log
!/service/src/main/resources/org/signal/badges/Badges_en.properties
/service/src/main/resources/org/signal/subscriptions/Subscriptions_*.properties
!/service/src/main/resources/org/signal/subscriptions/Subscriptions_en.properties
/.tx/config

2
.gitmodules vendored
View File

@@ -1,4 +1,4 @@
# Note that the implmentation of the abusive message filter is private; internal
# Note that the implementation of the abusive message filter is private; internal
# developers will need to override this URL with:
#
# ```

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

18
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar

View File

@@ -13,7 +13,7 @@ Cryptography Notice
This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software.
BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted.
See <http://www.wassenaar.org/> for more information.
See <https://www.wassenaar.org/> for more information.
The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms.
The form and manner of this distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code.
@@ -21,6 +21,6 @@ The form and manner of this distribution makes it eligible for export under the
License
---------------------
Copyright 2013-2021 Signal Messenger, LLC
Copyright 2013-2022 Signal Messenger, LLC
Licensed under the AGPLv3: https://www.gnu.org/licenses/agpl-3.0.html

77
event-logger/pom.xml Normal file
View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2022 Signal Messenger, LLC
~ SPDX-License-Identifier: AGPL-3.0-only
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>TextSecureServer</artifactId>
<groupId>org.whispersystems.textsecure</groupId>
<version>JGITVER</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>event-logger</artifactId>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-logging</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json</artifactId>
<version>${kotlinx-serialization.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.event
import java.util.Collections
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
val module = SerializersModule {
polymorphic(Event::class) {
subclass(RemoteConfigSetEvent::class)
subclass(RemoteConfigDeleteEvent::class)
}
}
val jsonFormat = Json { serializersModule = module }
sealed interface Event
@Serializable
data class RemoteConfigSetEvent(
val token: String,
val name: String,
val percentage: Int,
val defaultValue: String? = null,
val value: String? = null,
val hashKey: String? = null,
val uuids: Collection<String> = Collections.emptyList(),
) : Event
@Serializable
data class RemoteConfigDeleteEvent(
val token: String,
val name: String,
) : Event

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.event
import com.google.cloud.logging.LogEntry
import com.google.cloud.logging.Logging
import com.google.cloud.logging.MonitoredResourceUtil
import com.google.cloud.logging.Payload.JsonPayload
import com.google.cloud.logging.Severity
import com.google.protobuf.Struct
import com.google.protobuf.util.JsonFormat
import kotlinx.serialization.encodeToString
interface AdminEventLogger {
fun logEvent(event: Event, labels: Map<String, String>?)
fun logEvent(event: Event) = logEvent(event, null)
}
class NoOpAdminEventLogger : AdminEventLogger {
override fun logEvent(event: Event, labels: Map<String, String>?) {}
}
class GoogleCloudAdminEventLogger(private val logging: Logging, private val projectId: String, private val logName: String) : AdminEventLogger {
override fun logEvent(event: Event, labels: Map<String, String>?) {
val structBuilder = Struct.newBuilder()
JsonFormat.parser().merge(jsonFormat.encodeToString(event), structBuilder)
val struct = structBuilder.build()
val logEntryBuilder = LogEntry.newBuilder(JsonPayload.of(struct))
.setLogName(logName)
.setSeverity(Severity.NOTICE)
.setResource(MonitoredResourceUtil.getResource(projectId, "project"));
if (labels != null) {
logEntryBuilder.setLabels(labels);
}
logging.write(listOf(logEntryBuilder.build()))
}
}

View File

@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>TextSecureServer</artifactId>
<groupId>org.whispersystems.textsecure</groupId>
<version>JGITVER</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gcm-sender-async</artifactId>
<dependencies>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,9 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
public class AuthenticationFailedException extends Exception {
}

View File

@@ -1,9 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
public class InvalidRequestException extends Exception {
}

View File

@@ -1,144 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.whispersystems.gcm.server.internal.GcmRequestEntity;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class Message {
private static final ObjectMapper objectMapper = new ObjectMapper();
private final String collapseKey;
private final Long ttl;
private final Boolean delayWhileIdle;
private final Map<String, String> data;
private final List<String> registrationIds;
private final String priority;
private Message(String collapseKey, Long ttl, Boolean delayWhileIdle,
Map<String, String> data, List<String> registrationIds,
String priority)
{
this.collapseKey = collapseKey;
this.ttl = ttl;
this.delayWhileIdle = delayWhileIdle;
this.data = data;
this.registrationIds = registrationIds;
this.priority = priority;
}
public String serialize() throws JsonProcessingException {
GcmRequestEntity requestEntity = new GcmRequestEntity(collapseKey, ttl, delayWhileIdle,
data, registrationIds, priority);
return objectMapper.writeValueAsString(requestEntity);
}
/**
* Construct a new Message using a Builder.
* @return A new Builder.
*/
public static Builder newBuilder() {
return new Builder();
}
public static class Builder {
private String collapseKey = null;
private Long ttl = null;
private Boolean delayWhileIdle = null;
private Map<String, String> data = null;
private List<String> registrationIds = new LinkedList<>();
private String priority = null;
private Builder() {}
/**
* @param collapseKey The GCM collapse key to use (optional).
* @return The Builder.
*/
public Builder withCollapseKey(String collapseKey) {
this.collapseKey = collapseKey;
return this;
}
/**
* @param seconds The TTL (in seconds) for this message (optional).
* @return The Builder.
*/
public Builder withTtl(long seconds) {
this.ttl = seconds;
return this;
}
/**
* @param delayWhileIdle Set GCM delay_while_idle (optional).
* @return The Builder.
*/
public Builder withDelayWhileIdle(boolean delayWhileIdle) {
this.delayWhileIdle = delayWhileIdle;
return this;
}
/**
* Set a key in the GCM JSON payload delivered to the application (optional).
* @param key The key to set.
* @param value The value to set.
* @return The Builder.
*/
public Builder withDataPart(String key, String value) {
if (data == null) {
data = new HashMap<>();
}
data.put(key, value);
return this;
}
/**
* Set the destination GCM registration ID (mandatory).
* @param registrationId The destination GCM registration ID.
* @return The Builder.
*/
public Builder withDestination(String registrationId) {
this.registrationIds.clear();
this.registrationIds.add(registrationId);
return this;
}
/**
* Set the GCM message priority (optional).
*
* @param priority Valid values are "normal" and "high."
* On iOS, these correspond to APNs priority 5 and 10.
* @return The Builder.
*/
public Builder withPriority(String priority) {
this.priority = priority;
return this;
}
/**
* Construct a message object.
*
* @return An immutable message object, as configured by this builder.
*/
public Message build() {
if (registrationIds.isEmpty()) {
throw new IllegalArgumentException("You must specify a destination!");
}
return new Message(collapseKey, ttl, delayWhileIdle, data, registrationIds, priority);
}
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
/**
* The result of a GCM send operation.
*/
public class Result {
private final String canonicalRegistrationId;
private final String messageId;
private final String error;
Result(String canonicalRegistrationId, String messageId, String error) {
this.canonicalRegistrationId = canonicalRegistrationId;
this.messageId = messageId;
this.error = error;
}
/**
* Returns the "canonical" GCM registration ID for this destination.
* See GCM documentation for details.
* @return The canonical GCM registration ID.
*/
public String getCanonicalRegistrationId() {
return canonicalRegistrationId;
}
/**
* @return If a "canonical" GCM registration ID is present in the response.
*/
public boolean hasCanonicalRegistrationId() {
return canonicalRegistrationId != null && !canonicalRegistrationId.isEmpty();
}
/**
* @return The assigned GCM message ID, if successful.
*/
public String getMessageId() {
return messageId;
}
/**
* @return The raw error string, if present.
*/
public String getError() {
return error;
}
/**
* @return If the send was a success.
*/
public boolean isSuccess() {
return messageId != null && !messageId.isEmpty() && (error == null || error.isEmpty());
}
/**
* @return If the destination GCM registration ID is no longer registered.
*/
public boolean isUnregistered() {
return "NotRegistered".equals(error);
}
/**
* @return If messages to this device are being throttled.
*/
public boolean isThrottled() {
return "DeviceMessageRateExceeded".equals(error);
}
/**
* @return If the destination GCM registration ID is invalid.
*/
public boolean isInvalidRegistrationId() {
return "InvalidRegistration".equals(error);
}
}

View File

@@ -1,154 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.annotations.VisibleForTesting;
import io.github.resilience4j.retry.IntervalFunction;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import org.whispersystems.gcm.server.internal.GcmResponseEntity;
import org.whispersystems.gcm.server.internal.GcmResponseListEntity;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeoutException;
/**
* The main interface to sending GCM messages. Thread safe.
*
* @author Moxie Marlinspike
*/
public class Sender {
private static final String PRODUCTION_URL = "https://fcm.googleapis.com/fcm/send";
private final String authorizationHeader;
private final URI uri;
private final Retry retry;
private final ObjectMapper mapper;
private final ScheduledExecutorService executorService;
private final HttpClient[] clients = new HttpClient[10];
private final SecureRandom random = new SecureRandom();
/**
* Construct a Sender instance.
*
* @param apiKey Your application's GCM API key.
*/
public Sender(String apiKey, ObjectMapper mapper) {
this(apiKey, mapper, 10);
}
/**
* Construct a Sender instance with a specified retry count.
*
* @param apiKey Your application's GCM API key.
* @param retryCount The number of retries to attempt on a network error or 500 response.
*/
public Sender(String apiKey, ObjectMapper mapper, int retryCount) {
this(apiKey, mapper, retryCount, PRODUCTION_URL);
}
@VisibleForTesting
public Sender(String apiKey, ObjectMapper mapper, int retryCount, String url) {
this.mapper = mapper;
this.executorService = Executors.newSingleThreadScheduledExecutor();
this.uri = URI.create(url);
this.authorizationHeader = String.format("key=%s", apiKey);
this.retry = Retry.of("fcm-sender", RetryConfig.custom()
.maxAttempts(retryCount)
.intervalFunction(IntervalFunction.ofExponentialRandomBackoff(Duration.ofMillis(100), 2.0))
.retryOnException(this::isRetryableException)
.build());
for (int i=0;i<clients.length;i++) {
this.clients[i] = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
}
}
private boolean isRetryableException(Throwable throwable) {
while (throwable instanceof CompletionException) {
throwable = throwable.getCause();
}
return throwable instanceof ServerFailedException ||
throwable instanceof TimeoutException ||
throwable instanceof IOException;
}
/**
* Asynchronously send a message.
*
* @param message The message to send.
* @return A future.
*/
public CompletableFuture<Result> send(Message message) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(uri)
.header("Authorization", authorizationHeader)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(message.serialize()))
.timeout(Duration.ofSeconds(10))
.build();
return retry.executeCompletionStage(executorService,
() -> getClient().sendAsync(request, BodyHandlers.ofByteArray())
.thenApply(response -> {
switch (response.statusCode()) {
case 400: throw new CompletionException(new InvalidRequestException());
case 401: throw new CompletionException(new AuthenticationFailedException());
case 204:
case 200: return response.body();
default: throw new CompletionException(new ServerFailedException("Bad status: " + response.statusCode()));
}
})
.thenApply(responseBytes -> {
try {
List<GcmResponseEntity> responseList = mapper.readValue(responseBytes, GcmResponseListEntity.class).getResults();
if (responseList == null || responseList.size() == 0) {
throw new CompletionException(new IOException("Empty response list!"));
}
GcmResponseEntity responseEntity = responseList.get(0);
return new Result(responseEntity.getCanonicalRegistrationId(),
responseEntity.getMessageId(),
responseEntity.getError());
} catch (IOException e) {
throw new CompletionException(e);
}
})).toCompletableFuture();
} catch (JsonProcessingException e) {
return CompletableFuture.failedFuture(e);
}
}
public Retry getRetry() {
return retry;
}
private HttpClient getClient() {
return clients[random.nextInt(clients.length)];
}
}

View File

@@ -1,15 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
public class ServerFailedException extends Exception {
public ServerFailedException(String message) {
super(message);
}
public ServerFailedException(Exception e) {
super(e);
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server.internal;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class GcmRequestEntity {
@JsonProperty(value = "collapse_key")
private String collapseKey;
@JsonProperty(value = "time_to_live")
private Long ttl;
@JsonProperty(value = "delay_while_idle")
private Boolean delayWhileIdle;
@JsonProperty(value = "data")
private Map<String, String> data;
@JsonProperty(value = "registration_ids")
private List<String> registrationIds;
@JsonProperty
private String priority;
public GcmRequestEntity(String collapseKey, Long ttl, Boolean delayWhileIdle,
Map<String, String> data, List<String> registrationIds,
String priority)
{
this.collapseKey = collapseKey;
this.ttl = ttl;
this.delayWhileIdle = delayWhileIdle;
this.data = data;
this.registrationIds = registrationIds;
this.priority = priority;
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server.internal;
import com.fasterxml.jackson.annotation.JsonProperty;
public class GcmResponseEntity {
@JsonProperty(value = "message_id")
private String messageId;
@JsonProperty(value = "registration_id")
private String canonicalRegistrationId;
@JsonProperty
private String error;
public String getMessageId() {
return messageId;
}
public String getCanonicalRegistrationId() {
return canonicalRegistrationId;
}
public String getError() {
return error;
}
}

View File

@@ -1,19 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server.internal;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class GcmResponseListEntity {
@JsonProperty
private List<GcmResponseEntity> results;
public List<GcmResponseEntity> getResults() {
return results;
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
import static org.whispersystems.gcm.server.util.JsonHelpers.jsonFixture;
public class MessageTest {
@Test
public void testMinimal() throws IOException {
Message message = Message.newBuilder()
.withDestination("1")
.build();
assertEquals(message.serialize(), jsonFixture("fixtures/message-minimal.json"));
}
@Test
public void testComplete() throws IOException {
Message message = Message.newBuilder()
.withDestination("1")
.withCollapseKey("collapse")
.withDelayWhileIdle(true)
.withTtl(10)
.withPriority("high")
.build();
assertEquals(message.serialize(), jsonFixture("fixtures/message-complete.json"));
}
@Test
public void testWithData() throws IOException {
Message message = Message.newBuilder()
.withDestination("2")
.withDataPart("key1", "value1")
.withDataPart("key2", "value2")
.build();
assertEquals(message.serialize(), jsonFixture("fixtures/message-data.json"));
}
}

View File

@@ -1,200 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.any;
import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.whispersystems.gcm.server.util.FixtureHelpers.fixture;
import static org.whispersystems.gcm.server.util.JsonHelpers.jsonFixture;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tomakehurst.wiremock.client.CountMatchingStrategy;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Rule;
import org.junit.Test;
public class SenderTest {
@Rule
public WireMockRule wireMock = new WireMockRule(options().dynamicPort().dynamicHttpsPort());
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Test
public void testSuccess() throws InterruptedException, ExecutionException, TimeoutException, IOException {
wireMock.stubFor(any(anyUrl())
.willReturn(aResponse()
.withStatus(200)
.withBody(fixture("fixtures/response-success.json"))));
Sender sender = new Sender("foobarbaz", mapper, 10, "http://localhost:" + wireMock.port() + "/gcm/send");
CompletableFuture<Result> future = sender.send(Message.newBuilder().withDestination("1").build());
Result result = future.get(10, TimeUnit.SECONDS);
assertTrue(result.isSuccess());
assertFalse(result.isThrottled());
assertFalse(result.isUnregistered());
assertEquals(result.getMessageId(), "1:08");
assertNull(result.getError());
assertNull(result.getCanonicalRegistrationId());
verify(1, postRequestedFor(urlEqualTo("/gcm/send"))
.withHeader("Authorization", equalTo("key=foobarbaz"))
.withHeader("Content-Type", equalTo("application/json"))
.withRequestBody(equalTo(jsonFixture("fixtures/message-minimal.json"))));
}
@Test
public void testBadApiKey() throws InterruptedException, TimeoutException {
wireMock.stubFor(any(anyUrl())
.willReturn(aResponse()
.withStatus(401)));
Sender sender = new Sender("foobar", mapper, 10, "http://localhost:" + wireMock.port() + "/gcm/send");
CompletableFuture<Result> future = sender.send(Message.newBuilder().withDestination("1").build());
try {
future.get(10, TimeUnit.SECONDS);
throw new AssertionError();
} catch (ExecutionException ee) {
assertTrue(ee.getCause() instanceof AuthenticationFailedException);
}
verify(1, anyRequestedFor(anyUrl()));
}
@Test
public void testBadRequest() throws TimeoutException, InterruptedException {
wireMock.stubFor(any(anyUrl())
.willReturn(aResponse()
.withStatus(400)));
Sender sender = new Sender("foobarbaz", mapper, 10, "http://localhost:" + wireMock.port() + "/gcm/send");
CompletableFuture<Result> future = sender.send(Message.newBuilder().withDestination("1").build());
try {
future.get(10, TimeUnit.SECONDS);
throw new AssertionError();
} catch (ExecutionException e) {
assertTrue(e.getCause() instanceof InvalidRequestException);
}
verify(1, anyRequestedFor(anyUrl()));
}
@Test
public void testServerError() throws TimeoutException, InterruptedException {
wireMock.stubFor(any(anyUrl())
.willReturn(aResponse()
.withStatus(503)));
Sender sender = new Sender("foobarbaz", mapper, 3, "http://localhost:" + wireMock.port() + "/gcm/send");
CompletableFuture<Result> future = sender.send(Message.newBuilder().withDestination("1").build());
try {
future.get(10, TimeUnit.SECONDS);
throw new AssertionError();
} catch (ExecutionException ee) {
assertTrue(ee.getCause() instanceof ServerFailedException);
}
verify(3, anyRequestedFor(anyUrl()));
}
@Test
public void testServerErrorRecovery() throws InterruptedException, ExecutionException, TimeoutException {
wireMock.stubFor(any(anyUrl()).willReturn(aResponse().withStatus(503)));
Sender sender = new Sender("foobarbaz", mapper, 4, "http://localhost:" + wireMock.port() + "/gcm/send");
CompletableFuture<Result> future = sender.send(Message.newBuilder().withDestination("1").build());
// up to three failures can happen, with 100ms exponential backoff
// if we end up using the fourth, and finaly try, it would be after ~700 ms
CompletableFuture.delayedExecutor(300, TimeUnit.MILLISECONDS).execute(() ->
wireMock.stubFor(any(anyUrl())
.willReturn(aResponse()
.withStatus(200)
.withBody(fixture("fixtures/response-success.json"))))
);
Result result = future.get(10, TimeUnit.SECONDS);
verify(new CountMatchingStrategy(CountMatchingStrategy.GREATER_THAN, 1), anyRequestedFor(anyUrl()));
assertTrue(result.isSuccess());
assertFalse(result.isThrottled());
assertFalse(result.isUnregistered());
assertEquals(result.getMessageId(), "1:08");
assertNull(result.getError());
assertNull(result.getCanonicalRegistrationId());
}
@Test
public void testNetworkError() throws TimeoutException, InterruptedException {
wireMock.stubFor(any(anyUrl())
.willReturn(ok()));
Sender sender = new Sender("foobarbaz", mapper ,2, "http://localhost:" + wireMock.port() + "/gcm/send");
wireMock.stop();
CompletableFuture<Result> future = sender.send(Message.newBuilder().withDestination("1").build());
try {
future.get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) {
assertTrue(e.getCause() instanceof IOException);
}
}
@Test
public void testNotRegistered() throws InterruptedException, ExecutionException, TimeoutException {
wireMock.stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200)
.withBody(fixture("fixtures/response-not-registered.json"))));
Sender sender = new Sender("foobarbaz", mapper,2, "http://localhost:" + wireMock.port() + "/gcm/send");
CompletableFuture<Result> future = sender.send(Message.newBuilder()
.withDestination("2")
.withDataPart("message", "new message!")
.build());
Result result = future.get(10, TimeUnit.SECONDS);
assertFalse(result.isSuccess());
assertTrue(result.isUnregistered());
assertFalse(result.isThrottled());
assertEquals(result.getError(), "NotRegistered");
}
}

View File

@@ -1,89 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static junit.framework.TestCase.assertTrue;
import static org.whispersystems.gcm.server.util.FixtureHelpers.fixture;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
public class SimultaneousSenderTest {
@Rule
public WireMockRule wireMock = new WireMockRule(WireMockConfiguration.options().dynamicPort().dynamicHttpsPort());
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Test
public void testSimultaneousSuccess() throws TimeoutException, InterruptedException, ExecutionException, JsonProcessingException {
stubFor(post(urlPathEqualTo("/gcm/send"))
.willReturn(aResponse()
.withStatus(200)
.withBody(fixture("fixtures/response-success.json"))));
Sender sender = new Sender("foobarbaz", mapper, 2, "http://localhost:" + wireMock.port() + "/gcm/send");
List<CompletableFuture<Result>> results = new LinkedList<>();
for (int i=0;i<1000;i++) {
results.add(sender.send(Message.newBuilder().withDestination("1").build()));
}
for (CompletableFuture<Result> future : results) {
Result result = future.get(60, TimeUnit.SECONDS);
if (!result.isSuccess()) {
throw new AssertionError(result.getError());
}
}
}
@Test
@Ignore
public void testSimultaneousFailure() throws TimeoutException, InterruptedException {
stubFor(post(urlPathEqualTo("/gcm/send"))
.willReturn(aResponse()
.withStatus(503)));
Sender sender = new Sender("foobarbaz", mapper, 2, "http://localhost:" + wireMock.port() + "/gcm/send");
List<CompletableFuture<Result>> futures = new LinkedList<>();
for (int i=0;i<1000;i++) {
futures.add(sender.send(Message.newBuilder().withDestination("1").build()));
}
for (CompletableFuture<Result> future : futures) {
try {
Result result = future.get(60, TimeUnit.SECONDS);
} catch (ExecutionException e) {
assertTrue(e.getCause().toString(), e.getCause() instanceof ServerFailedException);
}
}
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server.util;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* A set of helper method for fixture files.
*/
public class FixtureHelpers {
private FixtureHelpers() { /* singleton */ }
/**
* Reads the given fixture file from the classpath (e. g. {@code src/test/resources})
* and returns its contents as a UTF-8 string.
*
* @param filename the filename of the fixture file
* @return the contents of {@code src/test/resources/{filename}}
* @throws IllegalArgumentException if an I/O error occurs.
*/
public static String fixture(String filename) {
return fixture(filename, Charsets.UTF_8);
}
/**
* Reads the given fixture file from the classpath (e. g. {@code src/test/resources})
* and returns its contents as a string.
*
* @param filename the filename of the fixture file
* @param charset the character set of {@code filename}
* @return the contents of {@code src/test/resources/{filename}}
* @throws IllegalArgumentException if an I/O error occurs.
*/
private static String fixture(String filename, Charset charset) {
try {
return Resources.toString(Resources.getResource(filename), charset).trim();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.gcm.server.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import static org.whispersystems.gcm.server.util.FixtureHelpers.fixture;
public class JsonHelpers {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static String asJson(Object object) throws JsonProcessingException {
return objectMapper.writeValueAsString(object);
}
public static <T> T fromJson(String value, Class<T> clazz) throws IOException {
return objectMapper.readValue(value, clazz);
}
public static String jsonFixture(String filename) throws IOException {
return objectMapper.writeValueAsString(objectMapper.readValue(fixture(filename), JsonNode.class));
}
}

View File

@@ -1,7 +0,0 @@
{
"priority" : "high",
"collapse_key" : "collapse",
"time_to_live" : 10,
"delay_while_idle" : true,
"registration_ids" : ["1"]
}

View File

@@ -1,7 +0,0 @@
{
"data" : {
"key1" : "value1",
"key2" : "value2"
},
"registration_ids" : ["2"]
}

View File

@@ -1,3 +0,0 @@
{
"registration_ids" : ["1"]
}

View File

@@ -1,8 +0,0 @@
{ "multicast_id": 216,
"success": 0,
"failure": 1,
"canonical_ids": 0,
"results": [
{ "error": "NotRegistered"}
]
}

View File

@@ -1,8 +0,0 @@
{ "multicast_id": 108,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{ "message_id": "1:08" }
]
}

View File

@@ -1,8 +0,0 @@
<configuration>
<!-- Turning down the wiremock logging -->
<logger name="com.github.tomakehurst.wiremock" level="WARN"/>
<logger name="wiremock.org" level="ERROR"/>
<logger name="WireMock" level="WARN"/>
<!-- wiremock has per endpoint servlet logging -->
<logger name="/" level="WARN"/>
</configuration>

316
mvnw vendored Executable file
View File

@@ -0,0 +1,316 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`\\unset -f command; \\command -v java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

188
mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,188 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

125
pom.xml
View File

@@ -21,40 +21,56 @@
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>ossrh-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<modules>
<module>event-logger</module>
<module>redis-dispatch</module>
<module>websocket-resources</module>
<module>gcm-sender-async</module>
<module>service</module>
</modules>
<properties>
<aws.sdk.version>1.11.939</aws.sdk.version>
<aws.sdk2.version>2.16.66</aws.sdk2.version>
<aws.sdk.version>1.12.287</aws.sdk.version>
<aws.sdk2.version>2.17.258</aws.sdk2.version>
<commons-codec.version>1.15</commons-codec.version>
<commons-csv.version>1.8</commons-csv.version>
<commons-io.version>2.9.0</commons-io.version>
<dropwizard.version>2.0.25</dropwizard.version>
<dropwizard.version>2.0.32</dropwizard.version>
<dropwizard-metrics-datadog.version>1.1.13</dropwizard-metrics-datadog.version>
<gson.version>2.8.8</gson.version>
<grpc.version>1.49.2</grpc.version>
<gson.version>2.9.0</gson.version>
<guava.version>30.1.1-jre</guava.version>
<jackson.version>2.13.4</jackson.version>
<jaxb.version>2.3.1</jaxb.version>
<jedis.version>2.9.0</jedis.version>
<lettuce.version>6.0.4.RELEASE</lettuce.version>
<libphonenumber.version>8.12.33</libphonenumber.version>
<logstash.logback.version>6.6</logstash.logback.version>
<micrometer.version>1.5.3</micrometer.version>
<mockito.version>4.0.0</mockito.version>
<netty.version>4.1.65.Final</netty.version>
<netty.tcnative-boringssl-static.version>2.0.39.Final</netty.tcnative-boringssl-static.version>
<kotlin.version>1.7.10</kotlin.version>
<kotlinx-serialization.version>1.4.0</kotlinx-serialization.version>
<lettuce.version>6.2.0.RELEASE</lettuce.version>
<libphonenumber.version>8.12.54</libphonenumber.version>
<logstash.logback.version>7.0.1</logstash.logback.version>
<micrometer.version>1.9.3</micrometer.version>
<mockito.version>4.7.0</mockito.version>
<netty.version>4.1.82.Final</netty.version>
<opentest4j.version>1.2.0</opentest4j.version>
<postgresql.version>9.4-1201-jdbc41</postgresql.version>
<protobuf.version>3.17.1</protobuf.version>
<pushy.version>0.15.0</pushy.version>
<resilience4j.version>1.5.0</resilience4j.version>
<protobuf.version>3.21.7</protobuf.version>
<pushy.version>0.15.1</pushy.version>
<resilience4j.version>1.7.0</resilience4j.version>
<semver4j.version>3.1.0</semver4j.version>
<slf4j.version>1.7.30</slf4j.version>
<stripe.version>20.79.0</stripe.version>
<stripe.version>21.2.0</stripe.version>
<vavr.version>0.10.4</vavr.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@@ -65,6 +81,13 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>${jackson.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-dependencies</artifactId>
@@ -72,6 +95,20 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-bom</artifactId>
<version>${grpc.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Needed for gRPC with Java 9+ -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
@@ -96,7 +133,7 @@
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>20.9.0</version>
<version>26.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -114,7 +151,13 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>2020.0.23</version> <!-- 3.4.x, see https://github.com/reactor/reactor#bom-versioning-scheme -->
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
@@ -161,10 +204,9 @@
<version>${lettuce.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${netty.tcnative-boringssl-static.version}</version>
<scope>runtime</scope>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>${vavr.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
@@ -210,12 +252,6 @@
<version>${opentest4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -253,6 +289,24 @@
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>libsignal-server</artifactId>
<version>0.21.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>2.17.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@@ -266,7 +320,7 @@
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.31.0</version>
<version>2.34.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
@@ -291,8 +345,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
@@ -336,13 +390,16 @@
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.18.0:exe:${os.detected.classifier}</protocArtifact>
<checkStaleness>true</checkStaleness>
<checkStaleness>false</checkStaleness>
<protocArtifact>com.google.protobuf:protoc:3.21.1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
<goal>test-compile</goal>
</goals>
</execution>

View File

@@ -115,7 +115,7 @@ public class PubSubConnection {
byte[] channelName = inputStream.readFully(channelNameHeader.getStringLength());
inputStream.readLine();
IntReply subscriptionCount = new IntReply(inputStream.readLine());
new IntReply(inputStream.readLine());
return new String(channelName);
}

View File

@@ -4,28 +4,25 @@
*/
package org.whispersystems.dispatch;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.whispersystems.dispatch.io.RedisPubSubConnectionFactory;
import org.whispersystems.dispatch.redis.PubSubConnection;
import org.whispersystems.dispatch.redis.PubSubReply;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
import org.whispersystems.dispatch.io.RedisPubSubConnectionFactory;
import org.whispersystems.dispatch.redis.PubSubConnection;
import org.whispersystems.dispatch.redis.PubSubReply;
public class DispatchManagerTest {
private PubSubConnection pubSubConnection;
@@ -33,31 +30,23 @@ public class DispatchManagerTest {
private DispatchManager dispatchManager;
private PubSubReplyInputStream pubSubReplyInputStream;
@Rule
public ExternalResource resource = new ExternalResource() {
@Override
protected void before() throws Throwable {
pubSubConnection = mock(PubSubConnection.class );
socketFactory = mock(RedisPubSubConnectionFactory.class);
pubSubReplyInputStream = new PubSubReplyInputStream();
@BeforeEach
void setUp() throws Exception {
pubSubConnection = mock(PubSubConnection.class );
socketFactory = mock(RedisPubSubConnectionFactory.class);
pubSubReplyInputStream = new PubSubReplyInputStream();
when(socketFactory.connect()).thenReturn(pubSubConnection);
when(pubSubConnection.read()).thenAnswer(new Answer<PubSubReply>() {
@Override
public PubSubReply answer(InvocationOnMock invocationOnMock) throws Throwable {
return pubSubReplyInputStream.read();
}
});
when(socketFactory.connect()).thenReturn(pubSubConnection);
when(pubSubConnection.read()).thenAnswer((Answer<PubSubReply>) invocationOnMock -> pubSubReplyInputStream.read());
dispatchManager = new DispatchManager(socketFactory, Optional.empty());
dispatchManager.start();
}
dispatchManager = new DispatchManager(socketFactory, Optional.empty());
dispatchManager.start();
}
@Override
protected void after() {
}
};
@AfterEach
void tearDown() {
dispatchManager.shutdown();
}
@Test
public void testConnect() {
@@ -65,7 +54,7 @@ public class DispatchManagerTest {
}
@Test
public void testSubscribe() throws IOException {
public void testSubscribe() {
DispatchChannel dispatchChannel = mock(DispatchChannel.class);
dispatchManager.subscribe("foo", dispatchChannel);
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
@@ -74,7 +63,7 @@ public class DispatchManagerTest {
}
@Test
public void testSubscribeUnsubscribe() throws IOException {
public void testSubscribeUnsubscribe() {
DispatchChannel dispatchChannel = mock(DispatchChannel.class);
dispatchManager.subscribe("foo", dispatchChannel);
dispatchManager.unsubscribe("foo", dispatchChannel);
@@ -86,7 +75,7 @@ public class DispatchManagerTest {
}
@Test
public void testMessages() throws IOException {
public void testMessages() {
DispatchChannel fooChannel = mock(DispatchChannel.class);
DispatchChannel barChannel = mock(DispatchChannel.class);

View File

@@ -4,9 +4,9 @@
*/
package org.whispersystems.dispatch.redis;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -18,12 +18,12 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.SecureRandom;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class PubSubConnectionTest {
class PubSubConnectionTest {
private static final String REPLY = "*3\r\n" +
"$9\r\n" +
@@ -60,8 +60,7 @@ public class PubSubConnectionTest {
@Test
public void testSubscribe() throws IOException {
// ByteChannel byteChannel = mock(ByteChannel.class);
void testSubscribe() throws IOException {
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
when(socket.getOutputStream()).thenReturn(outputStream);
@@ -76,7 +75,7 @@ public class PubSubConnectionTest {
}
@Test
public void testUnsubscribe() throws IOException {
void testUnsubscribe() throws IOException {
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
when(socket.getOutputStream()).thenReturn(outputStream);
@@ -91,7 +90,7 @@ public class PubSubConnectionTest {
}
@Test
public void testTricklyResponse() throws Exception {
void testTricklyResponse() throws Exception {
InputStream inputStream = mockInputStreamFor(new TrickleInputStream(REPLY.getBytes()));
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
@@ -103,7 +102,7 @@ public class PubSubConnectionTest {
}
@Test
public void testFullResponse() throws Exception {
void testFullResponse() throws Exception {
InputStream inputStream = mockInputStreamFor(new FullInputStream(REPLY.getBytes()));
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
@@ -115,7 +114,7 @@ public class PubSubConnectionTest {
}
@Test
public void testRandomLengthResponse() throws Exception {
void testRandomLengthResponse() throws Exception {
InputStream inputStream = mockInputStreamFor(new RandomInputStream(REPLY.getBytes()));
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );

View File

@@ -5,42 +5,41 @@
package org.whispersystems.dispatch.redis.protocol;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
class ArrayReplyHeaderTest {
public class ArrayReplyHeaderTest {
@Test(expected = IOException.class)
public void testNull() throws IOException {
new ArrayReplyHeader(null);
}
@Test(expected = IOException.class)
public void testBadPrefix() throws IOException {
new ArrayReplyHeader(":3");
}
@Test(expected = IOException.class)
public void testEmpty() throws IOException {
new ArrayReplyHeader("");
}
@Test(expected = IOException.class)
public void testTruncated() throws IOException {
new ArrayReplyHeader("*");
}
@Test(expected = IOException.class)
public void testBadNumber() throws IOException {
new ArrayReplyHeader("*ABC");
@Test
void testNull() {
assertThrows(IOException.class, () -> new ArrayReplyHeader(null));
}
@Test
public void testValid() throws IOException {
void testBadPrefix() {
assertThrows(IOException.class, () -> new ArrayReplyHeader(":3"));
}
@Test
void testEmpty() {
assertThrows(IOException.class, () -> new ArrayReplyHeader(""));
}
@Test
void testTruncated() {
assertThrows(IOException.class, () -> new ArrayReplyHeader("*"));
}
@Test
void testBadNumber() {
assertThrows(IOException.class, () -> new ArrayReplyHeader("*ABC"));
}
@Test
void testValid() throws IOException {
assertEquals(4, new ArrayReplyHeader("*4").getElementCount());
}

View File

@@ -4,36 +4,36 @@
*/
package org.whispersystems.dispatch.redis.protocol;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
class IntReplyHeaderTest {
public class IntReplyHeaderTest {
@Test(expected = IOException.class)
public void testNull() throws IOException {
new IntReply(null);
}
@Test(expected = IOException.class)
public void testEmpty() throws IOException {
new IntReply("");
}
@Test(expected = IOException.class)
public void testBadNumber() throws IOException {
new IntReply(":A");
}
@Test(expected = IOException.class)
public void testBadFormat() throws IOException {
new IntReply("*");
@Test
void testNull() {
assertThrows(IOException.class, () -> new IntReply(null));
}
@Test
public void testValid() throws IOException {
void testEmpty() {
assertThrows(IOException.class, () -> new IntReply(""));
}
@Test
void testBadNumber() {
assertThrows(IOException.class, () -> new IntReply(":A"));
}
@Test
void testBadFormat() {
assertThrows(IOException.class, () -> new IntReply("*"));
}
@Test
void testValid() throws IOException {
assertEquals(23, new IntReply(":23").getValue());
}
}

View File

@@ -4,48 +4,32 @@
*/
package org.whispersystems.dispatch.redis.protocol;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
public class StringReplyHeaderTest {
class StringReplyHeaderTest {
@Test
public void testNull() {
try {
new StringReplyHeader(null);
throw new AssertionError();
} catch (IOException e) {
// good
}
void testNull() {
assertThrows(IOException.class, () -> new StringReplyHeader(null));
}
@Test
public void testBadNumber() {
try {
new StringReplyHeader("$100A");
throw new AssertionError();
} catch (IOException e) {
// good
}
void testBadNumber() {
assertThrows(IOException.class, () -> new StringReplyHeader("$100A"));
}
@Test
public void testBadPrefix() {
try {
new StringReplyHeader("*");
throw new AssertionError();
} catch (IOException e) {
// good
}
void testBadPrefix() {
assertThrows(IOException.class, () -> new StringReplyHeader("*"));
}
@Test
public void testValid() throws IOException {
void testValid() throws IOException {
assertEquals(1000, new StringReplyHeader("$1000").getStringLength());
}
}

View File

@@ -3,6 +3,13 @@
# `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.
adminEventLoggingConfiguration:
credentials: |
Some credentials text
blah blah blah
projectId: some-project-id
logName: some-log-name
stripe:
apiKey: unset
idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
@@ -13,50 +20,48 @@ dynamoDbClientConfiguration:
region: us-west-2 # AWS Region
dynamoDbTables:
accounts:
tableName: Example_Accounts
phoneNumberTableName: Example_Accounts_PhoneNumbers
phoneNumberIdentifierTableName: Example_Accounts_PhoneNumberIdentifiers
usernamesTableName: Example_Accounts_Usernames
scanPageSize: 100
deletedAccounts:
tableName: Example_DeletedAccounts
needsReconciliationIndexName: NeedsReconciliation
deletedAccountsLock:
tableName: Example_DeletedAccountsLock
issuedReceipts:
tableName: Example_IssuedReceipts
expiration: P30D # Duration of time until rows expire
generator: abcdefg12345678= # random base64-encoded binary sequence
keys:
tableName: Example_Keys
messages:
tableName: Example_Messages
expiration: P30D # Duration of time until rows expire
pendingAccounts:
tableName: Example_PendingAccounts
pendingDevices:
tableName: Example_PendingDevices
phoneNumberIdentifiers:
tableName: Example_PhoneNumberIdentifiers
profiles:
tableName: Example_Profiles
pushChallenge:
tableName: Example_PushChallenge
redeemedReceipts:
tableName: Example_RedeemedReceipts
expiration: P30D # Duration of time until rows expire
remoteConfig:
tableName: Example_RemoteConfig
reportMessage:
tableName: Example_ReportMessage
reservedUsernames:
tableName: Example_ReservedUsernames
subscriptions:
tableName: Example_Subscriptions
twilio: # Twilio gateway configuration
accountId: unset
accountToken: unset
nanpaMessagingServiceSid: unset # Twilio SID for the messaging service to use for NANPA.
messagingServiceSid: unset # Twilio SID for the message service to use for non-NANPA.
verifyServiceSid: unset # Twilio SID for a Verify service
localDomain: example.com # Domain Twilio can connect back to for calls. Should be domain of your service.
defaultClientVerificationTexts:
ios: example %1$s # Text to use for the verification message on iOS. Will be passed to String.format with the verification code as argument 1.
androidNg: example %1$s # Text to use for the verification message on android-ng client types. Will be passed to String.format with the verification code as argument 1.
android202001: example %1$s # Text to use for the verification message on android-2020-01 client types. Will be passed to String.format with the verification code as argument 1.
android202103: example %1$s # Text to use for the verification message on android-2021-03 client types. Will be passed to String.format with the verification code as argument 1.
generic: example %1$s # Text to use when the client type is unrecognized. Will be passed to String.format with the verification code as argument 1.
regionalClientVerificationTexts: # Map of country codes to custom texts
999: # example country code
ios: example %1$s # all keys from defaultClientVerificationTexts are required
androidNg: example %1$s
android202001: example %1$s
android202103: example %1$s
generic: example %1$s
androidAppHash: example # Hash appended to Android
verifyServiceFriendlyName: example # Service name used in template. Requires Twilio account rep to enable
push:
queueSize: 1000 # Size of push pending queue
turn: # TURN server configuration
secret: example # TURN server secret
uris:
- stun:example.com:80
- stun:another.example.com:443
- turn:example.com:443?transport=udp
- turn:ya.example.com:80?transport=udp
cacheCluster: # Redis server configuration for cache cluster
configurationUri: redis://redis.example.com:6379/
@@ -87,32 +92,34 @@ directory:
- replicationName: example # CDS replication name
replicationUrl: cds.example.com # CDS replication endpoint base url
replicationPassword: example # CDS replication endpoint password
replicationCaCertificate: | # CDS replication endpoint TLS certificate trust root
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
replicationCaCertificates: # CDS replication endpoint TLS certificate trust root
- |
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
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
messageCache: # Redis server configuration for message store cache
persistDelayMinutes: 1
@@ -122,53 +129,6 @@ messageCache: # Redis server configuration for message store cache
metricsCluster:
configurationUri: redis://redis.example.com:6379/
messageDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_Messages
keysDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_PreKeys
accountsDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_Accounts
phoneNumberTableName: Example_Accounts_PhoneNumbers
phoneNumberIdentifierTableName: Example_Accounts_PhoneNumberIdentifiers
deletedAccountsDynamoDb: # DynamoDb table configuration
region: us-west-2
tableName: Example_DeletedAccounts
needsReconciliationIndexName: NeedsReconciliation
deletedAccountsLockDynamoDb: # DynamoDb table configuration
region: us-west-2
tableName: Example_DeletedAccountsLock
pendingAccountsDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_PendingAccounts
pendingDevicesDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_PendingDevices
reservedUsernamesDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_ReservedUsernames
phoneNumberIdentifiersDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_PhoneNumberIdentifiers
pushChallengeDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_PushChallenges
reportMessageDynamoDb: # DynamoDB table configuration
region: us-west-2
tableName: Example_ReportedMessages
awsAttachments: # AWS S3 configuration
accessKey: test
accessSecret: test
@@ -210,18 +170,6 @@ gcpAttachments: # GCP Storage configuration
AAAAAAAA
-----END PRIVATE KEY-----
abuseDatabase: # Postgresql database configuration
driverClass: org.postgresql.Driver
user: example
password: password
url: jdbc:postgresql://example.com:5432/abusedb
accountsDatabase: # Postgresql database configuration
driverClass: org.postgresql.Driver
user: example
password: password
url: jdbc:postgresql://example.com:5432/accountsdb
accountDatabaseCrawler:
chunkSize: 10 # accounts per run
chunkIntervalMs: 60000 # time per run
@@ -239,9 +187,9 @@ apn: # Apple Push Notifications configuration
AAAAAAAA
-----END PRIVATE KEY-----
gcm: # GCM Configuration
senderId: 123456789
apiKey: unset
fcm: # FCM configuration
credentials: |
{ "json": true }
cdn:
accessKey: test # AWS Access Key ID
@@ -264,65 +212,62 @@ voiceVerification:
- en
recaptcha:
secret: unset
recaptchaV2:
siteKey: unset
scoreFloor: 1.0
projectPath: projects/example
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
storageService:
uri: storage.example.com
userAuthenticationTokenSharedSecret: 00000f
storageCaCertificate: |
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
storageCaCertificates:
- |
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
backupService:
uri: backup.example.com
userAuthenticationTokenSharedSecret: 00000f
backupCaCertificate: |
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
backupCaCertificates:
- |
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
zkConfig:
serverPublic: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
@@ -349,14 +294,6 @@ paymentsService:
# list of symbols for supported currencies
- MOB
donation:
uri: donation.example.com # value
supportedCurrencies:
- # 1st supported currency
- # 2nd supported currency
- # ...
- # Nth supported currency
badges:
badges:
- id: TEST
@@ -401,3 +338,37 @@ boost:
- '8'
- '20'
- '40'
gift:
level: 10
expiration: P90D
badge: EXAMPLE
currencies:
# ISO 4217 currency codes and amounts in those currencies
xts: '2'
registrationService:
host: registration.example.com
apiKey: EXAMPLE
registrationCaCertificate: | # Registration service TLS certificate trust root
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----

View File

@@ -10,19 +10,6 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>service</artifactId>
<pluginRepositories>
<pluginRepository>
<id>ossrh-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
@@ -37,6 +24,11 @@
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>event-logger</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>redis-dispatch</artifactId>
@@ -47,30 +39,15 @@
<artifactId>websocket-resources</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>gcm-sender-async</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>zkgroup-java</artifactId>
<version>0.8.2</version>
</dependency>
<dependency>
<groupId>org.whispersystems</groupId>
<artifactId>curve25519-java</artifactId>
<version>0.5.0</version>
<artifactId>libsignal-server</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-jdbi3</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-auth</artifactId>
@@ -142,24 +119,10 @@
<artifactId>logstash-logback-encoder</artifactId>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-core</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jdbi3</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-healthchecks</artifactId>
@@ -189,6 +152,12 @@
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -213,6 +182,39 @@
<artifactId>commons-csv</artifactId>
</dependency>
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>9.0.0</version>
<!-- firebase-admin has conflicting versions of these artifacts in its dependency tree; for firebase-admin
9.0.0, we'll need to depend directly on com.google.api-client:google-api-client:1.35.1 and
com.google.oauth-client:google-oauth-client:1.34.1 -->
<exclusions>
<exclusion>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.35.1</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
<version>1.34.1</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
@@ -226,6 +228,30 @@
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-reactor</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<!-- Needed for gRPC with Java 9+ -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
@@ -285,12 +311,12 @@
<artifactId>appconfig</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<groupId>software.amazon.awssdk</groupId>
<artifactId>appconfigdata</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<artifactId>aws-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
@@ -314,12 +340,6 @@
<artifactId>lettuce-core</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
@@ -329,12 +349,6 @@
<artifactId>pushy-dropwizard-metrics-listener</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>semver4j</artifactId>
@@ -364,6 +378,12 @@
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
@@ -381,13 +401,6 @@
</exclusions>
</dependency>
<dependency>
<groupId>com.opentable.components</groupId>
<artifactId>otj-pg-embedded</artifactId>
<version>0.13.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.almworks.sqlite4java</groupId>
<artifactId>sqlite4java</artifactId>
@@ -398,50 +411,41 @@
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>3.2.0</version>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>1.16.0</version>
<version>1.19.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -453,13 +457,6 @@
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
</dependency>
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>

View File

@@ -6,7 +6,6 @@ package org.whispersystems.textsecuregcm;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.Configuration;
import io.dropwizard.client.JerseyClientConfiguration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -15,35 +14,29 @@ import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.configuration.AbusiveMessageFilterConfiguration;
import org.whispersystems.textsecuregcm.configuration.AccountDatabaseCrawlerConfiguration;
import org.whispersystems.textsecuregcm.configuration.AccountsDatabaseConfiguration;
import org.whispersystems.textsecuregcm.configuration.AccountsDynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.AdminEventLoggingConfiguration;
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
import org.whispersystems.textsecuregcm.configuration.AppConfigConfiguration;
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatabaseConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration;
import org.whispersystems.textsecuregcm.configuration.DeletedAccountsDynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
import org.whispersystems.textsecuregcm.configuration.DonationConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
import org.whispersystems.textsecuregcm.configuration.GcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.GiftConfiguration;
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageDynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
import org.whispersystems.textsecuregcm.configuration.RecaptchaConfiguration;
import org.whispersystems.textsecuregcm.configuration.RecaptchaV2Configuration;
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
@@ -51,9 +44,8 @@ import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfig
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
import org.whispersystems.textsecuregcm.configuration.TestDeviceConfiguration;
import org.whispersystems.textsecuregcm.configuration.TurnConfiguration;
import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration;
import org.whispersystems.textsecuregcm.configuration.UnidentifiedDeliveryConfiguration;
import org.whispersystems.textsecuregcm.configuration.UsernameConfiguration;
import org.whispersystems.textsecuregcm.configuration.VoiceVerificationConfiguration;
import org.whispersystems.textsecuregcm.configuration.ZkConfig;
import org.whispersystems.websocket.configuration.WebSocketConfiguration;
@@ -61,6 +53,11 @@ import org.whispersystems.websocket.configuration.WebSocketConfiguration;
/** @noinspection MismatchedQueryAndUpdateOfCollection, WeakerAccess */
public class WhisperServerConfiguration extends Configuration {
@NotNull
@Valid
@JsonProperty
private AdminEventLoggingConfiguration adminEventLoggingConfiguration;
@NotNull
@Valid
@JsonProperty
@@ -76,16 +73,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private DynamoDbTables dynamoDbTables;
@NotNull
@Valid
@JsonProperty
private TwilioConfiguration twilio;
@NotNull
@Valid
@JsonProperty
private PushConfiguration push;
@NotNull
@Valid
@JsonProperty
@@ -156,66 +143,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private RedisClusterConfiguration clientPresenceCluster;
@Valid
@NotNull
@JsonProperty
private MessageDynamoDbConfiguration messageDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration keysDynamoDb;
@Valid
@NotNull
@JsonProperty
private AccountsDynamoDbConfiguration accountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration phoneNumberIdentifiersDynamoDb;
@Valid
@NotNull
@JsonProperty
private DeletedAccountsDynamoDbConfiguration deletedAccountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration deletedAccountsLockDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration pushChallengeDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration reportMessageDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration pendingAccountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration pendingDevicesDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration reservedUsernamesDynamoDb;
@Valid
@NotNull
@JsonProperty
private DatabaseConfiguration abuseDatabase;
@Valid
@NotNull
@JsonProperty
@@ -226,21 +153,11 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private List<MaxDeviceConfiguration> maxDevices = new LinkedList<>();
@Valid
@NotNull
@JsonProperty
private AccountsDatabaseConfiguration accountsDatabase;
@Valid
@NotNull
@JsonProperty
private RateLimitsConfiguration limits = new RateLimitsConfiguration();
@Valid
@NotNull
@JsonProperty
private JerseyClientConfiguration httpClient = new JerseyClientConfiguration();
@Valid
@NotNull
@JsonProperty
@@ -249,12 +166,7 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@NotNull
@JsonProperty
private TurnConfiguration turn;
@Valid
@NotNull
@JsonProperty
private GcmConfiguration gcm;
private FcmConfiguration fcm;
@Valid
@NotNull
@@ -276,11 +188,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private RecaptchaConfiguration recaptcha;
@Valid
@NotNull
@JsonProperty
private RecaptchaV2Configuration recaptchaV2;
@Valid
@NotNull
@JsonProperty
@@ -311,11 +218,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private AppConfigConfiguration appConfig;
@Valid
@NotNull
@JsonProperty
private DonationConfiguration donation;
@Valid
@NotNull
@JsonProperty
@@ -331,16 +233,33 @@ public class WhisperServerConfiguration extends Configuration {
@NotNull
private BoostConfiguration boost;
@Valid
@JsonProperty
@NotNull
private GiftConfiguration gift;
@Valid
@NotNull
@JsonProperty
private ReportMessageConfiguration reportMessage = new ReportMessageConfiguration();
@Valid
@NotNull
@JsonProperty
private UsernameConfiguration username = new UsernameConfiguration();
@Valid
@JsonProperty
private AbusiveMessageFilterConfiguration abusiveMessageFilter;
private Map<String, String> transparentDataIndex = new HashMap<>();
@Valid
@NotNull
@JsonProperty
private RegistrationServiceConfiguration registrationService;
public AdminEventLoggingConfiguration getAdminEventLoggingConfiguration() {
return adminEventLoggingConfiguration;
}
public StripeConfiguration getStripe() {
return stripe;
@@ -358,10 +277,6 @@ public class WhisperServerConfiguration extends Configuration {
return recaptcha;
}
public RecaptchaV2Configuration getRecaptchaV2Configuration() {
return recaptchaV2;
}
public VoiceVerificationConfiguration getVoiceVerificationConfiguration() {
return voiceVerification;
}
@@ -370,18 +285,6 @@ public class WhisperServerConfiguration extends Configuration {
return webSocket;
}
public TwilioConfiguration getTwilioConfiguration() {
return twilio;
}
public PushConfiguration getPushConfiguration() {
return push;
}
public JerseyClientConfiguration getJerseyClientConfiguration() {
return httpClient;
}
public AwsAttachmentsConfiguration getAwsAttachmentsConfiguration() {
return awsAttachments;
}
@@ -434,48 +337,12 @@ public class WhisperServerConfiguration extends Configuration {
return rateLimitersCluster;
}
public MessageDynamoDbConfiguration getMessageDynamoDbConfiguration() {
return messageDynamoDb;
}
public DynamoDbConfiguration getKeysDynamoDbConfiguration() {
return keysDynamoDb;
}
public AccountsDynamoDbConfiguration getAccountsDynamoDbConfiguration() {
return accountsDynamoDb;
}
public DynamoDbConfiguration getPhoneNumberIdentifiersDynamoDbConfiguration() {
return phoneNumberIdentifiersDynamoDb;
}
public DeletedAccountsDynamoDbConfiguration getDeletedAccountsDynamoDbConfiguration() {
return deletedAccountsDynamoDb;
}
public DynamoDbConfiguration getDeletedAccountsLockDynamoDbConfiguration() {
return deletedAccountsLockDynamoDb;
}
public DatabaseConfiguration getAbuseDatabaseConfiguration() {
return abuseDatabase;
}
public AccountsDatabaseConfiguration getAccountsDatabaseConfiguration() {
return accountsDatabase;
}
public RateLimitsConfiguration getLimitsConfiguration() {
return limits;
}
public TurnConfiguration getTurnConfiguration() {
return turn;
}
public GcmConfiguration getGcmConfiguration() {
return gcm;
public FcmConfiguration getFcmConfiguration() {
return fcm;
}
public ApnConfiguration getApnConfiguration() {
@@ -516,10 +383,6 @@ public class WhisperServerConfiguration extends Configuration {
return results;
}
public Map<String, String> getTransparentDataIndex() {
return transparentDataIndex;
}
public SecureBackupServiceConfiguration getSecureBackupServiceConfiguration() {
return backupService;
}
@@ -540,30 +403,6 @@ public class WhisperServerConfiguration extends Configuration {
return appConfig;
}
public DynamoDbConfiguration getPushChallengeDynamoDbConfiguration() {
return pushChallengeDynamoDb;
}
public DynamoDbConfiguration getReportMessageDynamoDbConfiguration() {
return reportMessageDynamoDb;
}
public DynamoDbConfiguration getPendingAccountsDynamoDbConfiguration() {
return pendingAccountsDynamoDb;
}
public DynamoDbConfiguration getPendingDevicesDynamoDbConfiguration() {
return pendingDevicesDynamoDb;
}
public DynamoDbConfiguration getReservedUsernamesDynamoDbConfiguration() {
return reservedUsernamesDynamoDb;
}
public DonationConfiguration getDonationConfiguration() {
return donation;
}
public BadgesConfiguration getBadges() {
return badges;
}
@@ -576,6 +415,10 @@ public class WhisperServerConfiguration extends Configuration {
return boost;
}
public GiftConfiguration getGift() {
return gift;
}
public ReportMessageConfiguration getReportMessageConfiguration() {
return reportMessage;
}
@@ -583,4 +426,12 @@ public class WhisperServerConfiguration extends Configuration {
public AbusiveMessageFilterConfiguration getAbusiveMessageFilterConfiguration() {
return abusiveMessageFilter;
}
public UsernameConfiguration getUsername() {
return username;
}
public RegistrationServiceConfiguration getRegistrationServiceConfiguration() {
return registrationService;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm;
@@ -11,10 +11,11 @@ import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.jdbi3.strategies.DefaultNameStrategy;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.logging.LoggingOptions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
@@ -24,11 +25,10 @@ import io.dropwizard.auth.PolymorphicAuthDynamicFeature;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.auth.basic.BasicCredentials;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.db.PooledDataSourceFactory;
import io.dropwizard.jdbi3.JdbiFactory;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.lettuce.core.metrics.MicrometerCommandLatencyRecorder;
import io.lettuce.core.metrics.MicrometerOptions;
import io.lettuce.core.resource.ClientResources;
import io.micrometer.core.instrument.Meter.Id;
import io.micrometer.core.instrument.Metrics;
@@ -36,7 +36,9 @@ import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.datadog.DatadogMeterRegistry;
import java.io.ByteArrayInputStream;
import java.net.http.HttpClient;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.util.ArrayList;
@@ -48,25 +50,29 @@ import java.util.ServiceLoader;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletRegistration;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import org.glassfish.jersey.server.ServerProperties;
import org.jdbi.v3.core.Jdbi;
import org.signal.event.AdminEventLogger;
import org.signal.event.GoogleCloudAdminEventLogger;
import org.signal.i18n.HeaderControlledResourceBundleLookup;
import org.signal.zkgroup.ServerSecretParams;
import org.signal.zkgroup.auth.ServerZkAuthOperations;
import org.signal.zkgroup.profiles.ServerZkProfileOperations;
import org.signal.zkgroup.receipts.ReceiptCredentialPresentation;
import org.signal.zkgroup.receipts.ServerZkReceiptOperations;
import org.signal.libsignal.zkgroup.ServerSecretParams;
import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation;
import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.dispatch.DispatchManager;
import org.whispersystems.textsecuregcm.abuse.AbusiveMessageFilter;
import org.whispersystems.textsecuregcm.abuse.FilterAbusiveMessages;
import org.whispersystems.textsecuregcm.abuse.RateLimitChallengeListener;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
@@ -79,9 +85,7 @@ import org.whispersystems.textsecuregcm.badges.ConfiguredProfileBadgeConverter;
import org.whispersystems.textsecuregcm.badges.ResourceBundleLevelTranslator;
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.AcceptNumericOnlineFlagRequestFilter;
import org.whispersystems.textsecuregcm.controllers.AccountController;
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV1;
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV2;
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV3;
import org.whispersystems.textsecuregcm.controllers.CertificateController;
@@ -109,22 +113,17 @@ import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.filters.ContentLengthFilter;
import org.whispersystems.textsecuregcm.filters.RemoteDeprecationFilter;
import org.whispersystems.textsecuregcm.filters.TimestampResponseFilter;
import org.whispersystems.textsecuregcm.limits.PreKeyRateLimiter;
import org.whispersystems.textsecuregcm.limits.DynamicRateLimiters;
import org.whispersystems.textsecuregcm.limits.PushChallengeManager;
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
import org.whispersystems.textsecuregcm.limits.RateLimitResetMetricsManager;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.limits.UnsealedSenderRateLimiter;
import org.whispersystems.textsecuregcm.liquibase.NameableMigrationsBundle;
import org.whispersystems.textsecuregcm.mappers.CompletionExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.IOExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.ImpossiblePhoneNumberExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.InvalidWebsocketAddressExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.NonNormalizedPhoneNumberExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RateLimitChallengeExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RetryLaterExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.ServerRejectedExceptionMapper;
import org.whispersystems.textsecuregcm.metrics.ApplicationShutdownMonitor;
import org.whispersystems.textsecuregcm.metrics.BufferPoolGauges;
@@ -132,37 +131,37 @@ import org.whispersystems.textsecuregcm.metrics.CpuUsageGauge;
import org.whispersystems.textsecuregcm.metrics.FileDescriptorGauge;
import org.whispersystems.textsecuregcm.metrics.FreeMemoryGauge;
import org.whispersystems.textsecuregcm.metrics.GarbageCollectionGauges;
import org.whispersystems.textsecuregcm.metrics.LettuceMetricsMeterFilter;
import org.whispersystems.textsecuregcm.metrics.MaxFileDescriptorGauge;
import org.whispersystems.textsecuregcm.metrics.MetricsApplicationEventListener;
import org.whispersystems.textsecuregcm.metrics.MetricsRequestEventListener;
import org.whispersystems.textsecuregcm.metrics.MicrometerRegistryManager;
import org.whispersystems.textsecuregcm.metrics.NetworkReceivedGauge;
import org.whispersystems.textsecuregcm.metrics.NetworkSentGauge;
import org.whispersystems.textsecuregcm.metrics.OperatingSystemMemoryGauge;
import org.whispersystems.textsecuregcm.metrics.PushLatencyManager;
import org.whispersystems.textsecuregcm.metrics.ReportedMessageMetricsListener;
import org.whispersystems.textsecuregcm.metrics.TrafficSource;
import org.whispersystems.textsecuregcm.providers.MultiRecipientMessageProvider;
import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
import org.whispersystems.textsecuregcm.providers.RedisClusterHealthCheck;
import org.whispersystems.textsecuregcm.push.APNSender;
import org.whispersystems.textsecuregcm.push.ApnFallbackManager;
import org.whispersystems.textsecuregcm.push.ApnPushNotificationScheduler;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.push.GCMSender;
import org.whispersystems.textsecuregcm.push.FcmSender;
import org.whispersystems.textsecuregcm.push.MessageSender;
import org.whispersystems.textsecuregcm.push.ProvisioningManager;
import org.whispersystems.textsecuregcm.push.PushLatencyManager;
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
import org.whispersystems.textsecuregcm.push.ReceiptSender;
import org.whispersystems.textsecuregcm.recaptcha.EnterpriseRecaptchaClient;
import org.whispersystems.textsecuregcm.recaptcha.LegacyRecaptchaClient;
import org.whispersystems.textsecuregcm.recaptcha.TransitionalRecaptchaClient;
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
import org.whispersystems.textsecuregcm.redis.ConnectionEventLogger;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
import org.whispersystems.textsecuregcm.s3.PolicySigner;
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.sms.SmsSender;
import org.whispersystems.textsecuregcm.sms.TwilioSmsSender;
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
import org.whispersystems.textsecuregcm.storage.AccountCleaner;
@@ -171,6 +170,7 @@ import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
import org.whispersystems.textsecuregcm.storage.ContactDiscoveryWriter;
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
import org.whispersystems.textsecuregcm.storage.DeletedAccountsDirectoryReconciler;
@@ -179,7 +179,6 @@ import org.whispersystems.textsecuregcm.storage.DeletedAccountsTableCrawler;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciler;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciliationClient;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.storage.FaultTolerantDatabase;
import org.whispersystems.textsecuregcm.storage.IssuedReceiptsManager;
import org.whispersystems.textsecuregcm.storage.Keys;
import org.whispersystems.textsecuregcm.storage.MessagePersister;
@@ -190,6 +189,7 @@ import org.whispersystems.textsecuregcm.storage.NonNormalizedAccountCrawlerListe
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
import org.whispersystems.textsecuregcm.storage.Profiles;
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
import org.whispersystems.textsecuregcm.storage.ProhibitedUsernames;
import org.whispersystems.textsecuregcm.storage.PubSubManager;
import org.whispersystems.textsecuregcm.storage.PushChallengeDynamoDb;
import org.whispersystems.textsecuregcm.storage.PushFeedbackProcessor;
@@ -198,22 +198,20 @@ import org.whispersystems.textsecuregcm.storage.RemoteConfigs;
import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager;
import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
import org.whispersystems.textsecuregcm.storage.ReservedUsernames;
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
import org.whispersystems.textsecuregcm.storage.SubscriptionManager;
import org.whispersystems.textsecuregcm.storage.Usernames;
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.storage.VerificationCodeStore;
import org.whispersystems.textsecuregcm.stripe.StripeManager;
import org.whispersystems.textsecuregcm.subscriptions.StripeManager;
import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
import org.whispersystems.textsecuregcm.util.HostnameUtil;
import org.whispersystems.textsecuregcm.util.UsernameGenerator;
import org.whispersystems.textsecuregcm.util.logging.LoggingUnhandledExceptionMapper;
import org.whispersystems.textsecuregcm.util.logging.UncaughtExceptionHandler;
import org.whispersystems.textsecuregcm.websocket.AuthenticatedConnectListener;
import org.whispersystems.textsecuregcm.websocket.DeadLetterHandler;
import org.whispersystems.textsecuregcm.websocket.ProvisioningConnectListener;
import org.whispersystems.textsecuregcm.websocket.WebSocketAccountAuthenticator;
import org.whispersystems.textsecuregcm.workers.AssignUsernameCommand;
import org.whispersystems.textsecuregcm.workers.CertificateCommand;
import org.whispersystems.textsecuregcm.workers.CheckDynamicConfigurationCommand;
import org.whispersystems.textsecuregcm.workers.DeleteUserCommand;
@@ -245,20 +243,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
bootstrap.addCommand(new CheckDynamicConfigurationCommand());
bootstrap.addCommand(new SetUserDiscoverabilityCommand());
bootstrap.addCommand(new ReserveUsernameCommand());
bootstrap.addBundle(new NameableMigrationsBundle<WhisperServerConfiguration>("accountdb", "accountsdb.xml") {
@Override
public DataSourceFactory getDataSourceFactory(WhisperServerConfiguration configuration) {
return configuration.getAccountsDatabaseConfiguration();
}
});
bootstrap.addBundle(new NameableMigrationsBundle<WhisperServerConfiguration>("abusedb", "abusedb.xml") {
@Override
public PooledDataSourceFactory getDataSourceFactory(WhisperServerConfiguration configuration) {
return configuration.getAbuseDatabaseConfiguration();
}
});
bootstrap.addCommand(new AssignUsernameCommand());
}
@Override
@@ -284,15 +269,16 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getDatadogConfiguration(), io.micrometer.core.instrument.Clock.SYSTEM);
datadogMeterRegistry.config().commonTags(
Tags.of(
"service", "chat",
"host", HostnameUtil.getLocalHostname(),
"version", WhisperServerVersion.getServerVersion(),
"env", config.getDatadogConfiguration().getEnvironment()))
Tags.of(
"service", "chat",
"host", HostnameUtil.getLocalHostname(),
"version", WhisperServerVersion.getServerVersion(),
"env", config.getDatadogConfiguration().getEnvironment()))
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.REQUEST_COUNTER_NAME))
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.ANDROID_REQUEST_COUNTER_NAME))
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.DESKTOP_REQUEST_COUNTER_NAME))
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.IOS_REQUEST_COUNTER_NAME))
.meterFilter(new LettuceMetricsMeterFilter())
.meterFilter(new MeterFilter() {
@Override
public DistributionStatisticConfig configure(final Id id, final DistributionStatisticConfig config) {
@@ -303,6 +289,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
Metrics.addRegistry(datadogMeterRegistry);
}
environment.lifecycle().manage(new MicrometerRegistryManager(Metrics.globalRegistry));
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
environment.getObjectMapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
@@ -314,91 +302,77 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
ResourceBundleLevelTranslator resourceBundleLevelTranslator = new ResourceBundleLevelTranslator(
headerControlledResourceBundleLookup);
JdbiFactory jdbiFactory = new JdbiFactory(DefaultNameStrategy.CHECK_EMPTY);
Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb");
Jdbi abuseJdbi = jdbiFactory.build(environment, config.getAbuseDatabaseConfiguration(), "abusedb" );
FaultTolerantDatabase accountDatabase = new FaultTolerantDatabase("accounts_database", accountJdbi, config.getAccountsDatabaseConfiguration().getCircuitBreakerConfiguration());
FaultTolerantDatabase abuseDatabase = new FaultTolerantDatabase("abuse_database", abuseJdbi, config.getAbuseDatabaseConfiguration().getCircuitBreakerConfiguration());
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
config.getDynamoDbClientConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient messageDynamoDb = DynamoDbFromConfig.client(config.getMessageDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient preKeyDynamoDb = DynamoDbFromConfig.client(config.getKeysDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient accountsDynamoDbClient = DynamoDbFromConfig.client(config.getAccountsDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient reservedUsernamesDynamoDbClient = DynamoDbFromConfig.client(config.getReservedUsernamesDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient phoneNumberIdentifiersDynamoDbClient =
DynamoDbFromConfig.client(config.getPhoneNumberIdentifiersDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient deletedAccountsDynamoDbClient = DynamoDbFromConfig.client(config.getDeletedAccountsDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient pushChallengeDynamoDbClient = DynamoDbFromConfig.client(
config.getPushChallengeDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient reportMessageDynamoDbClient = DynamoDbFromConfig.client(
config.getReportMessageDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient pendingAccountsDynamoDbClient = DynamoDbFromConfig.client(
config.getPendingAccountsDynamoDbConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
DynamoDbClient pendingDevicesDynamoDbClient = DynamoDbFromConfig.client(
config.getPendingDevicesDynamoDbConfiguration(),
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
config.getDynamoDbClientConfiguration(),
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
.withRegion(config.getDeletedAccountsLockDynamoDbConfiguration().getRegion())
.withRegion(config.getDynamoDbClientConfiguration().getRegion())
.withClientConfiguration(new ClientConfiguration().withClientExecutionTimeout(
((int) config.getDeletedAccountsLockDynamoDbConfiguration().getClientExecutionTimeout().toMillis()))
((int) config.getDynamoDbClientConfiguration().getClientExecutionTimeout().toMillis()))
.withRequestTimeout(
(int) config.getDeletedAccountsLockDynamoDbConfiguration().getClientRequestTimeout().toMillis()))
(int) config.getDynamoDbClientConfiguration().getClientRequestTimeout().toMillis()))
.withCredentials(InstanceProfileCredentialsProvider.getInstance())
.build();
DeletedAccounts deletedAccounts = new DeletedAccounts(deletedAccountsDynamoDbClient,
config.getDeletedAccountsDynamoDbConfiguration().getTableName(),
config.getDeletedAccountsDynamoDbConfiguration().getNeedsReconciliationIndexName());
DeletedAccounts deletedAccounts = new DeletedAccounts(dynamoDbClient,
config.getDynamoDbTables().getDeletedAccounts().getTableName(),
config.getDynamoDbTables().getDeletedAccounts().getNeedsReconciliationIndexName());
Accounts accounts = new Accounts(accountsDynamoDbClient,
config.getAccountsDynamoDbConfiguration().getTableName(),
config.getAccountsDynamoDbConfiguration().getPhoneNumberTableName(),
config.getAccountsDynamoDbConfiguration().getPhoneNumberIdentifierTableName(),
config.getAccountsDynamoDbConfiguration().getScanPageSize());
PhoneNumberIdentifiers phoneNumberIdentifiers = new PhoneNumberIdentifiers(phoneNumberIdentifiersDynamoDbClient,
config.getPhoneNumberIdentifiersDynamoDbConfiguration().getTableName());
Usernames usernames = new Usernames(accountDatabase);
ReservedUsernames reservedUsernames = new ReservedUsernames(reservedUsernamesDynamoDbClient,
config.getReservedUsernamesDynamoDbConfiguration().getTableName());
Profiles profiles = new Profiles(accountDatabase);
Keys keys = new Keys(preKeyDynamoDb, config.getKeysDynamoDbConfiguration().getTableName());
MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(messageDynamoDb,
config.getMessageDynamoDbConfiguration().getTableName(),
config.getMessageDynamoDbConfiguration().getTimeToLive());
AbusiveHostRules abusiveHostRules = new AbusiveHostRules(abuseDatabase);
RemoteConfigs remoteConfigs = new RemoteConfigs(accountDatabase);
PushChallengeDynamoDb pushChallengeDynamoDb = new PushChallengeDynamoDb(pushChallengeDynamoDbClient, config.getPushChallengeDynamoDbConfiguration().getTableName());
ReportMessageDynamoDb reportMessageDynamoDb = new ReportMessageDynamoDb(reportMessageDynamoDbClient, config.getReportMessageDynamoDbConfiguration().getTableName(), config.getReportMessageConfiguration().getReportTtl());
VerificationCodeStore pendingAccounts = new VerificationCodeStore(pendingAccountsDynamoDbClient, config.getPendingAccountsDynamoDbConfiguration().getTableName());
VerificationCodeStore pendingDevices = new VerificationCodeStore(pendingDevicesDynamoDbClient, config.getPendingDevicesDynamoDbConfiguration().getTableName());
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
new DynamicConfigurationManager<>(config.getAppConfig().getApplication(),
config.getAppConfig().getEnvironment(),
config.getAppConfig().getConfigurationName(),
DynamicConfiguration.class);
BlockingQueue<Runnable> messageDeletionQueue = new ArrayBlockingQueue<>(10_000);
Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(),
messageDeletionQueue);
ExecutorService messageDeletionAsyncExecutor = environment.lifecycle()
.executorService(name(getClass(), "messageDeletionAsyncExecutor-%d")).maxThreads(16)
.workQueue(messageDeletionQueue).build();
Accounts accounts = new Accounts(dynamicConfigurationManager,
dynamoDbClient,
dynamoDbAsyncClient,
config.getDynamoDbTables().getAccounts().getTableName(),
config.getDynamoDbTables().getAccounts().getPhoneNumberTableName(),
config.getDynamoDbTables().getAccounts().getPhoneNumberIdentifierTableName(),
config.getDynamoDbTables().getAccounts().getUsernamesTableName(),
config.getDynamoDbTables().getAccounts().getScanPageSize());
PhoneNumberIdentifiers phoneNumberIdentifiers = new PhoneNumberIdentifiers(dynamoDbClient,
config.getDynamoDbTables().getPhoneNumberIdentifiers().getTableName());
ProhibitedUsernames prohibitedUsernames = new ProhibitedUsernames(dynamoDbClient,
config.getDynamoDbTables().getReservedUsernames().getTableName());
Profiles profiles = new Profiles(dynamoDbClient, dynamoDbAsyncClient,
config.getDynamoDbTables().getProfiles().getTableName());
Keys keys = new Keys(dynamoDbClient, config.getDynamoDbTables().getKeys().getTableName());
MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(dynamoDbClient, dynamoDbAsyncClient,
config.getDynamoDbTables().getMessages().getTableName(),
config.getDynamoDbTables().getMessages().getExpiration(),
messageDeletionAsyncExecutor);
RemoteConfigs remoteConfigs = new RemoteConfigs(dynamoDbClient,
config.getDynamoDbTables().getRemoteConfig().getTableName());
PushChallengeDynamoDb pushChallengeDynamoDb = new PushChallengeDynamoDb(dynamoDbClient,
config.getDynamoDbTables().getPushChallenge().getTableName());
ReportMessageDynamoDb reportMessageDynamoDb = new ReportMessageDynamoDb(dynamoDbClient,
config.getDynamoDbTables().getReportMessage().getTableName(),
config.getReportMessageConfiguration().getReportTtl());
VerificationCodeStore pendingAccounts = new VerificationCodeStore(dynamoDbClient,
config.getDynamoDbTables().getPendingAccounts().getTableName());
VerificationCodeStore pendingDevices = new VerificationCodeStore(dynamoDbClient,
config.getDynamoDbTables().getPendingDevices().getTableName());
RedisClientFactory pubSubClientFactory = new RedisClientFactory("pubsub_cache", config.getPubsubCacheConfiguration().getUrl(), config.getPubsubCacheConfiguration().getReplicaUrls(), config.getPubsubCacheConfiguration().getCircuitBreakerConfiguration());
ReplicatedJedisPool pubsubClient = pubSubClientFactory.getRedisClientPool();
ClientResources redisClientResources = ClientResources.builder().build();
MicrometerOptions options = MicrometerOptions.builder().build();
ClientResources redisClientResources = ClientResources.builder()
.commandLatencyRecorder(new MicrometerCommandLatencyRecorder(Metrics.globalRegistry, options)).build();
ConnectionEventLogger.logConnectionEvents(redisClientResources);
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache_cluster", config.getCacheClusterConfiguration(), redisClientResources);
@@ -410,15 +384,23 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
BlockingQueue<Runnable> keyspaceNotificationDispatchQueue = new ArrayBlockingQueue<>(10_000);
Metrics.gaugeCollectionSize(name(getClass(), "keyspaceNotificationDispatchQueueSize"), Collections.emptyList(), keyspaceNotificationDispatchQueue);
final ArrayBlockingQueue<Runnable> receiptSenderQueue = new ArrayBlockingQueue<>(10_000);
Metrics.gaugeCollectionSize(name(getClass(), "receiptSenderQueue"), Collections.emptyList(), receiptSenderQueue);
final BlockingQueue<Runnable> fcmSenderQueue = new LinkedBlockingQueue<>();
Metrics.gaugeCollectionSize(name(getClass(), "fcmSenderQueue"), Collections.emptyList(), fcmSenderQueue);
ScheduledExecutorService recurringJobExecutor = environment.lifecycle()
.scheduledExecutorService(name(getClass(), "recurringJob-%d")).threads(6).build();
ScheduledExecutorService retrySchedulingExecutor = environment.lifecycle().scheduledExecutorService(name(getClass(), "retry-%d")).threads(2).build();
ScheduledExecutorService websocketScheduledExecutor = environment.lifecycle().scheduledExecutorService(name(getClass(), "websocket-%d")).threads(8).build();
ExecutorService keyspaceNotificationDispatchExecutor = environment.lifecycle().executorService(name(getClass(), "keyspaceNotification-%d")).maxThreads(16).workQueue(keyspaceNotificationDispatchQueue).build();
ExecutorService apnSenderExecutor = environment.lifecycle().executorService(name(getClass(), "apnSender-%d")).maxThreads(1).minThreads(1).build();
ExecutorService gcmSenderExecutor = environment.lifecycle().executorService(name(getClass(), "gcmSender-%d")).maxThreads(1).minThreads(1).build();
ExecutorService fcmSenderExecutor = environment.lifecycle().executorService(name(getClass(), "fcmSender-%d")).maxThreads(32).minThreads(32).workQueue(fcmSenderQueue).build();
ExecutorService backupServiceExecutor = environment.lifecycle().executorService(name(getClass(), "backupService-%d")).maxThreads(1).minThreads(1).build();
ExecutorService storageServiceExecutor = environment.lifecycle().executorService(name(getClass(), "storageService-%d")).maxThreads(1).minThreads(1).build();
// TODO: generally speaking this is a DynamoDB I/O executor for the accounts table; we should eventually have a general executor for speaking to the accounts table, but most of the server is still synchronous so this isn't widely useful yet
ExecutorService batchIdentityCheckExecutor = environment.lifecycle().executorService(name(getClass(), "batchIdentityCheck-%d")).minThreads(32).maxThreads(32).build();
ExecutorService multiRecipientMessageExecutor = environment.lifecycle()
.executorService(name(getClass(), "multiRecipientMessage-%d")).minThreads(64).maxThreads(64).build();
ExecutorService stripeExecutor = environment.lifecycle().executorService(name(getClass(), "stripe-%d")).
@@ -426,6 +408,26 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
minThreads(availableProcessors). // mostly this is IO bound so tying to number of processors is tenuous at best
allowCoreThreadTimeOut(true).
build();
ExecutorService receiptSenderExecutor = environment.lifecycle()
.executorService(name(getClass(), "receiptSender-%d"))
.maxThreads(2)
.minThreads(2)
.workQueue(receiptSenderQueue)
.rejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy())
.build();
ExecutorService registrationCallbackExecutor = environment.lifecycle()
.executorService(name(getClass(), "registration-%d"))
.maxThreads(2)
.minThreads(2)
.build();
final AdminEventLogger adminEventLogger = new GoogleCloudAdminEventLogger(
LoggingOptions.newBuilder().setProjectId(config.getAdminEventLoggingConfiguration().projectId())
.setCredentials(GoogleCredentials.fromStream(new ByteArrayInputStream(
config.getAdminEventLoggingConfiguration().credentials().getBytes(StandardCharsets.UTF_8))))
.build().getService(),
config.getAdminEventLoggingConfiguration().projectId(),
config.getAdminEventLoggingConfiguration().logName());
StripeManager stripeManager = new StripeManager(config.getStripe().getApiKey(), stripeExecutor,
config.getStripe().getIdempotencyKeyGenerator(), config.getStripe().getBoostDescription());
@@ -434,22 +436,14 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getDirectoryConfiguration().getDirectoryClientConfiguration().getUserAuthenticationTokenSharedSecret(),
config.getDirectoryConfiguration().getDirectoryClientConfiguration().getUserAuthenticationTokenUserIdSecret());
ExternalServiceCredentialGenerator directoryV2CredentialsGenerator = new ExternalServiceCredentialGenerator(
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration()
.getUserAuthenticationTokenSharedSecret(), false);
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
new DynamicConfigurationManager<>(config.getAppConfig().getApplication(),
config.getAppConfig().getEnvironment(),
config.getAppConfig().getConfigurationName(),
DynamicConfiguration.class);
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration().getUserAuthenticationTokenSharedSecret(),
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration().getUserIdTokenSharedSecret(),
true, false);
dynamicConfigurationManager.start();
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(dynamicConfigurationManager);
TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager = new TwilioVerifyExperimentEnrollmentManager(
config.getVoiceVerificationConfiguration(), experimentEnrollmentManager);
ExternalServiceCredentialGenerator storageCredentialsGenerator = new ExternalServiceCredentialGenerator(
config.getSecureStorageServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
ExternalServiceCredentialGenerator backupCredentialsGenerator = new ExternalServiceCredentialGenerator(
@@ -457,30 +451,36 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
ExternalServiceCredentialGenerator paymentsCredentialsGenerator = new ExternalServiceCredentialGenerator(
config.getPaymentsServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
AbusiveHostRules abusiveHostRules = new AbusiveHostRules(rateLimitersCluster, dynamicConfigurationManager);
RegistrationServiceClient registrationServiceClient = new RegistrationServiceClient(config.getRegistrationServiceConfiguration().getHost(), config.getRegistrationServiceConfiguration().getPort(), config.getRegistrationServiceConfiguration().getApiKey(), config.getRegistrationServiceConfiguration().getRegistrationCaCertificate(), registrationCallbackExecutor);
SecureBackupClient secureBackupClient = new SecureBackupClient(backupCredentialsGenerator, backupServiceExecutor, config.getSecureBackupServiceConfiguration());
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, storageServiceExecutor, config.getSecureStorageServiceConfiguration());
ClientPresenceManager clientPresenceManager = new ClientPresenceManager(clientPresenceCluster, recurringJobExecutor, keyspaceNotificationDispatchExecutor);
DirectoryQueue directoryQueue = new DirectoryQueue(config.getDirectoryConfiguration().getSqsConfiguration());
StoredVerificationCodeManager pendingAccountsManager = new StoredVerificationCodeManager(pendingAccounts);
StoredVerificationCodeManager pendingDevicesManager = new StoredVerificationCodeManager(pendingDevices);
UsernamesManager usernamesManager = new UsernamesManager(usernames, reservedUsernames, cacheCluster);
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster);
MessagesCache messagesCache = new MessagesCache(messagesCluster, messagesCluster, keyspaceNotificationDispatchExecutor);
PushLatencyManager pushLatencyManager = new PushLatencyManager(metricsCluster, dynamicConfigurationManager);
ReportMessageManager reportMessageManager = new ReportMessageManager(reportMessageDynamoDb, rateLimitersCluster, Metrics.globalRegistry, config.getReportMessageConfiguration().getCounterTtl());
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, pushLatencyManager, reportMessageManager);
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster);
MessagesCache messagesCache = new MessagesCache(messagesCluster, messagesCluster, Clock.systemUTC(),
keyspaceNotificationDispatchExecutor, messageDeletionAsyncExecutor);
PushLatencyManager pushLatencyManager = new PushLatencyManager(metricsCluster, dynamicConfigurationManager);
ReportMessageManager reportMessageManager = new ReportMessageManager(reportMessageDynamoDb, rateLimitersCluster, config.getReportMessageConfiguration().getCounterTtl());
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, reportMessageManager);
UsernameGenerator usernameGenerator = new UsernameGenerator(config.getUsername());
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
deletedAccountsLockDynamoDbClient, config.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
deletedAccountsLockDynamoDbClient, config.getDynamoDbTables().getDeletedAccountsLock().getTableName());
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
deletedAccountsManager, directoryQueue, keys, messagesManager, usernamesManager, profilesManager,
pendingAccountsManager, secureStorageClient, secureBackupClient, clientPresenceManager, clock);
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(accountsManager, messagesManager);
DispatchManager dispatchManager = new DispatchManager(pubSubClientFactory, Optional.of(deadLetterHandler));
deletedAccountsManager, directoryQueue, keys, messagesManager, prohibitedUsernames, profilesManager,
pendingAccountsManager, secureStorageClient, secureBackupClient, clientPresenceManager, usernameGenerator,
experimentEnrollmentManager, clock);
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
DispatchManager dispatchManager = new DispatchManager(pubSubClientFactory, Optional.empty());
PubSubManager pubSubManager = new PubSubManager(pubsubClient, dispatchManager);
APNSender apnSender = new APNSender(apnSenderExecutor, accountsManager, config.getApnConfiguration());
GCMSender gcmSender = new GCMSender(gcmSenderExecutor, accountsManager, config.getGcmConfiguration().getApiKey());
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), dynamicConfigurationManager, rateLimitersCluster);
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials());
ApnPushNotificationScheduler apnPushNotificationScheduler = new ApnPushNotificationScheduler(pushSchedulerCluster, apnSender, accountsManager);
PushNotificationManager pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender, apnPushNotificationScheduler, pushLatencyManager, dynamicConfigurationManager);
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), rateLimitersCluster);
DynamicRateLimiters dynamicRateLimiters = new DynamicRateLimiters(rateLimitersCluster, dynamicConfigurationManager);
ProvisioningManager provisioningManager = new ProvisioningManager(pubSubManager);
IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(
config.getDynamoDbTables().getIssuedReceipts().getTableName(),
@@ -495,33 +495,25 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
SubscriptionManager subscriptionManager = new SubscriptionManager(
config.getDynamoDbTables().getSubscriptions().getTableName(), dynamoDbAsyncClient);
ReportedMessageMetricsListener reportedMessageMetricsListener = new ReportedMessageMetricsListener(accountsManager);
reportMessageManager.addListener(reportedMessageMetricsListener);
AccountAuthenticator accountAuthenticator = new AccountAuthenticator(accountsManager);
DisabledPermittedAccountAuthenticator disabledPermittedAccountAuthenticator = new DisabledPermittedAccountAuthenticator(accountsManager);
RateLimitResetMetricsManager rateLimitResetMetricsManager = new RateLimitResetMetricsManager(metricsCluster, Metrics.globalRegistry);
UnsealedSenderRateLimiter unsealedSenderRateLimiter = new UnsealedSenderRateLimiter(rateLimiters, rateLimitersCluster, dynamicConfigurationManager, rateLimitResetMetricsManager);
PreKeyRateLimiter preKeyRateLimiter = new PreKeyRateLimiter(rateLimiters, dynamicConfigurationManager, rateLimitResetMetricsManager);
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushSchedulerCluster, apnSender, accountsManager);
TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration(), dynamicConfigurationManager);
SmsSender smsSender = new SmsSender(twilioSmsSender);
MessageSender messageSender = new MessageSender(apnFallbackManager, clientPresenceManager, messagesManager, gcmSender, apnSender, pushLatencyManager);
ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender);
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(config.getTurnConfiguration());
LegacyRecaptchaClient legacyRecaptchaClient = new LegacyRecaptchaClient(config.getRecaptchaConfiguration().getSecret());
EnterpriseRecaptchaClient enterpriseRecaptchaClient = new EnterpriseRecaptchaClient(
config.getRecaptchaV2Configuration().getScoreFloor().doubleValue(),
config.getRecaptchaV2Configuration().getSiteKey(),
config.getRecaptchaV2Configuration().getProjectPath(),
config.getRecaptchaV2Configuration().getCredentialConfigurationJson());
TransitionalRecaptchaClient transitionalRecaptchaClient = new TransitionalRecaptchaClient(legacyRecaptchaClient, enterpriseRecaptchaClient);
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
transitionalRecaptchaClient, preKeyRateLimiter, unsealedSenderRateLimiter, rateLimiters,
MessageSender messageSender = new MessageSender(clientPresenceManager, messagesManager, pushNotificationManager, pushLatencyManager);
ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender, receiptSenderExecutor);
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager);
RecaptchaClient recaptchaClient = new RecaptchaClient(
config.getRecaptchaConfiguration().getProjectPath(),
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
dynamicConfigurationManager);
PushChallengeManager pushChallengeManager = new PushChallengeManager(pushNotificationManager, pushChallengeDynamoDb);
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
recaptchaClient, dynamicRateLimiters);
MessagePersister messagePersister = new MessagePersister(messagesCache, messagesManager, accountsManager, dynamicConfigurationManager, Duration.ofMinutes(config.getMessageCacheConfiguration().getPersistDelayMinutes()));
ChangeNumberManager changeNumberManager = new ChangeNumberManager(messageSender, accountsManager);
final List<AccountDatabaseCrawlerListener> directoryReconciliationAccountDatabaseCrawlerListeners = new ArrayList<>();
final List<DeletedAccountsDirectoryReconciler> deletedAccountsDirectoryReconcilers = new ArrayList<>();
@@ -550,15 +542,21 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getAccountDatabaseCrawlerConfiguration().getChunkIntervalMs()
);
AccountDatabaseCrawlerCache accountCleanerAccountDatabaseCrawlerCache =
new AccountDatabaseCrawlerCache(cacheCluster, AccountDatabaseCrawlerCache.ACCOUNT_CLEANER_PREFIX);
AccountDatabaseCrawler accountCleanerAccountDatabaseCrawler = new AccountDatabaseCrawler("Account cleaner crawler",
accountsManager,
accountCleanerAccountDatabaseCrawlerCache, List.of(new AccountCleaner(accountsManager)),
config.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
config.getAccountDatabaseCrawlerConfiguration().getChunkIntervalMs()
);
// TODO listeners must be ordered so that ones that directly update accounts come last, so that read-only ones are not working with stale data
final List<AccountDatabaseCrawlerListener> accountDatabaseCrawlerListeners = List.of(
new NonNormalizedAccountCrawlerListener(accountsManager, metricsCluster),
new ContactDiscoveryWriter(accountsManager),
// PushFeedbackProcessor may update device properties
new PushFeedbackProcessor(accountsManager),
// delete accounts last
new AccountCleaner(accountsManager)
);
new PushFeedbackProcessor(accountsManager));
AccountDatabaseCrawlerCache accountDatabaseCrawlerCache = new AccountDatabaseCrawlerCache(cacheCluster,
AccountDatabaseCrawlerCache.GENERAL_PURPOSE_PREFIX);
@@ -576,20 +574,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
FtxClient ftxClient = new FtxClient(currencyClient);
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, ftxClient, config.getPaymentsServiceConfiguration().getPaymentCurrencies());
apnSender.setApnFallbackManager(apnFallbackManager);
environment.lifecycle().manage(new ApplicationShutdownMonitor());
environment.lifecycle().manage(apnFallbackManager);
environment.lifecycle().manage(apnSender);
environment.lifecycle().manage(apnPushNotificationScheduler);
environment.lifecycle().manage(pubSubManager);
environment.lifecycle().manage(messageSender);
environment.lifecycle().manage(accountDatabaseCrawler);
environment.lifecycle().manage(directoryReconciliationAccountDatabaseCrawler);
environment.lifecycle().manage(accountCleanerAccountDatabaseCrawler);
environment.lifecycle().manage(deletedAccountsTableCrawler);
environment.lifecycle().manage(remoteConfigsManager);
environment.lifecycle().manage(messagesCache);
environment.lifecycle().manage(messagePersister);
environment.lifecycle().manage(clientPresenceManager);
environment.lifecycle().manage(currencyManager);
environment.lifecycle().manage(directoryQueue);
environment.lifecycle().manage(registrationServiceClient);
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
.create(AwsBasicCredentials.create(
@@ -618,12 +615,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
.addFilter("RemoteDeprecationFilter", new RemoteDeprecationFilter(dynamicConfigurationManager))
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
// TODO Remove on or after 2022-03-01
final AcceptNumericOnlineFlagRequestFilter acceptNumericOnlineFlagRequestFilter =
new AcceptNumericOnlineFlagRequestFilter("v1/messages/multi_recipient");
environment.jersey().register(new ContentLengthFilter(TrafficSource.HTTP));
environment.jersey().register(acceptNumericOnlineFlagRequestFilter);
environment.jersey().register(MultiRecipientMessageProvider.class);
environment.jersey().register(new MetricsApplicationEventListener(TrafficSource.HTTP));
environment.jersey()
@@ -641,40 +633,38 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getWebSocketConfiguration(), 90000);
webSocketEnvironment.setAuthenticator(new WebSocketAccountAuthenticator(accountAuthenticator));
webSocketEnvironment.setConnectListener(
new AuthenticatedConnectListener(receiptSender, messagesManager, messageSender, apnFallbackManager,
clientPresenceManager, retrySchedulingExecutor));
webSocketEnvironment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
new AuthenticatedConnectListener(receiptSender, messagesManager, pushNotificationManager,
clientPresenceManager, websocketScheduledExecutor, experimentEnrollmentManager));
webSocketEnvironment.jersey()
.register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
webSocketEnvironment.jersey().register(new ContentLengthFilter(TrafficSource.WEBSOCKET));
webSocketEnvironment.jersey().register(acceptNumericOnlineFlagRequestFilter);
webSocketEnvironment.jersey().register(MultiRecipientMessageProvider.class);
webSocketEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET));
webSocketEnvironment.jersey().register(new KeepAliveController(clientPresenceManager));
// these should be common, but use @Auth DisabledPermittedAccount, which isnt supported yet on websocket
environment.jersey().register(
new AccountController(pendingAccountsManager, accountsManager, usernamesManager, abusiveHostRules, rateLimiters,
smsSender, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
transitionalRecaptchaClient, gcmSender, apnSender, backupCredentialsGenerator,
verifyExperimentEnrollmentManager));
environment.jersey().register(new KeysController(rateLimiters, keys, accountsManager, preKeyRateLimiter, rateLimitChallengeManager));
new AccountController(pendingAccountsManager, accountsManager, abusiveHostRules, rateLimiters,
registrationServiceClient, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
recaptchaClient, pushNotificationManager, changeNumberManager, backupCredentialsGenerator));
environment.jersey().register(new KeysController(rateLimiters, keys, accountsManager));
final List<Object> commonControllers = Lists.newArrayList(
new AttachmentControllerV1(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getBucket()),
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 CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations),
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations, clock),
new ChallengeController(rateLimitChallengeManager),
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keys, rateLimiters, config.getMaxDevices()),
new DirectoryController(directoryCredentialsGenerator),
new DirectoryV2Controller(directoryV2CredentialsGenerator),
new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(),
ReceiptCredentialPresentation::new, stripeExecutor, config.getDonationConfiguration(), config.getStripe()),
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, unsealedSenderRateLimiter, apnFallbackManager,
rateLimitChallengeManager, reportMessageManager, multiRecipientMessageExecutor),
ReceiptCredentialPresentation::new),
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, deletedAccountsManager, messagesManager, pushNotificationManager, reportMessageManager, multiRecipientMessageExecutor),
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, usernamesManager, dynamicConfigurationManager, profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations),
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager, profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations, batchIdentityCheckExecutor),
new ProvisioningController(rateLimiters, provisioningManager),
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()),
new RemoteConfigController(remoteConfigsManager, adminEventLogger, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()),
new SecureBackupController(backupCredentialsGenerator),
new SecureStorageController(storageCredentialsGenerator),
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
@@ -683,8 +673,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
);
if (config.getSubscription() != null && config.getBoost() != null) {
commonControllers.add(new SubscriptionController(clock, config.getSubscription(), config.getBoost(),
subscriptionManager, stripeManager, zkReceiptOperations, issuedReceiptsManager, profileBadgeConverter,
resourceBundleLevelTranslator));
config.getGift(), subscriptionManager, stripeManager, zkReceiptOperations, issuedReceiptsManager,
profileBadgeConverter, resourceBundleLevelTranslator));
}
for (Object controller : commonControllers) {
@@ -712,6 +702,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
log.warn("Abusive message filter {} not annotated with @FilterAbusiveMessages and will not be installed",
filter.getClass().getName());
}
if (filter instanceof RateLimitChallengeListener) {
log.info("Registered rate limit challenge listener: {}", filter.getClass().getName());
rateLimitChallengeManager.addListener((RateLimitChallengeListener) filter);
}
}
if (!registeredAbusiveMessageFilter) {
@@ -728,13 +723,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
registerCorsFilter(environment);
registerExceptionMappers(environment, webSocketEnvironment, provisioningEnvironment);
RateLimitChallengeExceptionMapper rateLimitChallengeExceptionMapper = new RateLimitChallengeExceptionMapper(
rateLimitChallengeManager);
environment.jersey().register(rateLimitChallengeExceptionMapper);
webSocketEnvironment.jersey().register(rateLimitChallengeExceptionMapper);
provisioningEnvironment.jersey().register(rateLimitChallengeExceptionMapper);
environment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
webSocketEnvironment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
provisioningEnvironment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
@@ -758,6 +746,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
environment.healthChecks().register("cacheCluster", new RedisClusterHealthCheck(cacheCluster));
environment.lifecycle().manage(new ApplicationShutdownMonitor(Metrics.globalRegistry));
environment.metrics().register(name(CpuUsageGauge.class, "cpu"), new CpuUsageGauge(3, TimeUnit.SECONDS));
environment.metrics().register(name(FreeMemoryGauge.class, "free_memory"), new FreeMemoryGauge());
environment.metrics().register(name(NetworkSentGauge.class, "bytes_sent"), new NetworkSentGauge());
@@ -784,7 +774,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new RateLimitExceededExceptionMapper(),
new InvalidWebsocketAddressExceptionMapper(),
new DeviceLimitExceededExceptionMapper(),
new RetryLaterExceptionMapper(),
new ServerRejectedExceptionMapper(),
new ImpossiblePhoneNumberExceptionMapper(),
new NonNormalizedPhoneNumberExceptionMapper()

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.abuse;
import org.whispersystems.textsecuregcm.storage.Account;
import java.io.IOException;
public interface RateLimitChallengeListener {
void handleRateLimitChallengeAnswered(Account account);
/**
* Configures this rate limit challenge listener. This method will be called before the service begins processing any
* challenges.
*
* @param environmentName the name of the environment in which this listener is running (e.g. "staging" or "production")
* @throws IOException if the listener could not read its configuration source for any reason
*/
void configure(String environmentName) throws IOException;
}

View File

@@ -0,0 +1,12 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.abuse;
public enum RateLimitChallengeType {
PUSH_CHALLENGE,
RECAPTCHA
}

View File

@@ -7,32 +7,19 @@ package org.whispersystems.textsecuregcm.auth;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Optional;
import io.micrometer.core.instrument.Metrics;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import static com.codahale.metrics.MetricRegistry.name;
public class AccountAuthenticator extends BaseAccountAuthenticator implements
Authenticator<BasicCredentials, AuthenticatedAccount> {
private static final String AUTHENTICATION_COUNTER_NAME = name(AccountAuthenticator.class, "authenticate");
public AccountAuthenticator(AccountsManager accountsManager) {
super(accountsManager);
}
@Override
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials) {
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = super.authenticate(basicCredentials, true);
// TODO Remove after announcement groups have launched
maybeAuthenticatedAccount.ifPresent(authenticatedAccount ->
Metrics.counter(AUTHENTICATION_COUNTER_NAME,
"supportsAnnouncementGroups",
String.valueOf(authenticatedAccount.getAccount().isAnnouncementGroupSupported()))
.increment());
return maybeAuthenticatedAccount;
return super.authenticate(basicCredentials, true);
}
}

View File

@@ -4,7 +4,9 @@
*/
package org.whispersystems.textsecuregcm.auth;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.codec.binary.Hex;
import org.signal.libsignal.protocol.kdf.HKDF;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -12,10 +14,18 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class AuthenticationCredentials {
private static final String V2_PREFIX = "2.";
private final String hashedAuthenticationToken;
private final String salt;
public enum Version {
V1,
V2,
}
public static final Version CURRENT_VERSION = Version.V2;
public AuthenticationCredentials(String hashedAuthenticationToken, String salt) {
this.hashedAuthenticationToken = hashedAuthenticationToken;
this.salt = salt;
@@ -23,7 +33,20 @@ public class AuthenticationCredentials {
public AuthenticationCredentials(String authenticationToken) {
this.salt = String.valueOf(Math.abs(new SecureRandom().nextInt()));
this.hashedAuthenticationToken = getHashedValue(salt, authenticationToken);
this.hashedAuthenticationToken = getV2HashedValue(salt, authenticationToken);
}
@VisibleForTesting
public AuthenticationCredentials v1ForTesting(String authenticationToken) {
String salt = String.valueOf(Math.abs(new SecureRandom().nextInt()));
return new AuthenticationCredentials(getV1HashedValue(salt, authenticationToken), salt);
}
public Version getVersion() {
if (this.hashedAuthenticationToken.startsWith(V2_PREFIX)) {
return Version.V2;
}
return Version.V1;
}
public String getHashedAuthenticationToken() {
@@ -35,11 +58,14 @@ public class AuthenticationCredentials {
}
public boolean verify(String authenticationToken) {
String theirValue = getHashedValue(salt, authenticationToken);
final String theirValue = switch (getVersion()) {
case V1 -> getV1HashedValue(salt, authenticationToken);
case V2 -> getV2HashedValue(salt, authenticationToken);
};
return MessageDigest.isEqual(theirValue.getBytes(StandardCharsets.UTF_8), this.hashedAuthenticationToken.getBytes(StandardCharsets.UTF_8));
}
private static String getHashedValue(String salt, String token) {
private static String getV1HashedValue(String salt, String token) {
try {
return new String(Hex.encodeHex(MessageDigest.getInstance("SHA1").digest((salt + token).getBytes(StandardCharsets.UTF_8))));
} catch (NoSuchAlgorithmException e) {
@@ -47,4 +73,13 @@ public class AuthenticationCredentials {
}
}
private static final byte[] AUTH_TOKEN_HKDF_INFO = "authtoken".getBytes(StandardCharsets.UTF_8);
private static String getV2HashedValue(String salt, String token) {
byte[] secret = HKDF.deriveSecrets(
token.getBytes(StandardCharsets.UTF_8), // key
salt.getBytes(StandardCharsets.UTF_8), // salt
AUTH_TOKEN_HKDF_INFO,
32);
return V2_PREFIX + Hex.encodeHexString(secret);
}
}

View File

@@ -30,6 +30,9 @@ public class BaseAccountAuthenticator {
private static final String AUTHENTICATION_SUCCEEDED_TAG_NAME = "succeeded";
private static final String AUTHENTICATION_FAILURE_REASON_TAG_NAME = "reason";
private static final String AUTHENTICATION_ENABLED_REQUIRED_TAG_NAME = "enabledRequired";
private static final String AUTHENTICATION_HAS_STORY_CAPABILITY = "hasStoryCapability";
private static final String STORY_ADOPTION_COUNTER_NAME = name(BaseAccountAuthenticator.class, "storyAdoption");
private static final String DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME = name(BaseAccountAuthenticator.class, "daysSinceLastSeen");
private static final String IS_PRIMARY_DEVICE_TAG = "isPrimary";
@@ -43,8 +46,8 @@ public class BaseAccountAuthenticator {
@VisibleForTesting
public BaseAccountAuthenticator(AccountsManager accountsManager, Clock clock) {
this.accountsManager = accountsManager;
this.clock = clock;
this.accountsManager = accountsManager;
this.clock = clock;
}
static Pair<String, Long> getIdentifierAndDeviceId(final String basicUsername) {
@@ -67,6 +70,7 @@ public class BaseAccountAuthenticator {
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials, boolean enabledRequired) {
boolean succeeded = false;
String failureReason = null;
boolean hasStoryCapability = false;
try {
final UUID accountUuid;
@@ -85,6 +89,8 @@ public class BaseAccountAuthenticator {
return Optional.empty();
}
hasStoryCapability = account.map(Account::isStoriesSupported).orElse(false);
Optional<Device> device = account.get().getDevice(deviceId);
if (device.isEmpty()) {
@@ -104,9 +110,16 @@ public class BaseAccountAuthenticator {
}
}
if (device.get().getAuthenticationCredentials().verify(basicCredentials.getPassword())) {
AuthenticationCredentials deviceAuthenticationCredentials = device.get().getAuthenticationCredentials();
if (deviceAuthenticationCredentials.verify(basicCredentials.getPassword())) {
succeeded = true;
final Account authenticatedAccount = updateLastSeen(account.get(), device.get());
Account authenticatedAccount = updateLastSeen(account.get(), device.get());
if (deviceAuthenticationCredentials.getVersion() != AuthenticationCredentials.CURRENT_VERSION) {
authenticatedAccount = accountsManager.updateDeviceAuthentication(
authenticatedAccount,
device.get(),
new AuthenticationCredentials(basicCredentials.getPassword())); // new credentials have current version
}
return Optional.of(new AuthenticatedAccount(
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
}
@@ -125,6 +138,9 @@ public class BaseAccountAuthenticator {
}
Metrics.counter(AUTHENTICATION_COUNTER_NAME, tags).increment();
Tags storyTags = Tags.of(AUTHENTICATION_HAS_STORY_CAPABILITY, String.valueOf(hasStoryCapability));
Metrics.counter(STORY_ADOPTION_COUNTER_NAME, storyTags).increment();
}
}
@@ -142,5 +158,4 @@ public class BaseAccountAuthenticator {
return account;
}
}

View File

@@ -5,9 +5,7 @@
package org.whispersystems.textsecuregcm.auth;
import java.util.Base64;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Pair;
public class BasicAuthorizationHeader {

View File

@@ -7,18 +7,16 @@ package org.whispersystems.textsecuregcm.auth;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.textsecuregcm.crypto.Curve;
import org.whispersystems.textsecuregcm.crypto.ECPrivateKey;
import java.security.InvalidKeyException;
import java.util.Base64;
import java.util.concurrent.TimeUnit;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
import org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate;
import org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.util.Base64;
import java.util.concurrent.TimeUnit;
public class CertificateGenerator {
private final ECPrivateKey privateKey;
@@ -46,7 +44,12 @@ public class CertificateGenerator {
}
byte[] certificate = builder.build().toByteArray();
byte[] signature = Curve.calculateSignature(privateKey, certificate);
byte[] signature;
try {
signature = Curve.calculateSignature(privateKey, certificate);
} catch (org.signal.libsignal.protocol.InvalidKeyException e) {
throw new InvalidKeyException(e);
}
return SenderCertificate.newBuilder()
.setCertificate(ByteString.copyFrom(certificate))

View File

@@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.auth;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Optional;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
public class DisabledPermittedAccountAuthenticator extends BaseAccountAuthenticator implements

View File

@@ -35,7 +35,7 @@ public class ExternalServiceCredentialGenerator {
this(key, userIdKey, usernameDerivation, true);
}
private ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
boolean prependUsername) {
this(key, userIdKey, usernameDerivation, prependUsername, Clock.systemUTC());
}

View File

@@ -8,6 +8,8 @@ package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.security.MessageDigest;
@@ -36,9 +38,9 @@ public class OptionalAccess {
}
if (requestAccount.isPresent()) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
throw new NotFoundException();
} else {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
throw new NotAuthorizedException(Response.Status.UNAUTHORIZED);
}
}
} catch (NumberFormatException e) {
@@ -56,7 +58,7 @@ public class OptionalAccess {
//noinspection ConstantConditions
if (requestAccount.isPresent() && (targetAccount.isEmpty() || (targetAccount.isPresent() && !targetAccount.get().isEnabled()))) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
throw new NotFoundException();
}
if (accessKey.isPresent() && targetAccount.isPresent() && targetAccount.get().isEnabled() && targetAccount.get().isUnrestrictedUnidentifiedAccess()) {
@@ -72,7 +74,7 @@ public class OptionalAccess {
return;
}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
throw new NotAuthorizedException(Response.Status.UNAUTHORIZED);
}
}

View File

@@ -10,7 +10,6 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.util.Pair;

View File

@@ -5,60 +5,19 @@
package org.whispersystems.textsecuregcm.auth;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.security.MessageDigest;
import java.time.Duration;
import java.util.Optional;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.util.Util;
public class StoredVerificationCode {
@JsonProperty
private final String code;
@JsonProperty
private final long timestamp;
@JsonProperty
private final String pushCode;
@JsonProperty
@Nullable
private final String twilioVerificationSid;
public record StoredVerificationCode(String code,
long timestamp,
String pushCode,
@Nullable String twilioVerificationSid,
@Nullable byte[] sessionId) {
public static final Duration EXPIRATION = Duration.ofMinutes(10);
@JsonCreator
public StoredVerificationCode(
@JsonProperty("code") final String code,
@JsonProperty("timestamp") final long timestamp,
@JsonProperty("pushCode") final String pushCode,
@JsonProperty("twilioVerificationSid") @Nullable final String twilioVerificationSid) {
this.code = code;
this.timestamp = timestamp;
this.pushCode = pushCode;
this.twilioVerificationSid = twilioVerificationSid;
}
public String getCode() {
return code;
}
public long getTimestamp() {
return timestamp;
}
public String getPushCode() {
return pushCode;
}
public Optional<String> getTwilioVerificationSid() {
return Optional.ofNullable(twilioVerificationSid);
}
public boolean isValid(String theirCodeString) {
if (Util.isEmpty(code) || Util.isEmpty(theirCodeString)) {
return false;

View File

@@ -6,6 +6,7 @@
package org.whispersystems.textsecuregcm.auth;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
@@ -25,4 +26,9 @@ public class TurnToken {
this.password = password;
this.urls = urls;
}
@VisibleForTesting
List<String> getUrls() {
return urls;
}
}

View File

@@ -5,7 +5,12 @@
package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.configuration.TurnConfiguration;
import org.whispersystems.textsecuregcm.configuration.TurnUriConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicTurnConfiguration;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.WeightedRandomSelect;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@@ -14,20 +19,21 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class TurnTokenGenerator {
private final byte[] key;
private final List<String> urls;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfiguration;
public TurnTokenGenerator(TurnConfiguration configuration) {
this.key = configuration.getSecret().getBytes();
this.urls = configuration.getUris();
public TurnTokenGenerator(final DynamicConfigurationManager<DynamicConfiguration> config) {
this.dynamicConfiguration = config;
}
public TurnToken generate() {
public TurnToken generate(final String e164) {
try {
byte[] key = dynamicConfiguration.getConfiguration().getTurnConfiguration().getSecret().getBytes();
List<String> urls = urls(e164);
Mac mac = Mac.getInstance("HmacSHA1");
long validUntilSeconds = (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)) / 1000;
long user = Math.abs(new SecureRandom().nextInt());
@@ -41,4 +47,22 @@ public class TurnTokenGenerator {
throw new AssertionError(e);
}
}
private List<String> urls(final String e164) {
final DynamicTurnConfiguration turnConfig = dynamicConfiguration.getConfiguration().getTurnConfiguration();
// Check if number is enrolled to test out specific turn servers
final Optional<TurnUriConfiguration> enrolled = turnConfig.getUriConfigs().stream()
.filter(config -> config.getEnrolledNumbers().contains(e164))
.findFirst();
if (enrolled.isPresent()) {
return enrolled.get().getUris();
}
// Otherwise, select from turn server sets by weighted choice
return WeightedRandomSelect.select(turnConfig
.getUriConfigs()
.stream()
.map(c -> new Pair<List<String>, Long>(c.getUris(), c.getWeight())).toList());
}
}

View File

@@ -5,10 +5,14 @@
package org.whispersystems.textsecuregcm.auth;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.glassfish.jersey.server.monitoring.RequestEvent.Type;
import org.glassfish.jersey.server.monitoring.RequestEventListener;
@@ -16,11 +20,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
public class WebsocketRefreshRequestEventListener implements RequestEventListener {
private final ClientPresenceManager clientPresenceManager;
@@ -60,7 +59,7 @@ public class WebsocketRefreshRequestEventListener implements RequestEventListene
.forEach(pair -> {
try {
displacedDevices.incrementAndGet();
clientPresenceManager.displacePresence(pair.first(), pair.second());
clientPresenceManager.disconnectPresence(pair.first(), pair.second());
} catch (final Exception e) {
logger.error("Could not displace device presence", e);
}

View File

@@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.auth;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.whispersystems.textsecuregcm.util.Pair;

View File

@@ -1,23 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
public class AccountsDatabaseConfiguration extends DatabaseConfiguration {
@JsonProperty
@NotNull
@Valid
private RetryConfiguration keyOperationRetry = new RetryConfiguration();
public RetryConfiguration getKeyOperationRetryConfiguration() {
return keyOperationRetry;
}
}

View File

@@ -1,31 +0,0 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotNull;
public class AccountsDynamoDbConfiguration extends DynamoDbConfiguration {
@NotNull
private String phoneNumberTableName;
@NotNull
private String phoneNumberIdentifierTableName;
private int scanPageSize = 100;
@JsonProperty
public String getPhoneNumberTableName() {
return phoneNumberTableName;
}
@JsonProperty
public String getPhoneNumberIdentifierTableName() {
return phoneNumberIdentifierTableName;
}
@JsonProperty
public int getScanPageSize() {
return scanPageSize;
}
}

View File

@@ -0,0 +1,49 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables.Table;
import javax.validation.constraints.NotBlank;
public class AccountsTableConfiguration extends Table {
private final String phoneNumberTableName;
private final String phoneNumberIdentifierTableName;
private final String usernamesTableName;
private final int scanPageSize;
@JsonCreator
public AccountsTableConfiguration(
@JsonProperty("tableName") final String tableName,
@JsonProperty("phoneNumberTableName") final String phoneNumberTableName,
@JsonProperty("phoneNumberIdentifierTableName") final String phoneNumberIdentifierTableName,
@JsonProperty("usernamesTableName") final String usernamesTableName,
@JsonProperty("scanPageSize") final int scanPageSize) {
super(tableName);
this.phoneNumberTableName = phoneNumberTableName;
this.phoneNumberIdentifierTableName = phoneNumberIdentifierTableName;
this.usernamesTableName = usernamesTableName;
this.scanPageSize = scanPageSize;
}
@NotBlank
public String getPhoneNumberTableName() {
return phoneNumberTableName;
}
@NotBlank
public String getPhoneNumberIdentifierTableName() {
return phoneNumberIdentifierTableName;
}
@NotBlank
public String getUsernamesTableName() {
return usernamesTableName;
}
public int getScanPageSize() {
return scanPageSize;
}
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright 2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import javax.validation.constraints.NotEmpty;
public record AdminEventLoggingConfiguration(
@NotEmpty String credentials,
@NotEmpty String projectId,
@NotEmpty String logName) {
}

View File

@@ -27,12 +27,17 @@ public class CircuitBreakerConfiguration {
@JsonProperty
@NotNull
@Min(1)
private int ringBufferSizeInHalfOpenState = 10;
private int permittedNumberOfCallsInHalfOpenState = 10;
@JsonProperty
@NotNull
@Min(1)
private int ringBufferSizeInClosedState = 100;
private int slidingWindowSize = 100;
@JsonProperty
@NotNull
@Min(1)
private int slidingWindowMinimumNumberOfCalls = 100;
@JsonProperty
@NotNull
@@ -47,28 +52,32 @@ public class CircuitBreakerConfiguration {
return failureRateThreshold;
}
public int getRingBufferSizeInHalfOpenState() {
return ringBufferSizeInHalfOpenState;
public int getPermittedNumberOfCallsInHalfOpenState() {
return permittedNumberOfCallsInHalfOpenState;
}
public int getRingBufferSizeInClosedState() {
return ringBufferSizeInClosedState;
public int getSlidingWindowSize() {
return slidingWindowSize;
}
public int getSlidingWindowMinimumNumberOfCalls() {
return slidingWindowMinimumNumberOfCalls;
}
public long getWaitDurationInOpenStateInSeconds() {
return waitDurationInOpenStateInSeconds;
}
public List<Class> getIgnoredExceptions() {
return ignoredExceptions.stream()
.map(name -> {
try {
return Class.forName(name);
} catch (final ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
public List<Class<?>> getIgnoredExceptions() {
return ignoredExceptions.stream()
.map(name -> {
try {
return Class.forName(name);
} catch (final ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
@VisibleForTesting
@@ -77,13 +86,18 @@ public class CircuitBreakerConfiguration {
}
@VisibleForTesting
public void setRingBufferSizeInClosedState(int size) {
this.ringBufferSizeInClosedState = size;
public void setSlidingWindowSize(int size) {
this.slidingWindowSize = size;
}
@VisibleForTesting
public void setRingBufferSizeInHalfOpenState(int size) {
this.ringBufferSizeInHalfOpenState = size;
public void setSlidingWindowMinimumNumberOfCalls(int size) {
this.slidingWindowMinimumNumberOfCalls = size;
}
@VisibleForTesting
public void setPermittedNumberOfCallsInHalfOpenState(int size) {
this.permittedNumberOfCallsInHalfOpenState = size;
}
@VisibleForTesting
@@ -98,11 +112,12 @@ public class CircuitBreakerConfiguration {
public CircuitBreakerConfig toCircuitBreakerConfig() {
return CircuitBreakerConfig.custom()
.failureRateThreshold(getFailureRateThreshold())
.ignoreExceptions(getIgnoredExceptions().toArray(new Class[0]))
.ringBufferSizeInHalfOpenState(getRingBufferSizeInHalfOpenState())
.waitDurationInOpenState(Duration.ofSeconds(getWaitDurationInOpenStateInSeconds()))
.ringBufferSizeInClosedState(getRingBufferSizeInClosedState())
.build();
.failureRateThreshold(getFailureRateThreshold())
.ignoreExceptions(getIgnoredExceptions().toArray(new Class[0]))
.permittedNumberOfCallsInHalfOpenState(getPermittedNumberOfCallsInHalfOpenState())
.waitDurationInOpenState(Duration.ofSeconds(getWaitDurationInOpenStateInSeconds()))
.slidingWindow(getSlidingWindowSize(), getSlidingWindowMinimumNumberOfCalls(),
CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.build();
}
}

View File

@@ -1,13 +0,0 @@
package org.whispersystems.textsecuregcm.configuration;
import javax.validation.constraints.NotNull;
public class DeletedAccountsDynamoDbConfiguration extends DynamoDbConfiguration {
@NotNull
private String needsReconciliationIndexName;
public String getNeedsReconciliationIndexName() {
return needsReconciliationIndexName;
}
}

View File

@@ -0,0 +1,25 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotBlank;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables.Table;
public class DeletedAccountsTableConfiguration extends Table {
private final String needsReconciliationIndexName;
@JsonCreator
public DeletedAccountsTableConfiguration(
@JsonProperty("tableName") final String tableName,
@JsonProperty("needsReconciliationIndexName") final String needsReconciliationIndexName) {
super(tableName);
this.needsReconciliationIndexName = needsReconciliationIndexName;
}
@NotBlank
public String getNeedsReconciliationIndexName() {
return needsReconciliationIndexName;
}
}

View File

@@ -5,7 +5,9 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
public class DirectoryServerConfiguration {
@@ -23,7 +25,7 @@ public class DirectoryServerConfiguration {
@NotEmpty
@JsonProperty
private String replicationCaCertificate;
private List<@NotBlank String> replicationCaCertificates;
public String getReplicationName() {
return replicationName;
@@ -37,8 +39,8 @@ public class DirectoryServerConfiguration {
return replicationPassword;
}
public String getReplicationCaCertificate() {
return replicationCaCertificate;
public List<String> getReplicationCaCertificates() {
return replicationCaCertificates;
}
}

View File

@@ -11,11 +11,14 @@ import org.whispersystems.textsecuregcm.util.ExactlySize;
public class DirectoryV2ClientConfiguration {
private final byte[] userAuthenticationTokenSharedSecret;
private final byte[] userIdTokenSharedSecret;
@JsonCreator
public DirectoryV2ClientConfiguration(
@JsonProperty("userAuthenticationTokenSharedSecret") final byte[] userAuthenticationTokenSharedSecret) {
@JsonProperty("userAuthenticationTokenSharedSecret") final byte[] userAuthenticationTokenSharedSecret,
@JsonProperty("userIdTokenSharedSecret") final byte[] userIdTokenSharedSecret) {
this.userAuthenticationTokenSharedSecret = userAuthenticationTokenSharedSecret;
this.userIdTokenSharedSecret = userIdTokenSharedSecret;
}
@ExactlySize({32})
@@ -23,4 +26,8 @@ public class DirectoryV2ClientConfiguration {
return userAuthenticationTokenSharedSecret;
}
@ExactlySize({32})
public byte[] getUserIdTokenSharedSecret() {
return userIdTokenSharedSecret;
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import java.util.Set;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class DonationConfiguration {
private String uri;
private String description;
private Set<String> supportedCurrencies;
private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
private RetryConfiguration retry = new RetryConfiguration();
@JsonProperty
@NotEmpty
public String getUri() {
return uri;
}
@VisibleForTesting
public void setUri(final String uri) {
this.uri = uri;
}
@JsonProperty
public String getDescription() {
return description;
}
@VisibleForTesting
public void setDescription(final String description) {
this.description = description;
}
@JsonProperty
@NotEmpty
public Set<String> getSupportedCurrencies() {
return supportedCurrencies;
}
@VisibleForTesting
public void setSupportedCurrencies(final Set<String> supportedCurrencies) {
this.supportedCurrencies = supportedCurrencies;
}
@JsonProperty
@NotNull
@Valid
public CircuitBreakerConfiguration getCircuitBreaker() {
return circuitBreaker;
}
@VisibleForTesting
public void setCircuitBreaker(final CircuitBreakerConfiguration circuitBreaker) {
this.circuitBreaker = circuitBreaker;
}
@JsonProperty
@NotNull
@Valid
public RetryConfiguration getRetry() {
return retry;
}
@VisibleForTesting
public void setRetry(final RetryConfiguration retry) {
this.retry = retry;
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.time.Duration;
public class DynamoDbConfiguration {
private String region;
private String tableName;
private Duration clientExecutionTimeout = Duration.ofSeconds(30);
private Duration clientRequestTimeout = Duration.ofSeconds(10);
@Valid
@NotEmpty
@JsonProperty
public String getRegion() {
return region;
}
@Valid
@NotEmpty
@JsonProperty
public String getTableName() {
return tableName;
}
@JsonProperty
public Duration getClientExecutionTimeout() {
return clientExecutionTimeout;
}
@JsonProperty
public Duration getClientRequestTimeout() {
return clientRequestTimeout;
}
}

View File

@@ -46,34 +46,151 @@ public class DynamoDbTables {
}
}
private final AccountsTableConfiguration accounts;
private final DeletedAccountsTableConfiguration deletedAccounts;
private final Table deletedAccountsLock;
private final IssuedReceiptsTableConfiguration issuedReceipts;
private final Table keys;
private final TableWithExpiration messages;
private final Table pendingAccounts;
private final Table pendingDevices;
private final Table phoneNumberIdentifiers;
private final Table profiles;
private final Table pushChallenge;
private final TableWithExpiration redeemedReceipts;
private final Table remoteConfig;
private final Table reportMessage;
private final Table reservedUsernames;
private final Table subscriptions;
@JsonCreator
public DynamoDbTables(
@JsonProperty("accounts") final AccountsTableConfiguration accounts,
@JsonProperty("deletedAccounts") final DeletedAccountsTableConfiguration deletedAccounts,
@JsonProperty("deletedAccountsLock") final Table deletedAccountsLock,
@JsonProperty("issuedReceipts") final IssuedReceiptsTableConfiguration issuedReceipts,
@JsonProperty("keys") final Table keys,
@JsonProperty("messages") final TableWithExpiration messages,
@JsonProperty("pendingAccounts") final Table pendingAccounts,
@JsonProperty("pendingDevices") final Table pendingDevices,
@JsonProperty("phoneNumberIdentifiers") final Table phoneNumberIdentifiers,
@JsonProperty("profiles") final Table profiles,
@JsonProperty("pushChallenge") final Table pushChallenge,
@JsonProperty("redeemedReceipts") final TableWithExpiration redeemedReceipts,
@JsonProperty("remoteConfig") final Table remoteConfig,
@JsonProperty("reportMessage") final Table reportMessage,
@JsonProperty("reservedUsernames") final Table reservedUsernames,
@JsonProperty("subscriptions") final Table subscriptions) {
this.accounts = accounts;
this.deletedAccounts = deletedAccounts;
this.deletedAccountsLock = deletedAccountsLock;
this.issuedReceipts = issuedReceipts;
this.keys = keys;
this.messages = messages;
this.pendingAccounts = pendingAccounts;
this.pendingDevices = pendingDevices;
this.phoneNumberIdentifiers = phoneNumberIdentifiers;
this.profiles = profiles;
this.pushChallenge = pushChallenge;
this.redeemedReceipts = redeemedReceipts;
this.remoteConfig = remoteConfig;
this.reportMessage = reportMessage;
this.reservedUsernames = reservedUsernames;
this.subscriptions = subscriptions;
}
@Valid
@NotNull
@Valid
public AccountsTableConfiguration getAccounts() {
return accounts;
}
@NotNull
@Valid
public DeletedAccountsTableConfiguration getDeletedAccounts() {
return deletedAccounts;
}
@NotNull
@Valid
public Table getDeletedAccountsLock() {
return deletedAccountsLock;
}
@NotNull
@Valid
public IssuedReceiptsTableConfiguration getIssuedReceipts() {
return issuedReceipts;
}
@Valid
@NotNull
@Valid
public Table getKeys() {
return keys;
}
@NotNull
@Valid
public TableWithExpiration getMessages() {
return messages;
}
@NotNull
@Valid
public Table getPendingAccounts() {
return pendingAccounts;
}
@NotNull
@Valid
public Table getPendingDevices() {
return pendingDevices;
}
@NotNull
@Valid
public Table getPhoneNumberIdentifiers() {
return phoneNumberIdentifiers;
}
@NotNull
@Valid
public Table getProfiles() {
return profiles;
}
@NotNull
@Valid
public Table getPushChallenge() {
return pushChallenge;
}
@NotNull
@Valid
public TableWithExpiration getRedeemedReceipts() {
return redeemedReceipts;
}
@Valid
@NotNull
@Valid
public Table getRemoteConfig() {
return remoteConfig;
}
@NotNull
@Valid
public Table getReportMessage() {
return reportMessage;
}
@NotNull
@Valid
public Table getReservedUsernames() {
return reservedUsernames;
}
@NotNull
@Valid
public Table getSubscriptions() {
return subscriptions;
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import javax.validation.constraints.NotBlank;
public record FcmConfiguration(@NotBlank String credentials) {
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright 2013-2020 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.NotNull;
public class GcmConfiguration {
@NotNull
@JsonProperty
private long senderId;
@NotEmpty
@JsonProperty
private String apiKey;
public String getApiKey() {
return apiKey;
}
public long getSenderId() {
return senderId;
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public record GiftConfiguration(
long level,
@NotNull Duration expiration,
@Valid @NotNull Map<@NotEmpty String, @DecimalMin("0.01") @NotNull BigDecimal> currencies,
@NotEmpty String badge) {
}

View File

@@ -1,19 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import java.time.Duration;
import javax.validation.Valid;
public class MessageDynamoDbConfiguration extends DynamoDbConfiguration {
private Duration timeToLive = Duration.ofDays(14);
@Valid
public Duration getTimeToLive() {
return timeToLive;
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2013-2020 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.Min;
public class PushConfiguration {
@JsonProperty
@Min(0)
private int queueSize = 200;
public int getQueueSize() {
return queueSize;
}
}

View File

@@ -5,7 +5,6 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Duration;
public class RateLimitsConfiguration {
@@ -37,7 +36,7 @@ public class RateLimitsConfiguration {
private RateLimitConfiguration attachments = new RateLimitConfiguration(50, 50);
@JsonProperty
private RateLimitConfiguration prekeys = new RateLimitConfiguration(3, 1.0 / 10.0);
private RateLimitConfiguration prekeys = new RateLimitConfiguration(6, 1.0 / 10.0);
@JsonProperty
private RateLimitConfiguration messages = new RateLimitConfiguration(60, 60);
@@ -46,7 +45,7 @@ public class RateLimitsConfiguration {
private RateLimitConfiguration allocateDevice = new RateLimitConfiguration(2, 1.0 / 2.0);
@JsonProperty
private RateLimitConfiguration verifyDevice = new RateLimitConfiguration(2, 2);
private RateLimitConfiguration verifyDevice = new RateLimitConfiguration(6, 1.0 / 10.0);
@JsonProperty
private RateLimitConfiguration turnAllocations = new RateLimitConfiguration(60, 60);
@@ -63,9 +62,15 @@ public class RateLimitsConfiguration {
@JsonProperty
private RateLimitConfiguration usernameSet = new RateLimitConfiguration(100, 100 / (24.0 * 60.0));
@JsonProperty
private RateLimitConfiguration usernameReserve = new RateLimitConfiguration(100, 100 / (24.0 * 60.0));
@JsonProperty
private RateLimitConfiguration checkAccountExistence = new RateLimitConfiguration(1_000, 1_000 / 60.0);
@JsonProperty
private RateLimitConfiguration stories = new RateLimitConfiguration(10_000, 10_000 / (24.0 * 60.0));
public RateLimitConfiguration getAutoBlock() {
return autoBlock;
}
@@ -138,10 +143,16 @@ public class RateLimitsConfiguration {
return usernameSet;
}
public RateLimitConfiguration getUsernameReserve() {
return usernameReserve;
}
public RateLimitConfiguration getCheckAccountExistence() {
return checkAccountExistence;
}
public RateLimitConfiguration getStories() { return stories; }
public static class RateLimitConfiguration {
@JsonProperty
private int bucketSize;
@@ -164,28 +175,4 @@ public class RateLimitsConfiguration {
return leakRatePerMinute;
}
}
public static class CardinalityRateLimitConfiguration {
@JsonProperty
private int maxCardinality;
@JsonProperty
private Duration ttl;
public CardinalityRateLimitConfiguration() {
}
public CardinalityRateLimitConfiguration(int maxCardinality, Duration ttl) {
this.maxCardinality = maxCardinality;
this.ttl = ttl;
}
public int getMaxCardinality() {
return maxCardinality;
}
public Duration getTtl() {
return ttl;
}
}
}

View File

@@ -1,21 +1,24 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2021-2022 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 RecaptchaConfiguration {
@JsonProperty
@NotEmpty
private String secret;
private String projectPath;
private String credentialConfigurationJson;
public String getSecret() {
return secret;
@NotEmpty
public String getProjectPath() {
return projectPath;
}
@NotEmpty
public String getCredentialConfigurationJson() {
return credentialConfigurationJson;
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import java.math.BigDecimal;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class RecaptchaV2Configuration {
private BigDecimal scoreFloor;
private String projectPath;
private String siteKey;
private String credentialConfigurationJson;
@DecimalMin("0")
@DecimalMax("1")
@NotNull
public BigDecimal getScoreFloor() {
return scoreFloor;
}
@NotEmpty
public String getProjectPath() {
return projectPath;
}
@NotEmpty
public String getSiteKey() {
return siteKey;
}
@NotEmpty
public String getCredentialConfigurationJson() {
return credentialConfigurationJson;
}
}

View File

@@ -0,0 +1,49 @@
package org.whispersystems.textsecuregcm.configuration;
import javax.validation.constraints.NotBlank;
public class RegistrationServiceConfiguration {
@NotBlank
private String host;
private int port = 443;
@NotBlank
private String apiKey;
@NotBlank
private String registrationCaCertificate;
public String getHost() {
return host;
}
public void setHost(final String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(final int port) {
this.port = port;
}
public String getApiKey() {
return apiKey;
}
public void setApiKey(final String apiKey) {
this.apiKey = apiKey;
}
public String getRegistrationCaCertificate() {
return registrationCaCertificate;
}
public void setRegistrationCaCertificate(final String registrationCaCertificate) {
this.registrationCaCertificate = registrationCaCertificate;
}
}

View File

@@ -13,6 +13,7 @@ import javax.validation.constraints.NotNull;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import java.util.List;
public class SecureBackupServiceConfiguration {
@@ -24,9 +25,9 @@ public class SecureBackupServiceConfiguration {
@JsonProperty
private String uri;
@NotBlank
@NotEmpty
@JsonProperty
private String backupCaCertificate;
private List<@NotBlank String> backupCaCertificates;
@NotNull
@Valid
@@ -52,12 +53,12 @@ public class SecureBackupServiceConfiguration {
}
@VisibleForTesting
public void setBackupCaCertificate(final String backupCaCertificate) {
this.backupCaCertificate = backupCaCertificate;
public void setBackupCaCertificates(final List<String> backupCaCertificates) {
this.backupCaCertificates = backupCaCertificates;
}
public String getBackupCaCertificate() {
return backupCaCertificate;
public List<String> getBackupCaCertificates() {
return backupCaCertificates;
}
public CircuitBreakerConfiguration getCircuitBreakerConfiguration() {

View File

@@ -13,6 +13,7 @@ import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import java.util.List;
public class SecureStorageServiceConfiguration {
@@ -24,9 +25,9 @@ public class SecureStorageServiceConfiguration {
@JsonProperty
private String uri;
@NotBlank
@NotEmpty
@JsonProperty
private String storageCaCertificate;
private List<@NotBlank String> storageCaCertificates;
@NotNull
@Valid
@@ -52,12 +53,12 @@ public class SecureStorageServiceConfiguration {
}
@VisibleForTesting
public void setStorageCaCertificate(final String certificatePem) {
this.storageCaCertificate = certificatePem;
public void setStorageCaCertificates(final List<String> certificatePem) {
this.storageCaCertificates = certificatePem;
}
public String getStorageCaCertificate() {
return storageCaCertificate;
public List<String> getStorageCaCertificates() {
return storageCaCertificates;
}
public CircuitBreakerConfiguration getCircuitBreakerConfiguration() {

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class TurnConfiguration {
@JsonProperty
@NotEmpty
private String secret;
@JsonProperty
@NotNull
private List<String> uris;
public List<String> getUris() {
return uris;
}
public String getSecret() {
return secret;
}
}

View File

@@ -0,0 +1,38 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class TurnUriConfiguration {
@JsonProperty
@NotNull
private List<String> uris;
/**
* The weight of this entry for weighted random selection
*/
@JsonProperty
@Min(0)
private long weight = 1;
/**
* Enrolled numbers will always get this uri list
*/
private Set<String> enrolledNumbers = Collections.emptySet();
public List<String> getUris() {
return uris;
}
public long getWeight() {
return weight;
}
public Set<String> getEnrolledNumbers() {
return Collections.unmodifiableSet(enrolledNumbers);
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.google.common.annotations.VisibleForTesting;
import java.util.Collections;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class TwilioConfiguration {
@NotEmpty
private String accountId;
@NotEmpty
private String accountToken;
@NotEmpty
private String localDomain;
@NotEmpty
private String messagingServiceSid;
@NotEmpty
private String nanpaMessagingServiceSid;
@NotEmpty
private String verifyServiceSid;
@NotNull
@Valid
private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
@NotNull
@Valid
private RetryConfiguration retry = new RetryConfiguration();
@Valid
private TwilioVerificationTextConfiguration defaultClientVerificationTexts;
@Valid
private Map<String,TwilioVerificationTextConfiguration> regionalClientVerificationTexts = Collections.emptyMap();
@NotEmpty
private String androidAppHash;
@NotEmpty
private String verifyServiceFriendlyName;
public String getAccountId() {
return accountId;
}
@VisibleForTesting
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public String getAccountToken() {
return accountToken;
}
@VisibleForTesting
public void setAccountToken(String accountToken) {
this.accountToken = accountToken;
}
public String getLocalDomain() {
return localDomain;
}
@VisibleForTesting
public void setLocalDomain(String localDomain) {
this.localDomain = localDomain;
}
public String getMessagingServiceSid() {
return messagingServiceSid;
}
@VisibleForTesting
public void setMessagingServiceSid(String messagingServiceSid) {
this.messagingServiceSid = messagingServiceSid;
}
public String getNanpaMessagingServiceSid() {
return nanpaMessagingServiceSid;
}
@VisibleForTesting
public void setNanpaMessagingServiceSid(String nanpaMessagingServiceSid) {
this.nanpaMessagingServiceSid = nanpaMessagingServiceSid;
}
public String getVerifyServiceSid() {
return verifyServiceSid;
}
@VisibleForTesting
public void setVerifyServiceSid(String verifyServiceSid) {
this.verifyServiceSid = verifyServiceSid;
}
public CircuitBreakerConfiguration getCircuitBreaker() {
return circuitBreaker;
}
@VisibleForTesting
public void setCircuitBreaker(CircuitBreakerConfiguration circuitBreaker) {
this.circuitBreaker = circuitBreaker;
}
public RetryConfiguration getRetry() {
return retry;
}
@VisibleForTesting
public void setRetry(RetryConfiguration retry) {
this.retry = retry;
}
public TwilioVerificationTextConfiguration getDefaultClientVerificationTexts() {
return defaultClientVerificationTexts;
}
@VisibleForTesting
public void setDefaultClientVerificationTexts(TwilioVerificationTextConfiguration defaultClientVerificationTexts) {
this.defaultClientVerificationTexts = defaultClientVerificationTexts;
}
public Map<String,TwilioVerificationTextConfiguration> getRegionalClientVerificationTexts() {
return regionalClientVerificationTexts;
}
@VisibleForTesting
public void setRegionalClientVerificationTexts(final Map<String,TwilioVerificationTextConfiguration> regionalClientVerificationTexts) {
this.regionalClientVerificationTexts = regionalClientVerificationTexts;
}
public String getAndroidAppHash() {
return androidAppHash;
}
public void setAndroidAppHash(String androidAppHash) {
this.androidAppHash = androidAppHash;
}
public void setVerifyServiceFriendlyName(String serviceFriendlyName) {
this.verifyServiceFriendlyName = serviceFriendlyName;
}
public String getVerifyServiceFriendlyName() {
return verifyServiceFriendlyName;
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.google.common.annotations.VisibleForTesting;
import javax.validation.constraints.NotEmpty;
public class TwilioCountrySenderIdConfiguration {
@NotEmpty
private String countryCode;
@NotEmpty
private String senderId;
public String getCountryCode() {
return countryCode;
}
@VisibleForTesting
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getSenderId() {
return senderId;
}
@VisibleForTesting
public void setSenderId(String senderId) {
this.senderId = senderId;
}
}

View File

@@ -1,70 +0,0 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import javax.validation.constraints.NotEmpty;
public class TwilioVerificationTextConfiguration {
@JsonProperty
@NotEmpty
private String ios;
@JsonProperty
@NotEmpty
private String androidNg;
@JsonProperty
@NotEmpty
private String android202001;
@JsonProperty
@NotEmpty
private String android202103;
@JsonProperty
@NotEmpty
private String generic;
public String getIosText() {
return ios;
}
public void setIosText(String ios) {
this.ios = ios;
}
public String getAndroidNgText() {
return androidNg;
}
public void setAndroidNgText(final String androidNg) {
this.androidNg = androidNg;
}
public String getAndroid202001Text() {
return android202001;
}
public void setAndroid202001Text(final String android202001) {
this.android202001 = android202001;
}
public String getAndroid202103Text() {
return android202103;
}
public void setAndroid202103Text(final String android202103) {
this.android202103 = android202103;
}
public String getGenericText() {
return generic;
}
public void setGenericText(final String generic) {
this.generic = generic;
}
}

View File

@@ -8,8 +8,8 @@ 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.crypto.Curve;
import org.whispersystems.textsecuregcm.crypto.ECPrivateKey;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import javax.validation.constraints.NotNull;

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2013-2020 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.Min;
import java.time.Duration;
public class UsernameConfiguration {
@JsonProperty
@Min(1)
private int discriminatorInitialWidth = 2;
@JsonProperty
@Min(1)
private int discriminatorMaxWidth = 9;
@JsonProperty
@Min(1)
private int attemptsPerWidth = 10;
@JsonProperty
private Duration reservationTtl = Duration.ofMinutes(5);
public int getDiscriminatorInitialWidth() {
return discriminatorInitialWidth;
}
public int getDiscriminatorMaxWidth() {
return discriminatorMaxWidth;
}
public int getAttemptsPerWidth() {
return attemptsPerWidth;
}
public Duration getReservationTtl() {
return reservationTtl;
}
}

View File

@@ -0,0 +1,14 @@
package org.whispersystems.textsecuregcm.configuration.dynamic;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Duration;
public class DynamicAbusiveHostRulesConfiguration {
@JsonProperty
private Duration expirationTime = Duration.ofDays(1);
public Duration getExpirationTime() {
return expirationTime;
}
}

View File

@@ -0,0 +1,49 @@
package org.whispersystems.textsecuregcm.configuration.dynamic;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Set;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
public class DynamicCaptchaConfiguration {
@JsonProperty
@DecimalMin("0")
@DecimalMax("1")
@NotNull
private BigDecimal scoreFloor;
@JsonProperty
@NotNull
private Set<String> signupCountryCodes = Collections.emptySet();
@JsonProperty
@NotNull
private Set<String> signupRegions = Collections.emptySet();
public BigDecimal getScoreFloor() {
return scoreFloor;
}
public Set<String> getSignupCountryCodes() {
return signupCountryCodes;
}
@VisibleForTesting
public void setSignupCountryCodes(Set<String> numbers) {
this.signupCountryCodes = numbers;
}
@VisibleForTesting
public void setSignupRegions(final Set<String> signupRegions) {
this.signupRegions = signupRegions;
}
public Set<String> getSignupRegions() {
return signupRegions;
}
}

View File

@@ -5,7 +5,6 @@ import com.google.common.annotations.VisibleForTesting;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.validation.Valid;
public class DynamicConfiguration {
@@ -26,23 +25,13 @@ public class DynamicConfiguration {
@Valid
private DynamicRemoteDeprecationConfiguration remoteDeprecation = new DynamicRemoteDeprecationConfiguration();
@JsonProperty
@Valid
private DynamicMessageRateConfiguration messageRate = new DynamicMessageRateConfiguration();
@JsonProperty
@Valid
private DynamicPaymentsConfiguration payments = new DynamicPaymentsConfiguration();
@JsonProperty
private Set<String> featureFlags = Collections.emptySet();
@JsonProperty
@Valid
private DynamicTwilioConfiguration twilio = new DynamicTwilioConfiguration();
@JsonProperty
private DynamicSignupCaptchaConfiguration signupCaptcha = new DynamicSignupCaptchaConfiguration();
private DynamicCaptchaConfiguration captcha = new DynamicCaptchaConfiguration();
@JsonProperty
@Valid
@@ -55,6 +44,22 @@ public class DynamicConfiguration {
@Valid
private DynamicPushLatencyConfiguration pushLatency = new DynamicPushLatencyConfiguration(Collections.emptyMap());
@JsonProperty
@Valid
private DynamicTurnConfiguration turn = new DynamicTurnConfiguration();
@JsonProperty
@Valid
DynamicAbusiveHostRulesConfiguration abusiveHostRules = new DynamicAbusiveHostRulesConfiguration();
@JsonProperty
@Valid
DynamicMessagePersisterConfiguration messagePersister = new DynamicMessagePersisterConfiguration();
@JsonProperty
@Valid
DynamicPushNotificationConfiguration pushNotifications = new DynamicPushNotificationConfiguration();
public Optional<DynamicExperimentEnrollmentConfiguration> getExperimentEnrollmentConfiguration(
final String experimentName) {
return Optional.ofNullable(experiments.get(experimentName));
@@ -73,29 +78,12 @@ public class DynamicConfiguration {
return remoteDeprecation;
}
public DynamicMessageRateConfiguration getMessageRateConfiguration() {
return messageRate;
}
public DynamicPaymentsConfiguration getPaymentsConfiguration() {
return payments;
}
public Set<String> getActiveFeatureFlags() {
return featureFlags;
}
public DynamicTwilioConfiguration getTwilioConfiguration() {
return twilio;
}
@VisibleForTesting
public void setTwilioConfiguration(DynamicTwilioConfiguration twilioConfiguration) {
this.twilio = twilioConfiguration;
}
public DynamicSignupCaptchaConfiguration getSignupCaptchaConfiguration() {
return signupCaptcha;
public DynamicCaptchaConfiguration getCaptchaConfiguration() {
return captcha;
}
public DynamicRateLimitChallengeConfiguration getRateLimitChallengeConfiguration() {
@@ -109,4 +97,20 @@ public class DynamicConfiguration {
public DynamicPushLatencyConfiguration getPushLatencyConfiguration() {
return pushLatency;
}
public DynamicTurnConfiguration getTurnConfiguration() {
return turn;
}
public DynamicAbusiveHostRulesConfiguration getAbusiveHostRules() {
return abusiveHostRules;
}
public DynamicMessagePersisterConfiguration getMessagePersisterConfiguration() {
return messagePersister;
}
public DynamicPushNotificationConfiguration getPushNotificationConfiguration() {
return pushNotifications;
}
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright 2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration.dynamic;
import com.fasterxml.jackson.annotation.JsonProperty;
public class DynamicMessagePersisterConfiguration {
@JsonProperty
private boolean persistenceEnabled = true;
public boolean isPersistenceEnabled() {
return persistenceEnabled;
}
}

View File

@@ -1,18 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration.dynamic;
import com.fasterxml.jackson.annotation.JsonProperty;
public class DynamicMessageRateConfiguration {
@JsonProperty
private boolean enforceUnsealedSenderRateLimit = false;
public boolean isEnforceUnsealedSenderRateLimit() {
return enforceUnsealedSenderRateLimit;
}
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration.dynamic;
import com.fasterxml.jackson.annotation.JsonProperty;
public class DynamicPushNotificationConfiguration {
@JsonProperty
private boolean lowUrgencyEnabled = false;
public boolean isLowUrgencyEnabled() {
return lowUrgencyEnabled;
}
}

Some files were not shown because too many files have changed in this diff Show More