Compare commits

...

809 Commits

Author SHA1 Message Date
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
Jon Chambers
46d64b949e Don't read "soft-deleted" profiles
Nothing is actually "soft-deleting" profiles yet, and this
is a first step toward migrating profiles to a new data
store.
2021-11-24 12:02:07 -05:00
Jon Chambers
6919354520 Fix a counting bug with reported messages 2021-11-23 17:28:39 -05:00
Jon Chambers
a42fe9bfb0 Add crawler names to log messages 2021-11-23 16:22:09 -05:00
Jon Chambers
ee1f8b34ea Add a command for reserving usernames 2021-11-23 16:21:03 -05:00
Jon Chambers
c910fa406d Migrate reserved usernames from a relational database to DynamoDB 2021-11-23 16:21:03 -05:00
Jon Chambers
559205e33f Log cases where accounts are missing or have inconsistent PNIs 2021-11-23 15:40:31 -05:00
Chris Eager
c0756e9c60 Attempt an orderly websocket close on displacement before a hard disconnect 2021-11-23 11:36:32 -07:00
Ehren Kret
bf1190696e Add badge workaround for old Android builds 2021-11-23 09:58:06 -06:00
Jon Chambers
71dd0890de Restore an accidentally-removed PNI consistency check 2021-11-23 10:53:40 -05:00
Jon Chambers
e5acdf1402 Don't update the PNI attribute during general account updates 2021-11-23 10:53:40 -05:00
Jon Chambers
0f08b6bb59 Drop "got successful captcha" messages from INFO to DEBUG 2021-11-22 17:06:34 -05:00
Jon Chambers
6198a7b69a Remove spurious @JsonProperty annotations 2021-11-22 15:43:09 -05:00
Jon Chambers
067aee6664 Remove unused properties from OutgoingMessageEntity 2021-11-22 15:43:09 -05:00
Jon Chambers
138a2ebbd0 Drop transactional logic from phone number identifier migration 2021-11-22 15:32:24 -05:00
Jon Chambers
296f6a7a88 Make phone number identifiers non-optional 2021-11-22 15:32:24 -05:00
Jon Chambers
069ffa9921 Drop PNI migration tools 2021-11-22 15:32:24 -05:00
Jon Chambers
f42fd8a840 Retire unused diagnostic metrics 2021-11-22 15:31:16 -05:00
Jon Chambers
10f27af6f2 Retire old unsealed-sender meters 2021-11-22 15:31:16 -05:00
Jon Chambers
0bbd34d060 Use text blocks where possible 2021-11-22 15:30:31 -05:00
Jon Chambers
282daeb0dc Add a command to assign PNIs to accounts that don't already have one 2021-11-22 15:03:19 -05:00
Jon Chambers
d33b313c11 Break down legacy "get profile" requests by platform 2021-11-19 12:37:39 -05:00
Ehren Kret
fb7316c9ae Return subscription status string in GET 2021-11-19 11:36:01 -06:00
Ehren Kret
279b0a51d9 Use latest invoice on subscription to generate receipts 2021-11-19 11:25:38 -06:00
Ehren Kret
6547d5ebf3 More consistent naming of receipt credentials endpoints 2021-11-19 10:14:00 -06:00
Ehren Kret
4f1ef9a039 Add additional http status codes to /v1/subscription/boost/receipt_credentials 2021-11-19 10:11:33 -06:00
Ehren Kret
4c80714d19 Update sample.yml 2021-11-18 10:49:30 -06:00
Jon Chambers
077ead71a5 Rename legacy profile methods to separate them in metrics 2021-11-18 11:31:15 -05:00
Ehren Kret
caba110266 Revert "Revert "Remove transparent SVG for badging""
This reverts commit 0fdb23c1e9.
2021-11-18 10:23:09 -06:00
Ehren Kret
0fdb23c1e9 Revert "Remove transparent SVG for badging"
This reverts commit 13a84f0c72.
2021-11-18 10:09:34 -06:00
Ehren Kret
13a84f0c72 Remove transparent SVG for badging 2021-11-18 10:04:42 -06:00
Jon Chambers
669bd58e33 Drop the unused Key utility class 2021-11-17 10:57:29 -05:00
Chris Eager
6e82740a9b Update sample.yml 2021-11-16 17:25:45 -07:00
Ehren Kret
7ea43a728d Set boost description from configuration 2021-11-16 17:21:57 -06:00
Chris Eager
71b38356b1 Update to Mockito 4.0.0 2021-11-16 15:56:35 -07:00
Chris Eager
5a99708f56 Update some deprecated usages 2021-11-16 15:56:13 -07:00
Chris Eager
24191d9599 Update Dropwizard to 2.0.25 2021-11-16 15:56:00 -07:00
Chris Eager
482ea8eb40 Update minimum required maven to 3.8.3 2021-11-16 15:55:52 -07:00
Jon Chambers
1dae05651f Add PNIs to account creation and whoami responses 2021-11-16 15:08:10 -05:00
Jon Chambers
5164e92538 Shorten metric names 2021-11-16 15:08:10 -05:00
Jon Chambers
f89a20dbc7 Allow callers to set/retrieve keys by ACI or PNI 2021-11-16 15:08:10 -05:00
Jon Chambers
3a4c5a2bfb Store and retrieve one-time pre-keys by UUID 2021-11-16 15:08:10 -05:00
Jon Chambers
5e1334e8de s/KeysDynamoDb/Keys/ 2021-11-16 15:08:10 -05:00
Jon Chambers
fa6e3d3690 Allow clients to request PNI-based group credentials 2021-11-16 15:08:10 -05:00
Jon Chambers
9383e7716b Resolve CertificateControllerTest warnings and recommendations 2021-11-16 15:08:10 -05:00
Jon Chambers
cfe34fbf0f Allow unsealed-sender messages to be addressed by PNI 2021-11-16 15:08:10 -05:00
Jon Chambers
9fe110625c Add a destinationUuid field to envelopes 2021-11-16 15:08:10 -05:00
Jon Chambers
975f753c2b Add an endpoint for testing whether an account with a given ACI or PNI exists 2021-11-16 15:08:10 -05:00
Jon Chambers
e6237480f8 Require that unidentified access keys be exactly 16 bytes 2021-11-16 15:08:10 -05:00
Chris Eager
966d4e29d4 Update sample.yml config to pass mvn verify 2021-11-16 11:43:07 -07:00
Chris Eager
26f876a2cb Check service configurations in verify phase 2021-11-16 11:43:07 -07:00
Jon Chambers
ab9e6ac48a Revert "Replace zkgroup with libsignal-client"
This reverts commit 73ea6e4251.
2021-11-16 11:35:10 -05:00
Jon Chambers
c1d6c04ab2 Revert "Replace curve25519-java with libsignal-client"
This reverts commit 0011b8925b.
2021-11-16 11:35:10 -05:00
Jon Chambers
888cec3d56 Introduce a filter for correcting numeric "online" flags 2021-11-16 10:15:14 -05:00
Jon Chambers
1461bcc2c2 Correct envelope types for certain iOS builds 2021-11-16 10:15:14 -05:00
Jon Chambers
11f1cf80bd Move MessageControllerTest out of the tests sub-package to expose package-private elements from the class under test 2021-11-16 10:15:14 -05:00
Jordan Rose
c675cc8b26 Test the response code for invalid serialized zkgroup objects
Test by Jon, making sure this is consistent even without up-front size
checking.
2021-11-16 09:52:38 -05:00
Jordan Rose
0011b8925b Replace curve25519-java with libsignal-client
These APIs stemmed from a common source long ago, so there's not much
to change!
2021-11-16 09:52:38 -05:00
Jordan Rose
73ea6e4251 Replace zkgroup with libsignal-client 2021-11-16 09:52:38 -05:00
Jon Chambers
e4441dddbb Consolidate Redis client resources 2021-11-16 09:52:12 -05:00
Chris Eager
8d1d56f694 Update to Java 17 2021-11-15 16:42:43 -07:00
Jon Chambers
2015ba77ca Switch to a disallowed prefix model instead of a disallowed country code model 2021-11-15 15:44:55 -05:00
Chris Eager
7033a0f68f Set checkStaleness to true for protoc 2021-11-12 13:34:15 -07:00
Jon Chambers
6ada76da7f Parallelize assignment of phone number identifiers 2021-11-12 11:03:46 -05:00
Jon Chambers
cbdec0cb22 Remove legacy push latency measurement pathways 2021-11-11 15:44:07 -05:00
Chris Eager
de6e9d31c9 Add dedicated crawler for directory reconciler 2021-11-11 13:38:13 -07:00
Jon Chambers
f0a6be32fc Add a crawler to assign PNIs to existing accounts 2021-11-10 11:15:05 -05:00
Ehren Kret
5c4855cca6 Remove trailing space 2021-11-10 10:11:49 -06:00
Ehren Kret
2e1e380418 Also update description text of boost badge 2021-11-10 10:10:53 -06:00
Ehren Kret
d07f0b4f71 Update badge description text 2021-11-10 09:51:51 -06:00
Jon Chambers
aaa2a6eef1 Break down push latency metrics by VOIP/not-VOIP and optionally by client version 2021-11-10 10:35:41 -05:00
Jon Chambers
b1f56c3324 Resolve formatting complaints 2021-11-10 10:15:14 -05:00
Jon Chambers
da5c0ae4b6 Enable Payments Beta for more country codes 2021-11-10 10:15:14 -05:00
Jon Chambers
1e1394560d Check length of cancellation reason list before getting reason codes 2021-11-09 11:42:44 -05:00
Jon Chambers
bae0196bcf Tolerate null UUID attribute values 2021-11-09 11:00:27 -05:00
Jon Chambers
3398955c1a Add basic support for phone number identifiers 2021-11-09 10:23:08 -05:00
Chris Eager
a1b925d1e0 Reduce visiblity on one constructor 2021-11-08 14:30:52 -07:00
Chris Eager
31c0c3275f Use the latest and in config, @JsonCreator 2021-11-08 14:30:52 -07:00
Chris Eager
0a4392f700 Streamline ExternalServiceCredentialsGenerator construction 2021-11-08 14:30:52 -07:00
Chris Eager
eb86986cf4 Add /v2/directory/auth endpoint 2021-11-08 14:30:52 -07:00
Ehren Kret
1053a47e42 Add an exception mapper for CompletionExceptions 2021-11-04 19:12:31 -05:00
Ehren Kret
99b1f48e0e Copy badges from existing account on re-reg 2021-11-04 18:00:47 -05:00
Jon Chambers
c21eb6aa50 Update to the latest abusive message filter 2021-11-01 15:27:14 -04:00
Jon Chambers
6dddf54222 Consolidate rate-limit counters 2021-11-01 14:33:05 -04:00
Jon Chambers
9e3eb2319e Update to the latest abusive message filter 2021-11-01 13:12:23 -04:00
Jon Chambers
1d8dcda815 Update to the latest abusive message filter 2021-11-01 12:45:50 -04:00
Jon Chambers
ee52a84262 Update to the latest abusive message filter 2021-11-01 12:19:21 -04:00
Jon Chambers
eb51e81faa Configuration-only change 2021-11-01 11:41:26 -04:00
Jon Chambers
d41ef1df18 Configuration-only change 2021-11-01 11:27:02 -04:00
Jon Chambers
66d47aff2c Update deployment configuration 2021-11-01 11:10:19 -04:00
Jon Chambers
c931103712 Remove unused utility classes 2021-11-01 10:51:47 -04:00
Jon Chambers
ad1aeea74b Add an abusive message filter interface and submodule 2021-11-01 10:51:47 -04:00
Ehren Kret
ae7f8af03e Mark boost and subscription configuration as not null 2021-10-28 16:12:23 -07:00
Ehren Kret
a52c91a665 Add names to subscription levels 2021-10-28 14:48:44 -07:00
Ehren Kret
94bf3a3902 Extract logic for created header controlled resource bundles 2021-10-28 14:26:53 -07:00
Ehren Kret
f5a539e128 Add subscriptions resource bundle 2021-10-28 14:15:20 -07:00
Ehren Kret
24480b2090 Add endpoint to fetch information on boost badges 2021-10-26 14:05:59 -07:00
Ehren Kret
a124b3abe9 Fix encoding of boost amounts 2021-10-26 08:08:09 -07:00
Ehren Kret
090d722b61 Add method to retrieve receipt credentials for a boost payment 2021-10-25 14:54:40 -07:00
Ehren Kret
d27ec6fe8d Create boost create endpoint 2021-10-25 12:58:32 -07:00
Jon Chambers
8d34f3447b Drop an unused registration meter 2021-10-25 14:52:21 -04:00
Ehren Kret
72b52965b9 Expand definition of badge SVGs 2021-10-25 10:28:02 -07:00
Jon Chambers
ae7077c643 Refresh accounts from storage when checking for device state changes after requests 2021-10-22 14:02:28 -04:00
Jon Chambers
11598e855f Count non-normalized or impossible numbers 2021-10-22 14:01:54 -04:00
Jon Chambers
534c577f59 Enforce phone number normalization when creating accounts or changing numbers 2021-10-22 14:01:54 -04:00
Jon Chambers
7762afc497 Add a method for verifying that numbers are normalized in addition to being dialable 2021-10-22 14:01:54 -04:00
Jon Chambers
a3fe4b9980 Update/parameterize ValidNumberTest 2021-10-22 14:01:54 -04:00
Jon Chambers
598599cd14 Use a default reportMessage configuration if not specified 2021-10-21 15:34:57 -05:00
Ehren Kret
07cd69ab34 Add endpoint for fetching boost amounts 2021-10-21 13:56:35 -05:00
Jon Chambers
3b764bed7a Make DynamicConfigurationManager generic 2021-10-19 11:52:29 -04:00
Jon Chambers
c91d5c2fdb Count reported messages per sender 2021-10-19 11:47:54 -04:00
Jon Chambers
40f7e6e994 Remove unused imports 2021-10-19 11:47:54 -04:00
Ehren Kret
ee9aa9ce12 Round up by days on receipt expiration 2021-10-18 12:11:27 -05:00
Ehren Kret
08304bf375 Validate GetReceiptCredentialsRequest 2021-10-18 11:57:18 -05:00
Ehren Kret
8b8c6237be Use last subscription created at time as a subscription generation number 2021-10-14 12:06:19 -05:00
Ehren Kret
c0837104cd Bring badge configuration into levels information 2021-10-14 11:35:18 -05:00
Jon Chambers
fe21d014f7 Remove legacy rate-limiting tools 2021-10-14 11:43:18 -04:00
Ehren Kret
75c5032cd3 Add method to set default payment method 2021-10-14 10:37:12 -05:00
Jon Chambers
f84e7aebd0 Count numbers that can't be normalized because another account has the normalized form of the number 2021-10-14 11:10:12 -04:00
Jon Chambers
c379a3d297 Remove deprecated counters 2021-10-14 10:52:20 -04:00
Jon Chambers
eedeaaecee Update rate-limiting for requests matching specific criteria 2021-10-14 10:42:16 -04:00
Ehren Kret
64eeb1e361 Move to using collections for sprites and svgs for badges 2021-10-13 23:25:39 -05:00
Ehren Kret
e07597eba7 Add initial text for donation badges 2021-10-13 23:08:02 -05:00
Ehren Kret
5f2656710c Update badge configuration to new style 2021-10-13 22:58:57 -05:00
Ehren Kret
1af53f2612 Simplify getLevels API 2021-10-13 13:28:31 -05:00
Ehren Kret
c89cfa4927 Move property setting above servlet construction 2021-10-13 13:17:07 -05:00
Ehren Kret
bbde93a3c7 Enable unwrapping of CompletionStage 2021-10-13 11:46:20 -05:00
Ehren Kret
b01b76d78f First pass at subscriptions API
This is an incomplete first pass at building the subscriptions API. More API endpoints are still to be added along with controller tests.
2021-10-12 21:23:20 -05:00
Ehren Kret
75c22038eb Create empty Badges_en.properties file 2021-10-11 22:27:30 -05:00
Jon Chambers
3c1705994d Count accounts with non-normalized phone numbers 2021-10-11 10:13:08 -04:00
Jon Chambers
439d2f5df8 Update to libphonenumber 8.12.33 2021-10-05 15:19:59 -04:00
Chris Eager
d2bc3c7360 Add dynamic configuration to disable directory reconciler 2021-10-04 14:38:47 -07:00
Jon Chambers
9734433f00 Use the default SecureRandom algorithm for tests 2021-10-04 11:37:32 -04:00
Ehren Kret
5bd08800bb Remove public modifier from test class 2021-10-01 14:27:07 -05:00
Ehren Kret
3032415141 Add receipt redemption API to chat server 2021-10-01 12:44:47 -05:00
Jon Chambers
ba58a95a0f Add support for changing phone numbers 2021-10-01 10:15:33 -04:00
Jon Chambers
aa4bd92fee Lazy-load scripts; fall back to eval if evalsha returns NOSCRIPT 2021-09-29 16:08:17 -04:00
Jon Chambers
f37c76dab1 Drop LuaScript 2021-09-29 16:08:17 -04:00
Jon Chambers
863969c77c Resolve ClusterLuaScript warnings/suggestions 2021-09-29 16:08:17 -04:00
Jon Chambers
2383aaaa3d Update ClusterLuaScript formatting 2021-09-29 16:08:17 -04:00
Jon Chambers
715d1157ad Reject Redis commands when disconnected 2021-09-29 16:07:26 -04:00
Jon Chambers
4aaae3f445 Use a single configuration URI for Redis clusters 2021-09-29 14:46:09 -04:00
Jon Chambers
8359ef73f4 Cycle all connected websockets on any device or account enabled state change 2021-09-29 14:18:35 -04:00
Jon Chambers
c6bb649adb Simplify map-building logic 2021-09-29 14:18:35 -04:00
Jon Chambers
e333cbd94d Close websockets on account deletion 2021-09-29 14:11:16 -04:00
Ehren Kret
cc9a825279 Remove checked in generated code 2021-09-29 13:10:27 -05:00
Chris Eager
5189cbe5c7 apply editorconfig formatting 2021-09-29 10:31:39 -07:00
Chris Eager
d1d6e5c652 Filter stale ephemeral messages from cache 2021-09-29 10:31:39 -07:00
Chris Eager
3e5087e60b Remove obsolete ephemeral queue handling 2021-09-29 10:31:39 -07:00
Chris Eager
93c3cea912 Clean up old "ephemeral" metrics 2021-09-29 10:31:39 -07:00
Chris Eager
e824b861d4 Drop accounts table 2021-09-29 10:31:20 -07:00
Chris Eager
e8dd1e0bf2 Fixup formatting 2021-09-29 10:31:20 -07:00
Ehren Kret
533afa4c6e Upgrade to zkgroup 0.8.1 2021-09-28 09:28:28 -05:00
Ehren Kret
559026933d Add low and high detail svgs to badges 2021-09-27 17:00:09 -05:00
Ehren Kret
7864405efd Remove single URL in favor of density based sprite sheets 2021-09-27 16:50:18 -05:00
Jon Chambers
a5575902de Pause if we bump into an exception while trying to persist messages 2021-09-27 13:38:17 -04:00
Ehren Kret
5b9bce59e1 Upgrade to zkgroup 0.8.0 2021-09-24 15:56:28 -05:00
Ehren Kret
041aed2d72 Remove zkgroup enabled flag
The last remnants of a time before zkgroup have been swept away.
2021-09-23 09:24:06 -05:00
Jon Chambers
02a296e500 Use a MIME Base64 decoder for provisioning messages 2021-09-22 16:30:44 -04:00
Jon Chambers
98e41f9a37 Improve Redis exception handling 2021-09-22 10:31:39 -04:00
Chris Eager
6a71d369e2 More Accounts cleanup
* Remove `AccountStore`
* Clean up `AccountsDynamoDb#delete`
* Rename `AccountsDynamoDb` → `Accounts`
* Remove unused configuration
* Move Accounts scan page size to static configuration
* Remove disabled tests and related methods
2021-09-21 15:25:16 -07:00
Ehren Kret
75661fa800 Add JavaTimeModule 2021-09-20 16:42:38 -05:00
Ehren Kret
df5498e1c0 Add test for self badge fetching 2021-09-20 16:42:38 -05:00
Ehren Kret
c0af911197 Show invisible badges to query for self 2021-09-20 16:42:38 -05:00
Ehren Kret
44bc90e5ab Return a badge with additional properties when fetching your own profile 2021-09-20 16:42:38 -05:00
Ehren Kret
5c1cde1b28 Add visibility modifying helper method in AccountBadge 2021-09-20 16:41:09 -05:00
Ehren Kret
3172b571c6 Move "testing" string into BadgeConfiguration 2021-09-20 16:41:09 -05:00
Ehren Kret
17e8b77e88 Add unit test for setting badges 2021-09-20 16:41:09 -05:00
Ehren Kret
8011935a3b Fix compilation issues created by constructor changes 2021-09-20 16:41:09 -05:00
Ehren Kret
3f3052c23c Remove isZkEnabled 2021-09-20 16:41:09 -05:00
Ehren Kret
8f17f45339 Reorder creating of testing badges
Need their expiration time to always get refreshed on set so reorder
how they're created to ensure that's the case.
2021-09-20 16:41:09 -05:00
Ehren Kret
009e2eeb97 Enable editing of badges on profiles 2021-09-20 16:41:09 -05:00
Ehren Kret
c70fa48835 Fix indentation of ProfileController#setProfile 2021-09-20 16:41:09 -05:00
Ehren Kret
bd5f5c407b Add method to merge badge ids list into a profile 2021-09-20 16:41:09 -05:00
Ehren Kret
2bc573a53d Add additional test badges for translation 2021-09-20 16:41:09 -05:00
Ehren Kret
537d61d5bd Add badges to CreateProfileRequest
This will permit users to set the order and visibility of badges on
their profile.
2021-09-20 16:41:09 -05:00
Jon Chambers
09519ae942 Only retry websocket sending if the client is still connected 2021-09-20 14:24:07 -04:00
Chris Eager
2a67b2e610 Remove Accounts Postgres 2021-09-20 11:10:24 -07:00
Chris Eager
8161f55a82 Add dynamic configuration for setting Dynamo as primary 2021-09-17 13:28:45 -07:00
Chris Eager
ecee189ad8 Add AccountDatabaseCrawler.dedicatedDynamoMigrationCrawler 2021-09-17 11:27:20 -07:00
Jon Chambers
ef0900f3ac Add .tx/ to .gitignore 2021-09-17 13:43:52 -04:00
Fedor Indutny
383d744bd8 Log the error message when retrying queue send 2021-09-16 18:03:42 -04:00
Jon Chambers
c2ba8ab562 Identify receipt destinations by UUID instead of e164 2021-09-16 10:47:03 -04:00
Chris Eager
cd49ea43c0 Use queryPaginator when loading messages 2021-09-16 10:46:37 -04:00
Chris Eager
53aa45a2bb Use queryPaginator when deleting messages 2021-09-16 10:46:37 -04:00
Chris Eager
83e0a19561 Migrate MessagesDynamoDbRule to MessagesDynamoDbExtension 2021-09-16 10:46:37 -04:00
Jon Chambers
6a5d475198 Add a "refresh websocket on number change" provider 2021-09-16 10:37:34 -04:00
Jon Chambers
49ccbba2e3 Generalize the "watch for websockets that need to be refreshed" listener 2021-09-16 10:37:34 -04:00
Fedor Indutnyy
41735ed40e Introduce queueDrainRetry counter metric 2021-09-16 10:30:19 -04:00
Ehren Kret
2d11a433c9 Wrap all calls to getAcceptableLanguages
ContainerRequestContext#getAcceptableLanguages throws a
ProcessingException if the header has invalid values in it. Rather than
error out of the request entirely with the exception handler for that
exception, we just treat it as though no Accept-Languages header was
specified.
2021-09-16 09:28:21 -05:00
Ehren Kret
e79ab2521f Rename field in ConfiguredProfileBadgeConverter 2021-09-16 09:28:21 -05:00
Ehren Kret
fb1f99da87 Add a method to enable a badge for all accounts 2021-09-16 09:28:21 -05:00
Ehren Kret
08c6a8c2e5 Add category to badges 2021-09-16 09:28:21 -05:00
Ehren Kret
ce3835e176 Rename id to name in the configuration 2021-09-16 09:28:21 -05:00
Ehren Kret
39f6eadbb9 Add test for add and remove badges 2021-09-16 09:28:21 -05:00
Ehren Kret
16dba09b61 Handle merging badges when adding to account 2021-09-16 09:28:21 -05:00
Ehren Kret
d5ebf2f2ed Rename name to id in Account#removeBadge 2021-09-16 09:28:21 -05:00
Ehren Kret
8a8e6e7b49 Rename name to id in the stored badge information and expose id in the profile endpoint 2021-09-16 09:28:21 -05:00
Ehren Kret
34e21b9f7b Change name to id on AccountBadge
This makes it distinct from the localized name field on the Badge
entity that is returned.
2021-09-16 09:28:21 -05:00
Ehren Kret
98a31d1474 Switch ProfileController to the actual badge converter 2021-09-16 09:28:21 -05:00
Chris Eager
72a0c1be0f Tune mismatch logging 2021-09-15 16:46:10 -07:00
Ehren Kret
5b25e38e41 Ensure badges are in ordered collections 2021-09-15 16:20:15 -05:00
Chris Eager
2fb400280b Remove unused parameter from deleteMessageByDestinationAndGuid 2021-09-15 10:14:08 -07:00
Ehren Kret
79ad09524e Implement the ProfileBadgeConverter interface 2021-09-15 10:32:20 -05:00
Chris Eager
5f8accb492 Add acceptable languages from request to variable 2021-09-14 17:43:39 -07:00
Chris Eager
6fcadc2297 Handle exception reading Accept-Language header 2021-09-13 18:07:16 -07:00
Chris Eager
3f4e1522eb Only put accounts that exhaust optimistic lock retries in migration retry table 2021-09-13 15:00:01 -07:00
Graeme Connell
6304c84cdb Add ContactDiscoveryWriterTest based on mock. 2021-09-13 15:20:21 -06:00
Chris Eager
894297efa9 Add dynamic configuration for doing a mismatch post-check 2021-09-13 13:54:19 -07:00
Chris Eager
a51a7a0901 Add MigrationMismatchedAccounts to AccountsManager 2021-09-13 13:54:19 -07:00
Chris Eager
372e131e25 Update PaymentsControllerTest 2021-09-13 09:58:42 -07:00
Chris Eager
6c6e6a4975 Switch to actions/setup-java’s built-in caching 2021-09-13 09:40:44 -07:00
Sophiah Ho
cd66a1ceb7 fix merge issue after 2021 Aug 15 commit d1735c7e57 (#137) 2021-09-13 09:39:11 -07:00
Sophiah Ho
feb59deb28 Use BigDecimal instead of Double for currency rate calculations (#134)
use BigDecimal instead of double for accuracy
2021-09-10 16:15:57 -05:00
Nicolas Remond
489519a982 Use Map.of() for statically defined map 2021-09-10 14:27:18 -05:00
Dambar Pun
a96865d0f5 Update RedisInputStream.java
Fix code style
2021-09-10 14:24:52 -05:00
Blake Irvin
12e11609a9 pin 3rd-party Actions dep by full SHA
This change follows GitHub's security-hardening guidance. By pinning to a full SHA, we reduce our exposure to supply-chain attacks where a malicious party could compromise the 3rd-party Actions repo, commit malicious code, and then mutate an existing git tag to redirect to a SHA containing the malicious commit.

See https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions for more.
2021-09-10 14:22:11 -05:00
F2theK
5b404095b0 Added missing config entries
Starting server with sample.yml throws errors because of missing elements in config - not just empty ones
2021-09-10 14:19:10 -05:00
Chris Eager
6a6555e2d5 Add metrics for AuthEnablementRequestEventListener displacements 2021-09-10 12:01:05 -07:00
Chris Eager
49489a6021 Re-check mismatched accounts after a delay, to avoid false positives from concurrent requests 2021-09-10 11:31:44 -07:00
Chris Eager
8cd93d68e4 Add MetricsUtil 2021-09-10 11:31:44 -07:00
Chris Eager
f3b9a8d97f Add account to migration retry table on transient dynamo failure 2021-09-10 11:30:49 -07:00
Chris Eager
b91a69d8b3 Add asynchronous chunk pre-read to AccountDatabaseCrawler 2021-09-10 11:14:11 -07:00
Chris Eager
624e40e3b7 Add separate AccountsDatabaseCrawler for DynamoDB migration 2021-09-10 11:14:11 -07:00
Chris Eager
23a076a204 Update Account#getNextDeviceId to not reuse disable device’s IDs 2021-09-10 10:48:48 -07:00
Chris Eager
016141a05d Add DevicesHelper 2021-09-10 10:48:48 -07:00
Graeme Connell
a064b25a14 Fix CDS writer to use AccountsManager. 2021-09-10 11:36:06 -06:00
Ehren Kret
bd40e32f3b Send acceptable languages instead of request into the profile badge converter 2021-09-10 10:53:04 -05:00
Ehren Kret
81a21c0d5f Use @NotNull since @NotEmpty doesn't support URL 2021-09-10 10:49:31 -05:00
Ehren Kret
6478210330 Update configuration for badges to use URL instead of String 2021-09-10 10:49:31 -05:00
Ehren Kret
aa1c37fe26 Create configuration for badges 2021-09-10 10:14:16 -05:00
Ehren Kret
6ee23b0186 Create resource bundle for badges 2021-09-10 10:11:56 -05:00
Jon Chambers
40eb445592 Add a command to set a user's discoverability in CDS 2021-09-10 10:34:20 -04:00
Jon Chambers
ce7d687205 Add a shutdown monitor that publishes shutdown state as a metric 2021-09-08 16:37:05 -04:00
Chris Eager
758900b7a8 Register AuthEnablementApplicationEventListener 2021-09-08 13:11:09 -07:00
Chris Eager
539b62a829 Add request event listener that handles device.isEnabled changes 2021-09-08 13:11:09 -07:00
Jon Chambers
2866f1b213 Include e164 in account creation (whoami) responses 2021-09-07 16:52:32 -04:00
Ehren Kret
fc1465c05d Wire up stored account badges to the profile endpoints 2021-09-07 15:51:29 -05:00
Ehren Kret
bc887ec6fa Add visibility flag to badge storage 2021-09-07 15:50:29 -05:00
Ehren Kret
84b3d324bb Creates a storage object for badges 2021-09-07 15:49:41 -05:00
Ehren Kret
fc10108788 Make fields final in Badge entity 2021-09-07 15:39:48 -05:00
Ehren Kret
fbbc1bec58 Add badge entity to profile 2021-09-07 15:39:48 -05:00
Graeme Connell
2059bb5ef8 Update test to handle read-then-write in ContactDiscoveryWriter. 2021-09-07 13:41:47 -06:00
gram-signal
b080a5db4d Get-and-set accounts, since other updates may have made them stale.
Co-authored-by: Chris Eager <79161849+eager-signal@users.noreply.github.com>
2021-09-07 13:41:47 -06:00
Graeme Connell
b4aabd799b Canonical discoverability writing. 2021-09-07 13:41:47 -06:00
Jon Chambers
92f035bc2a Add a "change number" device/account capability 2021-09-07 15:07:30 -04:00
Chris Eager
18a6df34bd Add timers to processChunk and deleteRecentlyDeletedUuids 2021-09-03 14:54:51 -07:00
Chris Eager
b1274125c9 Add start/chunk/sleep logging to crawler 2021-09-03 14:54:51 -07:00
Chris Eager
dceebc1c8d Consistently use whenCompleteAsync(…, migrationThreadPool) 2021-09-03 14:02:51 -07:00
Chris Eager
6aadb4b458 Parameterize registration lock constructor when updating account attributes 2021-09-03 14:02:27 -07:00
Fedor Indutnyy
703405b874 Start WebSocket before registering its presence 2021-08-27 16:41:07 -04:00
Jon Chambers
d1735c7e57 Retire AmbiguousIdentifier 2021-08-27 13:40:46 -04:00
Jon Chambers
1f815b49dd Measure APNs rejections by reason 2021-08-27 11:52:29 -04:00
Jon Chambers
a9339b7037 Update to Pushy 0.15.0 2021-08-27 11:52:29 -04:00
Jon Chambers
f2c6ca182d Include the current server version in the tag list for Dropwizard metrics 2021-08-27 11:52:11 -04:00
Jon Chambers
b946c27a20 Remove a metric aggregator 2021-08-27 11:52:11 -04:00
Ehren Kret
9fd6358518 Add missing section to end of LICENSE file 2021-08-26 12:55:56 -05:00
Jon Chambers
8a8a848fac Record error metrics from Twilio Verify 2021-08-26 12:22:17 -04:00
Chris Eager
aeb9f67266 Migrate MessageSenderTest to JUnit 5 2021-08-25 12:25:10 -05:00
Chris Eager
e08c5a412e Insert ephemeral messages in the standard cache queue 2021-08-25 12:25:10 -05:00
Chris Eager
a7443a9ece Don’t persist ephemeral messages; clear ephemeral field when sending to clients 2021-08-25 11:17:00 -05:00
Chris Eager
54fe3b9a43 Update TextSecure.proto 2021-08-25 11:17:00 -05:00
Ehren Kret
ba522b1691 Clean redis message cache structure 2021-08-24 10:30:52 -05:00
Jon Chambers
739c5bf22c Add a counter to estimate announcement group adoption 2021-08-23 17:31:34 -04:00
Chris Eager
7cdadeb791 Register circuit breaker metrics for FaultTolerantPubSubConnection 2021-08-23 15:49:19 -05:00
Chris Eager
dadf43b93e Consolidate directory reconciliation on v3 endpoints 2021-08-19 14:18:38 -05:00
Chris Eager
bd820e6d2e Migrate websocket-resources test to JUnit 5 and .editorconfig 2021-08-19 14:09:57 -05:00
Chris Eager
19f7b207b7 Extract configuration for WebSocket max message sizes 2021-08-19 14:09:57 -05:00
Chris Eager
a398e2269c Update AccountsManager mismatch comparison 2021-08-19 14:08:48 -05:00
Chris Eager
2e28fb97a4 Delete DynamoDB accounts with invalid UUIDs in AccountsManager#create 2021-08-19 14:05:21 -05:00
Chris Eager
5c68d83a93 Add integration test for re-registration with and without Dynamo DB 2021-08-19 14:05:21 -05:00
Chris Eager
0b7c3ad745 .editorconfig formatting 2021-08-16 16:32:26 -05:00
Chris Eager
0cde06557d Catch and log unexpected exceptions keyspace notification executor service 2021-08-16 16:32:26 -05:00
Chris Eager
27844fe692 Add JUnit 5 RedisClusterExtension 2021-08-13 12:07:04 -05:00
Ehren Kret
779051ef9f Add minThreads(64) to multiRecipientMessageExecutor 2021-08-12 13:03:40 -05:00
Ehren Kret
d13741fbd5 Change from using parallel streams to using an ExecutorService 2021-08-12 12:05:01 -05:00
Ehren Kret
f7f870fe62 Execute send multi-recipient message loop in parallel 2021-08-12 12:05:01 -05:00
Chris Eager
de59aa099d Add uncaught exception handler 2021-08-12 11:10:05 -05:00
Ehren Kret
57a478b898 Remove unused local variable 2021-08-12 10:26:23 -05:00
Ehren Kret
3e8d79e147 Remove unused lua script to delete by sender and timestamp 2021-08-11 17:38:55 -05:00
Ehren Kret
a46045d987 Remove unused methods that delete messages by sender and timestamp 2021-08-11 17:30:39 -05:00
Ehren Kret
662c905b80 Remove deprecated delete messages endpoint
DELETE /v1/messages/{source}/{timestamp} has been deprecated a long
time and has minimal usage each day at this point. Dropping support
for this endpoint to improve message cache storage flexibility.
2021-08-11 16:17:44 -05:00
Chris Eager
31022aeb79 Use refreshing AuthenticatedAccount for @Auth 2021-08-11 14:52:25 -05:00
Chris Eager
b3e6a50dee Send 508 status code for legacy clients that produce rate limit challenges 2021-08-11 11:57:30 -05:00
Chris Eager
d29764d11f Only process updates for enabled devices in PushFeedbackProcessor 2021-08-11 11:54:42 -05:00
Chris Eager
f8e4f6727a Reorder crawler listeners so updates happen after read-only processing 2021-08-11 11:54:20 -05:00
Ehren Kret
63d05df8a3 Fix indentation 2021-08-10 10:02:04 -05:00
Ehren Kret
52d13d1d62 Remove unused lua script 2021-08-10 10:02:04 -05:00
Ehren Kret
f58a320223 Remove unused method from MessagesCache 2021-08-10 10:02:04 -05:00
Chris Eager
3e01bc1174 Add metric for content-length header distribution 2021-08-06 14:41:16 -05:00
Jon Chambers
d1ada7f998 Revert "Continue to verify rate limiters by e164 during UUID migration period"
This reverts commit ce5edbb7fc.
2021-08-06 14:33:59 -05:00
Chris Eager
095fc8140e Increase from default binary message size 2021-08-06 12:56:34 -05:00
Jon Chambers
73c368ea86 Use UUIDs instead of e164s to associate accounts with push notifications. 2021-08-04 14:38:28 -04:00
Jon Chambers
ce5edbb7fc Continue to verify rate limiters by e164 during UUID migration period 2021-08-04 14:15:21 -04:00
Jon Chambers
a680639718 Use UUIDs as rate limiter keys. 2021-08-04 14:15:21 -04:00
Ehren Kret
becf6afbdd Block until all UUID bytes are read or EOF 2021-08-03 17:59:48 -05:00
Ehren Kret
1dda015c6a Update multi-recipient message sending to handle unrestricted destinations 2021-08-03 17:31:39 -05:00
Chris Eager
a0427ecf8c Update s3-upload-maven-plugin to 1.6-SNAPSHOT 2021-08-03 11:04:29 -05:00
Chris Eager
cfd31e98ff Move version comparison to after more meaningful checks 2021-08-03 11:03:41 -05:00
Jon Chambers
bcb89924b4 Simplify optimistic write logic 2021-08-03 11:54:26 -04:00
Ehren Kret
23f9199439 Fix dependency resolution error for commons-logging 2021-08-02 13:14:44 -05:00
Ehren Kret
1f6318a919 Rename constant 2021-08-02 13:14:44 -05:00
Ehren Kret
b0667b258b Implement EnterpriseRecaptchaClient 2021-08-02 13:14:44 -05:00
Ehren Kret
4c3a48f5be Use more specific prefix for recaptcha transition 2021-08-02 13:14:44 -05:00
Ehren Kret
33fb7a72de Use RecaptchaClient interface 2021-08-02 13:14:44 -05:00
Ehren Kret
2c808e369c Create a transitional recaptcha client for upgrading 2021-08-02 13:14:44 -05:00
Ehren Kret
906d0be382 Setup recaptcha client interface 2021-08-02 13:14:44 -05:00
Ehren Kret
1c9a3c6105 Bringing in Google Cloud Recaptcha Enterprise libraries 2021-08-02 13:14:44 -05:00
Ehren Kret
2aaddd721f Rename existing captcha client 2021-08-02 13:14:44 -05:00
Jon Chambers
4e2284b83f Retire old GV2 adoption metrics. 2021-08-02 12:51:49 -05:00
Chris Eager
d5d9978e48 Use non-stale account in mutator when adding a new device 2021-08-02 11:38:03 -05:00
Chris Eager
d45659ac76 Reduce contention when updating device.lastSeen 2021-08-02 11:26:15 -05:00
Jon Chambers
13a07dc6cd Drop the active user counter. 2021-07-29 15:40:27 -04:00
Chris Eager
51b7a8d868 Add excluded E164s configuration to pre-registration experiment 2021-07-29 14:16:40 -05:00
Chris Eager
df9c0051c9 Reconcile inactive and undiscoverable accounts when using v3 endpoints 2021-07-29 10:56:44 -05:00
Jon Chambers
331ff83cd5 Drop legacy PIN-based registration lock plumbing 2021-07-29 11:51:14 -04:00
Jon Chambers
44838d6238 Verify that nobody's addressing API calls by e164 any more. 2021-07-29 11:50:36 -04:00
Chris Eager
5400abb065 Better support unhandled exception logging on websocket requests 2021-07-28 14:06:09 -05:00
Jon Chambers
f47fefb73e Lock accounts for the duration of deletion operations. 2021-07-27 13:12:39 -04:00
Jon Chambers
cdef745a7a Drop a not-very-helpful metric (logging works better in this case). 2021-07-27 13:12:39 -04:00
Jon Chambers
1a1eab4ec0 Also clear profiles on re-registration. 2021-07-27 13:05:54 -04:00
Jon Chambers
3a966ef345 Reuse account UUIDs when registering an account with a recently-deleted e164. 2021-07-27 13:05:54 -04:00
Jon Chambers
be20c04cd8 Identify accounts for which to delete keys by UUID. 2021-07-27 13:05:54 -04:00
Jon Chambers
d09dcc90fe Add methods for getting, clearing, locking recently-deleted account records. 2021-07-27 13:05:54 -04:00
Sophiah Ho
1fd1207bf6 Prevent unit tests from failing for machines with a non-US default Locale 2021-07-27 13:01:48 -04:00
Jon Chambers
0117fc12c7 Actually increment the moved "new user" counter. 2021-07-27 12:09:51 -04:00
Jon Chambers
ef9a7fda9a Publish outstanding SQS operation count as a gauge. 2021-07-27 11:15:41 -04:00
Chris Eager
13447df1e0 Update validation for NotNull items in IncomingMessagesList 2021-07-27 10:39:30 -04:00
Jon Chambers
3608c5bfb0 Wait for outstanding requests to be resolved before shutting down the directory queue. 2021-07-27 10:36:53 -04:00
Jon Chambers
34dbff6786 Switch to an async SQS client. 2021-07-27 10:36:53 -04:00
Jon Chambers
a6066bfc2f Migrate DirectoryQueueTest to JUnit 5. 2021-07-27 10:36:53 -04:00
Jon Chambers
8579190cdf Consolidate account creation/directory updates into AccountsManager 2021-07-27 10:27:47 -04:00
Chris Eager
917f667229 Remove AccountController and KeysController from websocket 2021-07-26 14:27:43 -05:00
Chris Eager
317a551bdb Migrate MetricsRequestEventListenerTest to JUnit 5 2021-07-26 12:06:29 -05:00
Chris Eager
27e9271473 Add request path and user agent to unhandled exception logging 2021-07-26 12:06:29 -05:00
Fedor Indutny
11dff6c546 more controllers 2021-07-26 12:06:17 -05:00
Fedor Indutny
e6712937ca fix indent 2021-07-26 12:06:17 -05:00
Fedor Indutny
cf8887bb5a Provide more WebSocket endpoints 2021-07-26 12:06:17 -05:00
Chris Eager
696340f780 Migrate DeviceControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
86ddcbaa08 Migrate CertificateControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
2144d2a8d8 Migrate AttachmentControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
f7af861b31 Migrate SecureStorageControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
208a09b3ae Migrate RemoteConfigControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
831023e41d Migrate PaymentsControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
ff627793d6 Migrate DirectoryControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
f971c76a99 Migrate StickerControllerTest to JUnit 5 2021-07-26 11:18:17 -05:00
Chris Eager
8f41176c76 Enable "sms" transport for +98 2021-07-26 10:40:05 -05:00
Ehren Kret
31bbbbb5e0 Raise default message TTL to 14 days 2021-07-20 14:08:08 -05:00
Jon Chambers
effcd6038d Also record dimensional metrics for circuit breakers and retries. 2021-07-19 16:56:16 -04:00
Jon Chambers
12be7d49c2 Clear one-time pre-keys on re-registration. 2021-07-19 10:05:01 -04:00
Jon Chambers
14863b575e Clear one-time pre-keys when a device is unlinked. 2021-07-19 10:05:01 -04:00
Jon Chambers
32a95f96ff Add a pessimistic locking system for operations on recently-deleted account records 2021-07-16 16:52:58 -04:00
Jon Chambers
b757d4b334 Measure how many "send message" requests are still using e164-based addressing. 2021-07-16 16:52:58 -04:00
Chris Eager
bd03d910fe Set authenticated device after updating last seen 2021-07-16 16:52:58 -04:00
Chris Eager
01ef855157 Return a non-stale account from base authenticator when last seen is updated 2021-07-16 16:52:58 -04:00
Chris Eager
817866caf3 Use fresh accounts to update in PushFeedbackProcessor 2021-07-16 16:52:58 -04:00
Chris Eager
158d65c6a7 Add optimistic locking to account updates 2021-07-16 16:52:58 -04:00
realturner
62022c7de1 Migrate AppConfig to SDK v2 to detect and use web identify token 2021-07-16 16:48:33 -04:00
Chris Eager
a824b5575d Add dynamic configuration for using DynamoDB in AccountsDatabaseCrawler 2021-07-06 13:01:24 -05:00
Jon Chambers
78819d5382 Remove expiration logic when checking token validity.
The data store will no longer return tokens that have expired, and we no longer need to check for expiration in application space.
2021-07-06 11:03:49 -04:00
Jon Chambers
d128bc782a Retire Postgres-backed pending account/device tables. 2021-07-06 11:03:49 -04:00
Chris Eager
530b2a310f Ensure active future is always completed 2021-07-02 15:05:11 -05:00
Chris Eager
d5b0d99a54 Remove unused method 2021-07-02 15:05:11 -05:00
Chris Eager
43be72d076 Add test for ManagedPeriodicWork; fix shutdown not awaiting active execution 2021-07-02 15:05:11 -05:00
Chris Eager
9558944e22 Add needsReconciliationIndexName to sample.yml 2021-07-02 15:05:11 -05:00
Chris Eager
0f6c866c8d Update imports 2021-07-02 15:05:11 -05:00
Chris Eager
bac78e9291 Switch DeletedAccountsTableCrawler metrics to a basic Metrics#summary 2021-07-02 15:05:11 -05:00
Chris Eager
c22ea78672 Add crawler to process migration retry accounts 2021-07-02 15:05:11 -05:00
Chris Eager
a85afe827d Avoid NPE by using scheduledFuture as the Gauge state object 2021-07-02 15:05:11 -05:00
Chris Eager
abaed821ec Add additional case to unit test 2021-07-02 15:05:11 -05:00
Chris Eager
6fa9dcd954 Refactor to use shared recurringJobExecutor 2021-07-02 15:05:11 -05:00
Chris Eager
819d59cd79 Update reconciliation crawler to use secondary index 2021-07-02 15:05:11 -05:00
Chris Eager
2f88f0eedb Refactor to use single threaded scheduled executor 2021-07-02 15:05:11 -05:00
Chris Eager
74ff491671 Rename ManagedPeriodicWorkCache to ManagedPeriodicWorkLock 2021-07-02 15:05:11 -05:00
Chris Eager
eac48a6617 Don’t delete accounts after reconciling 2021-07-02 15:05:11 -05:00
Chris Eager
19617c14f8 Improved logging in ManagedPeriodcWork 2021-07-02 15:05:11 -05:00
Chris Eager
fc7291c3e8 Migrate DeletedAccountsTableCrawler to ManagedPeriodicWork 2021-07-02 15:05:11 -05:00
Chris Eager
88db808298 Add abstract ManagedPeriodicWork 2021-07-02 15:05:11 -05:00
Chris Eager
5193abdab3 Add DeletedAccountsTableCrawler 2021-07-02 15:05:11 -05:00
Chris Eager
a315c9be92 Add DeletedAccounts DynamoDB table 2021-07-02 15:05:11 -05:00
Chris Eager
fc1541591a Add AbstractDynamoDbStore#scan 2021-07-02 15:05:11 -05:00
Chris Eager
ae97c4db9f Use editorconfig in AbstractDynamoDbStore 2021-07-02 15:05:11 -05:00
Chris Eager
26bc5973b5 Clear message queue before and after removing a device 2021-07-02 10:48:42 -05:00
Chris Eager
e52b8c8423 Implement DatadogConfig in DatadogConfiguration 2021-07-02 10:48:05 -05:00
Jon Chambers
7395489bac Add tests for pending account/device managers. 2021-07-02 11:30:13 -04:00
Jon Chambers
b384ed7f5c Add a counter for requests for delivery certificates with/without e164s. 2021-07-01 10:59:10 -04:00
Jon Chambers
e3afcae7d3 Gather data to verify safety of retiring legacy reglock system. 2021-07-01 10:58:47 -04:00
Jon Chambers
9faeed7b20 Count E164 authentications versus UUID authentications. 2021-07-01 10:51:34 -04:00
Jon Chambers
49adcca80e Use Optional.isEmpty(). 2021-07-01 10:51:34 -04:00
Jon Chambers
49c43a6816 Simplify distribution summary for "days since last seen." 2021-07-01 10:51:34 -04:00
Jon Chambers
84f85ae098 Collapse various account meters into a single, multi-dimensional counter. 2021-07-01 10:51:34 -04:00
Jon Chambers
3d581941ab Add plumbing and configuration to migrate pending accounts/devices to DynamoDB. 2021-07-01 10:50:52 -04:00
Jon Chambers
d2d39baede Add a DynamoDB-backed stored verification code store. 2021-07-01 10:50:52 -04:00
Jon Chambers
111f5ba024 Use java.time classes for stored verification code expiration; add tests. 2021-07-01 10:50:52 -04:00
Jon Chambers
ce3fb7fa99 Extract a common base class for verification code store tests. 2021-07-01 10:50:52 -04:00
Jon Chambers
fc421d3f21 Introduce a common interface for verification code stores. 2021-07-01 10:50:52 -04:00
Jon Chambers
71bea759c6 Consolidate StoredVerificationCode constructors. 2021-07-01 10:50:52 -04:00
Jon Chambers
bf1dd791a5 Drop caching for pending accounts/devices. 2021-07-01 10:50:52 -04:00
Chris Eager
4c99577c08 Add configuration for Datadog batch size 2021-06-30 16:44:25 -05:00
Graeme Connell
5d5c63e6d4 Update profile controller to S3 AWSv2. 2021-06-30 13:09:18 -06:00
Graeme Connell
42ff3f8432 Switch SQS to Amazon SDKv2. 2021-06-30 12:46:12 -06:00
Chris Eager
be6ef76486 Update DynamoDBLocal to 1.16.0 2021-06-23 13:50:58 -05:00
Chris Eager
bc297e6d34 Update wiremock-jre8 to 2.28.1 2021-06-23 13:50:58 -05:00
Chris Eager
3a526dcbd7 Update mockito to 3.11.1 2021-06-23 13:50:58 -05:00
Ehren Kret
7883352b74 Match random capability generation in test 2021-06-21 17:32:31 -05:00
Ehren Kret
982d122d18 Match random capability generation in test 2021-06-21 17:32:31 -05:00
Ehren Kret
d8d94407c6 Create announcement group capability 2021-06-21 17:32:31 -05:00
Chris Eager
28cfc54170 Update FunctionCounter builder to use non-null object and method 2021-06-11 11:27:45 -05:00
Jon Chambers
2ee7279743 Pause nstat counters. 2021-06-11 12:26:56 -04:00
Jon Chambers
eb1b073385 Add a hostname-aware reporter factory. 2021-06-10 14:23:05 -04:00
Jon Chambers
c634185b6f Standardize a utility method for getting local host names. 2021-06-10 14:23:05 -04:00
Ehren Kret
827a3af419 Code cleanup 2021-06-09 20:44:18 -05:00
Jon Chambers
2c33d22a30 Stop recording specific client versions in metrics until we know we need them again. 2021-06-08 12:25:31 -04:00
Chris Eager
b41ed9d810 Update sample.yml config 2021-06-07 17:21:36 -04:00
Jon Chambers
58d3a12eff Set hostname to lowercase to avoid strange case mismatch issues; log hostname failures. 2021-06-07 17:17:46 -04:00
Jon Chambers
88c4b2be97 Correct a misunderstanding about the metrics host tag. 2021-06-07 16:29:44 -04:00
Jon Chambers
6cbd57f19f Include environment/service/version as common metric tags. 2021-06-04 18:17:09 -04:00
Jon Chambers
5522376584 Include a host tag with metrics. 2021-06-04 18:17:09 -04:00
Jon Chambers
5089c37d28 Drop a pair of unused commands. 2021-06-04 12:35:06 -04:00
Jon Chambers
1ccf24e68c Add a command to check dynamic config files. 2021-06-04 12:34:48 -04:00
Jon Chambers
411f7298f2 Enforce validation constraints for dynamic configuration objects. 2021-06-04 12:34:48 -04:00
Jon Chambers
5b0214c6f2 Make pre-key take operations more null-safe 2021-06-04 11:18:59 -04:00
Jon Chambers
735573e61b Make reporting intervals configurable. 2021-06-03 17:50:41 -04:00
Graeme Connell
c545cff1b3 Switch DynamoDB to AWSv2.
Switch from using com.amazonaws.services.dynamodbv2 to using
software.amazon.awssdk.services.dynamodb for all current DynamoDB uses.
2021-06-03 13:37:10 -06:00
Jon Chambers
cbd9681e3e Configure histograms and exclude high-cardinality metrics. 2021-06-03 14:12:02 -04:00
Jon Chambers
ca876e40ca Add a second metric aggregator. 2021-06-03 14:12:02 -04:00
Jon Chambers
76f5a71727 Include server version in logging tags 2021-06-03 11:24:25 -04:00
Jon Chambers
117de2382d Verify that API consumers can skip/clear VOIP tokens. 2021-06-02 16:50:49 -05:00
Jon Chambers
25e7036451 Send a payload with mutable content for non-VOIP topics. 2021-06-02 16:50:49 -05:00
Jon Chambers
3131bd3dd9 Allow iOS callers to specify whether they're providing a VOIP token for preauth. 2021-06-02 16:50:49 -05:00
Chris Eager
1cf9397bbd Bump dropwizard to 2.0.22 2021-06-02 12:30:30 -05:00
brock-signal
c97be15e79 Fix NPE when a null message comes in from a client 2021-06-01 15:00:41 -06:00
Ehren Kret
164fc40990 Rename receipt type and add new client-to-client plaintext type for decryption error receipts 2021-05-28 11:33:44 -05:00
Ehren Kret
6456af6284 Upgrade to latest protobuf
This upgrades to protobuf 3.17 and uses maven to automatically rebuild
the generated code instead of using prefabricated checked in Java
files.
2021-05-28 11:33:44 -05:00
Chris Eager
81212cc13a Add jgitver configuration to ignore branch names 2021-05-27 14:35:28 -05:00
Ehren Kret
6f0750790c Add metric to count number of legacy messages sent 2021-05-27 11:13:42 -05:00
Chris Eager
3e61b5c49d Add call chain and mismatch check for push token timestamp 2021-05-27 11:10:58 -05:00
Ehren Kret
50c4df4f45 Add deploy phase bindings 2021-05-26 19:42:45 -05:00
Ehren Kret
1eb946f5fe Add jgitver extension 2021-05-26 19:42:45 -05:00
Ehren Kret
7bd402b48d Build refactor in preparations for bringing in jgitver 2021-05-26 19:42:42 -05:00
Chris Eager
90444d5b91 Bump version to 5.95 2021-05-26 11:11:00 -05:00
Chris Eager
5ee093f87c Add mismatch for signed pre-key; remove mismatch for migration version 2021-05-26 10:58:23 -05:00
Chris Eager
623743286c Bump version to 5.94 2021-05-25 11:00:44 -05:00
Chris Eager
67067f1d2d Remove last-seen and registration lock comparisons 2021-05-25 10:47:57 -05:00
Ehren Kret
07f9bb112e Use separate object for multi recipient response
`needsSync` was being sent back from the server in the JSON response
which is an unnecessary and constantly false field in multi-recipient
message sending endpoint as it's always sealed sender.
2021-05-25 10:30:39 -05:00
Ehren Kret
417d48c452 Block downgrading sender key support
Disallow linking an additional device to an account that has already
upgraded to having sender key support where the linked device does not
have sender key support. This should prompt the person attempting to
link the older application to upgrade in order to complete the linking
process.
2021-05-25 10:30:26 -05:00
635 changed files with 38477 additions and 33942 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

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

View File

@@ -8,17 +8,11 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
- name: Set up JDK 17
uses: actions/setup-java@3bc31aaf88e8fc94dc1e632d48af61be5ca8721c
with:
distribution: 'adopt'
java-version: 11
- name: Cache local Maven repository
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
distribution: 'temurin'
java-version: 17
cache: 'maven'
- name: Build with Maven
run: mvn -e -B verify

16
.gitignore vendored
View File

@@ -9,12 +9,20 @@ config/production.yml
config/federated.yml
config/staging.yml
config/testing.yml
service/config/production.yml
service/config/federated.yml
service/config/staging.yml
service/config/testing.yml
config/deploy.properties
/service/config/production.yml
/service/config/federated.yml
/service/config/staging.yml
/service/config/testing.yml
/service/config/deploy.properties
/service/dependency-reduced-pom.xml
.opsmanage
put.sh
deployer-staging.properties
deployer-production.properties
deployer.log
/service/src/main/resources/org/signal/badges/Badges_*.properties
!/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

11
.gitmodules vendored Normal file
View File

@@ -0,0 +1,11 @@
# Note that the implementation of the abusive message filter is private; internal
# developers will need to override this URL with:
#
# ```
# git config submodule.abusive-message-filter.url PRIVATE_URL
# ```
#
# External developers may safely ignore this submodule.
[submodule "abusive-message-filter"]
path = abusive-message-filter
url = REDACTED

9
.mvn/extensions.xml Normal file
View File

@@ -0,0 +1,9 @@
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
<extension>
<groupId>fr.brouillard.oss</groupId>
<artifactId>jgitver-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>

14
.mvn/jgitver.config.xml Normal file
View File

@@ -0,0 +1,14 @@
<configuration xmlns="http://jgitver.github.io/maven/configuration/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jgitver.github.io/maven/configuration/1.1.0 https://jgitver.github.io/maven/configuration/jgitver-configuration-v1_1_0.xsd">
<useDirty>true</useDirty>
<useDefaultBranchingPolicy>false</useDefaultBranchingPolicy>
<branchPolicies>
<branchPolicy>
<pattern>(.*)</pattern>
<transformations>
<transformation>IGNORE</transformation>
</transformations>
</branchPolicy>
</branchPolicies>
</configuration>

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

44
LICENSE
View File

@@ -615,3 +615,47 @@ reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

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

View File

@@ -1,50 +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>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gcm-sender-async</artifactId>
<version>${TextSecureServer.version}</version>
<dependencies>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
<version>${resilience4j.version}</version>
</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>
</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%

321
pom.xml
View File

@@ -4,9 +4,6 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<prerequisites>
<maven>3.0.0</maven>
</prerequisites>
<repositories>
<repository>
@@ -24,32 +21,69 @@
</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>redis-dispatch</module>
<module>websocket-resources</module>
<module>gcm-sender-async</module>
<module>service</module>
</modules>
<properties>
<dropwizard.version>2.0.21</dropwizard.version>
<aws.sdk.version>1.11.939</aws.sdk.version>
<aws.sdk2.version>2.16.66</aws.sdk2.version>
<mockito.version>2.25.1</mockito.version>
<micrometer.version>1.5.3</micrometer.version>
<pushy.version>0.14.2</pushy.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.32</dropwizard.version>
<dropwizard-metrics-datadog.version>1.1.13</dropwizard-metrics-datadog.version>
<gson.version>2.9.0</gson.version>
<guava.version>30.1.1-jre</guava.version>
<jackson.version>2.13.3</jackson.version>
<jaxb.version>2.3.1</jaxb.version>
<jedis.version>2.9.0</jedis.version>
<lettuce.version>6.1.9.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.79.Final</netty.version>
<opentest4j.version>1.2.0</opentest4j.version>
<protobuf.version>3.19.4</protobuf.version>
<pushy.version>0.15.1</pushy.version>
<resilience4j.version>1.5.0</resilience4j.version>
<semver4j.version>3.1.0</semver4j.version>
<slf4j.version>1.7.30</slf4j.version>
<stripe.version>21.2.0</stripe.version>
<vavr.version>0.10.4</vavr.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<TextSecureServer.version>5.93</TextSecureServer.version>
</properties>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>TextSecureServer</artifactId>
<version>1.0</version>
<version>JGITVER</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.13.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-dependencies</artifactId>
@@ -57,6 +91,13 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>${netty.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
@@ -71,6 +112,13 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>20.9.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bom</artifactId>
@@ -85,6 +133,156 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
<version>${pushy.version}</version>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy-dropwizard-metrics-listener</artifactId>
<version>${pushy.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
<version>${libphonenumber.version}</version>
</dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>semver4j</artifactId>
<version>${semver4j.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>${lettuce.version}</version>
</dependency>
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>${vavr.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash.logback.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>${commons-csv.version}</version>
</dependency>
<dependency>
<groupId>org.coursera</groupId>
<artifactId>dropwizard-metrics-datadog</artifactId>
<version>${dropwizard-metrics-datadog.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${jaxb.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opentest4j</groupId>
<artifactId>opentest4j</artifactId>
<version>${opentest4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>${stripe.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<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.18.0</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>
@@ -98,7 +296,7 @@
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.27.2</version>
<version>2.33.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
@@ -123,28 +321,77 @@
<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>
</dependencies>
<profiles>
<profile>
<id>include-abusive-message-filter</id>
<activation>
<file>
<exists>abusive-message-filter/pom.xml</exists>
</file>
</activation>
<modules>
<module>abusive-message-filter</module>
</modules>
</profile>
<profile>
<id>exclude-abusive-message-filter</id>
<activation>
<file>
<missing>abusive-message-filter/pom.xml</missing>
</file>
</activation>
</profile>
</profiles>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<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>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
@@ -191,11 +438,39 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<version>3.0.0-M3</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<dependencyConvergence/>
<requireMavenVersion>
<version>3.8.3</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.0.0-M1</version>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.0.0-M1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>

View File

@@ -5,12 +5,10 @@
<parent>
<artifactId>TextSecureServer</artifactId>
<groupId>org.whispersystems.textsecure</groupId>
<version>1.0</version>
<version>JGITVER</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redis-dispatch</artifactId>
<version>${TextSecureServer.version}</version>
<dependencies>
<dependency>

View File

@@ -5,7 +5,7 @@
package org.whispersystems.dispatch;
public interface DispatchChannel {
public void onDispatchMessage(String channel, byte[] message);
public void onDispatchSubscribed(String channel);
public void onDispatchUnsubscribed(String channel);
void onDispatchMessage(String channel, byte[] message);
void onDispatchSubscribed(String channel);
void onDispatchUnsubscribed(String channel);
}

View File

@@ -59,9 +59,7 @@ public class DispatchManager extends Thread {
logger.warn("Subscription error", e);
}
if (previous.isPresent()) {
dispatchUnsubscription(name, previous.get());
}
previous.ifPresent(channel -> dispatchUnsubscription(name, channel));
}
public synchronized void unsubscribe(String name, DispatchChannel channel) {
@@ -132,46 +130,28 @@ public class DispatchManager extends Thread {
}
private void resubscribeAll() {
new Thread() {
@Override
public void run() {
synchronized (DispatchManager.this) {
try {
for (String name : subscriptions.keySet()) {
pubSubConnection.subscribe(name);
}
} catch (IOException e) {
logger.warn("***** RESUBSCRIPTION ERROR *****", e);
new Thread(() -> {
synchronized (DispatchManager.this) {
try {
for (String name : subscriptions.keySet()) {
pubSubConnection.subscribe(name);
}
} catch (IOException e) {
logger.warn("***** RESUBSCRIPTION ERROR *****", e);
}
}
}.start();
}).start();
}
private void dispatchMessage(final String name, final DispatchChannel channel, final byte[] message) {
executor.execute(new Runnable() {
@Override
public void run() {
channel.onDispatchMessage(name, message);
}
});
executor.execute(() -> channel.onDispatchMessage(name, message));
}
private void dispatchSubscription(final String name, final DispatchChannel channel) {
executor.execute(new Runnable() {
@Override
public void run() {
channel.onDispatchSubscribed(name);
}
});
executor.execute(() -> channel.onDispatchSubscribed(name));
}
private void dispatchUnsubscription(final String name, final DispatchChannel channel) {
executor.execute(new Runnable() {
@Override
public void run() {
channel.onDispatchUnsubscribed(name);
}
});
executor.execute(() -> channel.onDispatchUnsubscribed(name));
}
}

View File

@@ -20,7 +20,7 @@ public class RedisInputStream {
}
public String readLine() throws IOException {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
boolean foundCr = false;
@@ -31,14 +31,14 @@ public class RedisInputStream {
throw new IOException("Stream closed!");
}
boas.write(character);
baos.write(character);
if (foundCr && character == LF) break;
else if (character == CR) foundCr = true;
else if (foundCr) foundCr = false;
}
byte[] data = boas.toByteArray();
byte[] data = baos.toByteArray();
return new String(data, 0, data.length-2);
}

View File

@@ -8,6 +8,6 @@ import org.whispersystems.dispatch.redis.PubSubConnection;
public interface RedisPubSubConnectionFactory {
public PubSubConnection connect();
PubSubConnection connect();
}

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

@@ -1,6 +1,6 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>bin</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
@@ -18,8 +18,8 @@
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>${parent.artifactId}-${TextSecureServer.version}.jar</include>
<include>${parent.artifactId}-${project.version}.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>
</assembly>

View File

@@ -1,114 +1,295 @@
# Example, relatively minimal, configuration that passes validation (see `io.dropwizard.cli.CheckCommand`)
#
# `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.
stripe:
apiKey: unset
idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
boostDescription: >
Example
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:
accountToken:
nanpaMessagingServiceSid: # Twilio SID for the messaging service to use for NANPA.
messagingServiceSid: # Twilio SID for the message service to use for non-NANPA.
verifyServiceSid: # Twilio SID for a Verify service
localDomain: # Domain Twilio can connect back to for calls. Should be domain of your service.
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: # Text to use for the verification message on iOS. Will be passed to String.format with the verification code as argument 1.
androidNg: # 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: # 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: # 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: # Text to use when the client type is unrecognized. Will be passed to String.format with the verification code as argument 1.
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:
# … all keys from defaultClientVerificationTexts are required
androidAppHash: # Hash appended to Android
verifyServiceFriendlyName: # Service name used in template. Requires Twilio account rep to enable
push:
queueSize: # Size of push pending queue
redphone:
authKey: # Deprecated
turn: # TURN server configuration
secret: # TURN server secret
uris:
- stun:yourdomain:80
- stun:yourdomain.com:443
- turn:yourdomain:443?transport=udp
- turn:etc.com:80?transport=udp
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
cacheCluster: # Redis server configuration for cache cluster
urls:
configurationUri: redis://redis.example.com:6379/
clientPresenceCluster: # Redis server configuration for client presence cluster
configurationUri: redis://redis.example.com:6379/
pubsub: # Redis server configuration for pubsub cluster
url: redis://redis.example.com:6379/
replicaUrls:
- redis://redis.example.com:6379/
pushSchedulerCluster: # Redis server configuration for push scheduler cluster
configurationUri: redis://redis.example.com:6379/
rateLimitersCluster: # Redis server configuration for rate limiters cluster
configurationUri: redis://redis.example.com:6379/
directory:
client: # Configuration for interfacing with Contact Discovery Service cluster
userAuthenticationTokenSharedSecret: # hex-encoded secret shared with CDS used to generate auth tokens for Signal users
userAuthenticationTokenUserIdSecret: # hex-encoded secret shared among Signal-Servers to obscure user phone numbers from CDS
userAuthenticationTokenSharedSecret: 00000f # hex-encoded secret shared with CDS used to generate auth tokens for Signal users
userAuthenticationTokenUserIdSecret: 00000f # hex-encoded secret shared among Signal-Servers to obscure user phone numbers from CDS
sqs:
accessKey: # AWS SQS accessKey
accessSecret: # AWS SQS accessSecret
queueUrl: # AWS SQS queue url
server:
replicationUrl: # CDS replication endpoint base url
replicationPassword: # CDS replication endpoint password
replicationCaCertificate: # CDS replication endpoint TLS certificate trust root
reconciliationChunkSize: # CDS reconciliation chunk size
reconciliationChunkIntervalMs: # CDS reconciliation chunk interval, in milliseconds
accessKey: test # AWS SQS accessKey
accessSecret: test # AWS SQS accessSecret
queueUrls: # AWS SQS queue urls
- https://sqs.example.com/directory.fifo
server: # One or more CDS servers
- 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-----
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:
persistDelayMinutes: 1
cluster:
urls:
- redis://redis.example.com:6379/
messageStore: # Postgresql database configuration for message store
driverClass: org.postgresql.Driver
user:
password:
url:
configurationUri: redis://redis.example.com:6379/
metricsCluster:
urls:
- redis://redis.example.com:6379/
configurationUri: redis://redis.example.com:6379/
awsAttachments: # AWS S3 configuration
accessKey:
accessSecret:
bucket:
region:
accessKey: test
accessSecret: test
bucket: aws-attachments
region: us-west-2
gcpAttachments: # GCP Storage configuration
domain:
email:
maxSizeInBytes:
domain: example.com
email: user@example.cocm
maxSizeInBytes: 1024
pathPrefix:
rsaSigningKey:
rsaSigningKey: |
-----BEGIN PRIVATE KEY-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAA
-----END PRIVATE KEY-----
profiles: # AWS S3 configuration
accessKey:
accessSecret:
bucket:
region:
database: # Postgresql database configuration
driverClass: org.postgresql.Driver
user:
password:
url:
accountDatabaseCrawler:
chunkSize: 10 # accounts per run
chunkIntervalMs: 60000 # time per run
apn: # Apple Push Notifications configuration
sandbox: true
bundleId:
keyId:
teamId:
signingKey:
bundleId: com.example.textsecuregcm
keyId: unset
teamId: unset
signingKey: |
-----BEGIN PRIVATE KEY-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAA
-----END PRIVATE KEY-----
gcm: # GCM Configuration
senderId:
apiKey:
fcm: # FCM configuration
credentials: |
{ "json": true }
micrometer: # Micrometer metrics config
- name: "example"
- uri: "https://metrics.example.com/"
- apiKey:
- accountId:
cdn:
accessKey: test # AWS Access Key ID
accessSecret: test # AWS Access Secret
bucket: cdn # S3 Bucket name
region: us-west-2 # AWS region
datadog:
apiKey: unset
environment: dev
unidentifiedDelivery:
certificate: ABCD1234
privateKey: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
expiresDays: 7
voiceVerification:
url: https://cdn-ca.signal.org/verification/
locales:
- en
recaptcha:
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-----
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-----
zkConfig:
serverPublic: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
appConfig:
application: example
environment: example
configuration: example
remoteConfig:
authorizedTokens:
@@ -117,23 +298,72 @@ remoteConfig:
- # ...
- # Nth authorized token
globalConfig: # keys and values that are given to clients on GET /v1/config
EXAMPLE_KEY: VALUE
paymentService:
userAuthenticationTokenSharedSecret: # hex-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
paymentsService:
userAuthenticationTokenSharedSecret: 0000000f0000000f0000000f0000000f0000000f0000000f0000000f0000000f # hex-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
fixerApiKey: unset
paymentCurrencies:
# list of symbols for supported currencies
- MOB
donation:
uri: # value
apiKey: # value
uri: donation.example.com # value
supportedCurrencies:
- # 1st supported currency
- # 2nd supported currency
- # ...
- # Nth supported currency
circuitBreaker:
failureRateThreshold: # value
ringBufferSizeInHalfOpenState: # value
ringBufferSizeInClosedState: # value
waitDurationInOpenStateInSeconds: # value
retry:
maxAttempts: # value
waitDuration: # value
badges:
badges:
- id: TEST
category: other
sprites: # exactly 6
- sprite-1.png
- sprite-2.png
- sprite-3.png
- sprite-4.png
- sprite-5.png
- sprite-6.png
svg: example.svg
svgs:
- light: example-light.svg
dark: example-dark.svg
badgeIdsEnabledForAll:
- TEST
receiptLevels:
'1': TEST
subscription: # configuration for Stripe subscriptions
badgeGracePeriod: P15D
levels:
500:
badge: EXAMPLE
prices:
# list of ISO 4217 currency codes and amounts for the given badge level
xts:
amount: '10'
id: price_example # stripe ID
boost:
level: 1
expiration: P90D
badge: EXAMPLE
currencies:
# ISO 4217 currency codes and amounts in those currencies
xts:
- '1'
- '2'
- '4'
- '8'
- '20'
- '40'
gift:
level: 10
expiration: P90D
badge: EXAMPLE
currencies:
# ISO 4217 currency codes and amounts in those currencies
xts: '2'

View File

@@ -5,16 +5,10 @@
<parent>
<artifactId>TextSecureServer</artifactId>
<groupId>org.whispersystems.textsecure</groupId>
<version>1.0</version>
<version>JGITVER</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service</artifactId>
<version>${TextSecureServer.version}</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
@@ -33,38 +27,22 @@
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>redis-dispatch</artifactId>
<version>${TextSecureServer.version}</version>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>websocket-resources</artifactId>
<version>${TextSecureServer.version}</version>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>gcm-sender-async</artifactId>
<version>${TextSecureServer.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>zkgroup-java</artifactId>
<version>0.7.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>
@@ -134,27 +112,12 @@
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</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>
@@ -178,14 +141,18 @@
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -204,17 +171,43 @@
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.10.2</version>
<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>
@@ -225,12 +218,6 @@
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
@@ -243,9 +230,12 @@
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-wavefront</artifactId>
<artifactId>micrometer-registry-datadog</artifactId>
</dependency>
<dependency>
<groupId>org.coursera</groupId>
<artifactId>dropwizard-metrics-datadog</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
@@ -271,10 +261,30 @@
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sqs</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>appconfig</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>appconfigdata</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
@@ -285,90 +295,53 @@
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-sqs</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-appconfig</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.whispersystems</groupId>
<artifactId>curve25519-java</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
<version>${pushy.version}</version>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy-dropwizard-metrics-listener</artifactId>
<version>${pushy.version}</version>
<artifactId>dynamodb-lock-client</artifactId>
<version>1.1.0</version>
<exclusions>
<exclusion>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>2.0.34.Final</version>
<scope>runtime</scope>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy-dropwizard-metrics-listener</artifactId>
</dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>semver4j</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
<version>8.12.21</version>
</dependency>
<dependency>
@@ -380,6 +353,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>
@@ -397,13 +376,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>
@@ -414,29 +386,22 @@
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.16.RELEASE</version>
<version>3.3.22.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>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
@@ -450,81 +415,174 @@
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>1.13.6</version>
<version>1.17.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.1</version>
<scope>test</scope>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-recaptchaenterprise</artifactId>
</dependency>
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>exclude-abusive-message-filter</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.whispersystems.textsecuregcm.WhisperServerService</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>read-deploy-configuration</id>
<phase>deploy</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>${project.basedir}/config/deploy.properties</files>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.signal</groupId>
<artifactId>s3-upload-maven-plugin</artifactId>
<version>1.6-SNAPSHOT</version>
<configuration>
<source>${project.build.directory}/${project.build.finalName}-bin.tar.gz</source>
<bucketName>${deploy.bucketName}</bucketName>
<region>${deploy.bucketRegion}</region>
<destination>${project.build.finalName}-bin.tar.gz</destination>
</configuration>
<executions>
<execution>
<id>deploy-to-s3</id>
<phase>deploy</phase>
<goals>
<goal>s3-upload</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<finalName>${parent.artifactId}-${TextSecureServer.version}</finalName>
<finalName>${project.parent.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.6</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>package</phase>
<id>filter-src</id>
<goals>
<goal>shade</goal>
<goal>filter-sources</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.whispersystems.textsecuregcm.WhisperServerService</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
</configuration>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>check-all-service-config</id>
<phase>verify</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>org.whispersystems.textsecuregcm.CheckServiceConfigurations</mainClass>
<classpathScope>test</classpathScope>
<arguments>
<argument>${project.basedir}/config</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,3 +0,0 @@
all:
protoc --java_out=../src/main/java/ TextSecure.proto PubSubMessage.proto

View File

@@ -0,0 +1,15 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm;
public class WhisperServerVersion {
private static final String VERSION = "${project.version}";
public static String getServerVersion() {
return VERSION;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.i18n;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
public class HeaderControlledResourceBundleLookup {
private static final int MAX_LOCALES = 15;
private final ResourceBundleFactory resourceBundleFactory;
public HeaderControlledResourceBundleLookup() {
this(ResourceBundle::getBundle);
}
@VisibleForTesting
public HeaderControlledResourceBundleLookup(
@Nonnull final ResourceBundleFactory resourceBundleFactory) {
this.resourceBundleFactory = Objects.requireNonNull(resourceBundleFactory);
}
@Nonnull
private List<Locale> getAcceptableLocales(final List<Locale> acceptableLanguages) {
return acceptableLanguages.stream().limit(MAX_LOCALES).distinct().collect(Collectors.toList());
}
@Nonnull
public ResourceBundle getResourceBundle(final String baseName, final List<Locale> acceptableLocales) {
final List<Locale> deduplicatedLocales = getAcceptableLocales(acceptableLocales);
final Locale desiredLocale = deduplicatedLocales.isEmpty() ? Locale.getDefault() : deduplicatedLocales.get(0);
// define a control with a fallback order as specified in the header
Control control = new Control() {
@Override
public List<String> getFormats(final String baseName) {
Objects.requireNonNull(baseName);
return Control.FORMAT_PROPERTIES;
}
@Override
public Locale getFallbackLocale(final String baseName, final Locale locale) {
Objects.requireNonNull(baseName);
if (locale.equals(Locale.getDefault())) {
return null;
}
final int localeIndex = deduplicatedLocales.indexOf(locale);
if (localeIndex < 0 || localeIndex >= deduplicatedLocales.size() - 1) {
return Locale.getDefault();
}
// [0, deduplicatedLocales.size() - 2] is now the possible range for localeIndex
return deduplicatedLocales.get(localeIndex + 1);
}
};
return resourceBundleFactory.createBundle(baseName, desiredLocale, control);
}
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.i18n;
import java.util.Locale;
import java.util.ResourceBundle;
public interface ResourceBundleFactory {
ResourceBundle createBundle(String baseName, Locale locale, ResourceBundle.Control control);
}

View File

@@ -1,49 +1,51 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
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;
import java.util.Map;
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.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.DirectoryConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
import org.whispersystems.textsecuregcm.configuration.DonationConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
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.MicrometerConfiguration;
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.RedisClusterConfiguration;
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
import org.whispersystems.textsecuregcm.configuration.TestDeviceConfiguration;
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
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;
@@ -54,12 +56,22 @@ public class WhisperServerConfiguration extends Configuration {
@NotNull
@Valid
@JsonProperty
private TwilioConfiguration twilio;
private StripeConfiguration stripe;
@NotNull
@Valid
@JsonProperty
private PushConfiguration push;
private DynamoDbClientConfiguration dynamoDbClientConfiguration;
@NotNull
@Valid
@JsonProperty
private DynamoDbTables dynamoDbTables;
@NotNull
@Valid
@JsonProperty
private TwilioConfiguration twilio;
@NotNull
@Valid
@@ -79,7 +91,7 @@ public class WhisperServerConfiguration extends Configuration {
@NotNull
@Valid
@JsonProperty
private MicrometerConfiguration micrometer;
private DatadogConfiguration datadog;
@NotNull
@Valid
@@ -101,6 +113,11 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private DirectoryConfiguration directory;
@NotNull
@Valid
@JsonProperty
private DirectoryV2Configuration directoryV2;
@NotNull
@Valid
@JsonProperty
@@ -126,46 +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 migrationDeletedAccountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration migrationRetryAccountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration pushChallengeDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration reportMessageDynamoDb;
@Valid
@NotNull
@JsonProperty
private DatabaseConfiguration abuseDatabase;
@Valid
@NotNull
@JsonProperty
@@ -176,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
@@ -199,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
@@ -256,22 +218,56 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private AppConfigConfiguration appConfig;
@Valid
@NotNull
@JsonProperty
private MonitoredS3ObjectConfiguration torExitNodeList;
@Valid
@NotNull
@JsonProperty
private MonitoredS3ObjectConfiguration asnTable;
@Valid
@NotNull
@JsonProperty
private DonationConfiguration donation;
private Map<String, String> transparentDataIndex = new HashMap<>();
@Valid
@NotNull
@JsonProperty
private BadgesConfiguration badges;
@Valid
@JsonProperty
@NotNull
private SubscriptionConfiguration subscription;
@Valid
@JsonProperty
@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;
public StripeConfiguration getStripe() {
return stripe;
}
public DynamoDbClientConfiguration getDynamoDbClientConfiguration() {
return dynamoDbClientConfiguration;
}
public DynamoDbTables getDynamoDbTables() {
return dynamoDbTables;
}
public RecaptchaConfiguration getRecaptchaConfiguration() {
return recaptcha;
@@ -289,14 +285,6 @@ public class WhisperServerConfiguration extends Configuration {
return twilio;
}
public PushConfiguration getPushConfiguration() {
return push;
}
public JerseyClientConfiguration getJerseyClientConfiguration() {
return httpClient;
}
public AwsAttachmentsConfiguration getAwsAttachmentsConfiguration() {
return awsAttachments;
}
@@ -321,6 +309,10 @@ public class WhisperServerConfiguration extends Configuration {
return directory;
}
public DirectoryV2Configuration getDirectoryV2Configuration() {
return directoryV2;
}
public SecureStorageServiceConfiguration getSecureStorageServiceConfiguration() {
return storageService;
}
@@ -345,44 +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 getMigrationDeletedAccountsDynamoDbConfiguration() {
return migrationDeletedAccountsDynamoDb;
}
public DynamoDbConfiguration getMigrationRetryAccountsDynamoDbConfiguration() {
return migrationRetryAccountsDynamoDb;
}
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() {
@@ -393,8 +353,8 @@ public class WhisperServerConfiguration extends Configuration {
return cdn;
}
public MicrometerConfiguration getMicrometerConfiguration() {
return micrometer;
public DatadogConfiguration getDatadogConfiguration() {
return datadog;
}
public UnidentifiedDeliveryConfiguration getDeliveryCertificate() {
@@ -423,10 +383,6 @@ public class WhisperServerConfiguration extends Configuration {
return results;
}
public Map<String, String> getTransparentDataIndex() {
return transparentDataIndex;
}
public SecureBackupServiceConfiguration getSecureBackupServiceConfiguration() {
return backupService;
}
@@ -447,23 +403,35 @@ public class WhisperServerConfiguration extends Configuration {
return appConfig;
}
public DynamoDbConfiguration getPushChallengeDynamoDbConfiguration() {
return pushChallengeDynamoDb;
}
public DynamoDbConfiguration getReportMessageDynamoDbConfiguration() {
return reportMessageDynamoDb;
}
public MonitoredS3ObjectConfiguration getTorExitNodeListConfiguration() {
return torExitNodeList;
}
public MonitoredS3ObjectConfiguration getAsnTableConfiguration() {
return asnTable;
}
public DonationConfiguration getDonationConfiguration() {
return donation;
}
public BadgesConfiguration getBadges() {
return badges;
}
public SubscriptionConfiguration getSubscription() {
return subscription;
}
public BoostConfiguration getBoost() {
return boost;
}
public GiftConfiguration getGift() {
return gift;
}
public ReportMessageConfiguration getReportMessageConfiguration() {
return reportMessage;
}
public AbusiveMessageFilterConfiguration getAbusiveMessageFilterConfiguration() {
return abusiveMessageFilter;
}
public UsernameConfiguration getUsername() {
return username;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.abuse;
import io.dropwizard.lifecycle.Managed;
import javax.ws.rs.container.ContainerRequestFilter;
import java.io.IOException;
/**
* An abusive message filter is a {@link ContainerRequestFilter} that filters requests to message-sending endpoints to
* detect and respond to patterns of abusive behavior.
* <p/>
* Abusive message filters are managed components that are generally loaded dynamically via a
* {@link java.util.ServiceLoader}. Their {@link #configure(String)} method will be called prior to be adding to the
* server's pool of {@link Managed} objects.
* <p/>
* Abusive message filters must be annotated with {@link FilterAbusiveMessages}, a name binding annotation that
* restricts the endpoints to which the filter may apply.
*/
public interface AbusiveMessageFilter extends ContainerRequestFilter, Managed {
/**
* Configures this abusive message filter. This method will be called before the filter is added to the server's pool
* of managed objects and before the server processes any requests.
*
* @param environmentName the name of the environment in which this filter is running (e.g. "staging" or "production")
* @throws IOException if the filter could not read its configuration source for any reason
*/
void configure(String environmentName) throws IOException;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.abuse;
import javax.ws.rs.NameBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* A name-binding annotation that associates {@link AbusiveMessageFilter}s with resource methods.
*/
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface FilterAbusiveMessages {
}

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

@@ -0,0 +1,16 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
public interface AccountAndAuthenticatedDeviceHolder {
Account getAccount();
Device getAuthenticatedDevice();
}

View File

@@ -1,39 +1,24 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import io.micrometer.core.instrument.Metrics;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import java.util.Optional;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Optional;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import static com.codahale.metrics.MetricRegistry.name;
public class AccountAuthenticator extends BaseAccountAuthenticator implements Authenticator<BasicCredentials, Account> {
private static final String AUTHENTICATION_COUNTER_NAME = name(AccountAuthenticator.class, "authenticate");
private static final String GV2_CAPABLE_TAG_NAME = "gv1Migration";
public class AccountAuthenticator extends BaseAccountAuthenticator implements
Authenticator<BasicCredentials, AuthenticatedAccount> {
public AccountAuthenticator(AccountsManager accountsManager) {
super(accountsManager);
}
@Override
public Optional<Account> authenticate(BasicCredentials basicCredentials) {
final Optional<Account> maybeAccount = super.authenticate(basicCredentials, true);
// TODO Remove this temporary counter when we can replace it with more generic feature adoption system
maybeAccount.ifPresent(account -> {
Metrics.counter(AUTHENTICATION_COUNTER_NAME, GV2_CAPABLE_TAG_NAME, String.valueOf(account.isGv1MigrationSupported())).increment();
});
return maybeAccount;
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials) {
return super.authenticate(basicCredentials, true);
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.util.UUID;
public class AmbiguousIdentifier {
private final UUID uuid;
private final String number;
public AmbiguousIdentifier(String target) {
if (target.startsWith("+")) {
this.uuid = null;
this.number = target;
} else {
this.uuid = UUID.fromString(target);
this.number = null;
}
}
public UUID getUuid() {
return uuid;
}
public String getNumber() {
return number;
}
public boolean hasUuid() {
return uuid != null;
}
public boolean hasNumber() {
return number != null;
}
@Override
public String toString() {
return hasUuid() ? uuid.toString() : number;
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import com.google.common.annotations.VisibleForTesting;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Pair;
/**
* This {@link WebsocketRefreshRequirementProvider} observes intra-request changes in {@link Account#isEnabled()} and
* {@link Device#isEnabled()}.
* <p>
* If a change in {@link Account#isEnabled()} or any associated {@link Device#isEnabled()} is observed, then any active
* WebSocket connections for the account must be closed in order for clients to get a refreshed
* {@link io.dropwizard.auth.Auth} object with a current device list.
*
* @see AuthenticatedAccount
* @see DisabledPermittedAuthenticatedAccount
*/
public class AuthEnablementRefreshRequirementProvider implements WebsocketRefreshRequirementProvider {
private final AccountsManager accountsManager;
private static final Logger logger = LoggerFactory.getLogger(AuthEnablementRefreshRequirementProvider.class);
private static final String ACCOUNT_UUID = AuthEnablementRefreshRequirementProvider.class.getName() + ".accountUuid";
private static final String DEVICES_ENABLED = AuthEnablementRefreshRequirementProvider.class.getName() + ".devicesEnabled";
public AuthEnablementRefreshRequirementProvider(final AccountsManager accountsManager) {
this.accountsManager = accountsManager;
}
@VisibleForTesting
static Map<Long, Boolean> buildDevicesEnabledMap(final Account account) {
return account.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::isEnabled));
}
@Override
public void handleRequestFiltered(final RequestEvent requestEvent) {
if (requestEvent.getUriInfo().getMatchedResourceMethod().getInvocable().getHandlingMethod().getAnnotation(ChangesDeviceEnabledState.class) != null) {
// The authenticated principal, if any, will be available after filters have run.
// Now that the account is known, capture a snapshot of `isEnabled` for the account's devices before carrying out
// the requests business logic.
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest()).ifPresent(account ->
setAccount(requestEvent.getContainerRequest(), account));
}
}
public static void setAccount(final ContainerRequest containerRequest, final Account account) {
containerRequest.setProperty(ACCOUNT_UUID, account.getUuid());
containerRequest.setProperty(DEVICES_ENABLED, buildDevicesEnabledMap(account));
}
@Override
public List<Pair<UUID, Long>> handleRequestFinished(final RequestEvent requestEvent) {
// Now that the request is finished, check whether `isEnabled` changed for any of the devices. If the value did
// change or if a devices was added or removed, all devices must disconnect and reauthenticate.
if (requestEvent.getContainerRequest().getProperty(DEVICES_ENABLED) != null) {
@SuppressWarnings("unchecked") final Map<Long, Boolean> initialDevicesEnabled =
(Map<Long, Boolean>) requestEvent.getContainerRequest().getProperty(DEVICES_ENABLED);
return accountsManager.getByAccountIdentifier((UUID) requestEvent.getContainerRequest().getProperty(ACCOUNT_UUID)).map(account -> {
final Set<Long> deviceIdsToDisplace;
final Map<Long, Boolean> currentDevicesEnabled = buildDevicesEnabledMap(account);
if (!initialDevicesEnabled.equals(currentDevicesEnabled)) {
deviceIdsToDisplace = new HashSet<>(initialDevicesEnabled.keySet());
deviceIdsToDisplace.addAll(currentDevicesEnabled.keySet());
} else {
deviceIdsToDisplace = Collections.emptySet();
}
return deviceIdsToDisplace.stream()
.map(deviceId -> new Pair<>(account.getUuid(), deviceId))
.collect(Collectors.toList());
}).orElseGet(() -> {
logger.error("Request had account, but it is no longer present");
return Collections.emptyList();
});
} else
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.security.Principal;
import java.util.function.Supplier;
import javax.security.auth.Subject;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Pair;
public class AuthenticatedAccount implements Principal, AccountAndAuthenticatedDeviceHolder {
private final Supplier<Pair<Account, Device>> accountAndDevice;
public AuthenticatedAccount(final Supplier<Pair<Account, Device>> accountAndDevice) {
this.accountAndDevice = accountAndDevice;
}
@Override
public Account getAccount() {
return accountAndDevice.get().first();
}
@Override
public Device getAuthenticatedDevice() {
return accountAndDevice.get().second();
}
// Principal implementation
@Override
public String getName() {
return null;
}
@Override
public boolean implies(final Subject subject) {
return false;
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.util.Util;
import java.io.IOException;
import java.util.Base64;
public class AuthorizationHeader {
private final AmbiguousIdentifier identifier;
private final long deviceId;
private final String password;
private AuthorizationHeader(AmbiguousIdentifier identifier, long deviceId, String password) {
this.identifier = identifier;
this.deviceId = deviceId;
this.password = password;
}
public static AuthorizationHeader fromUserAndPassword(String user, String password) throws InvalidAuthorizationHeaderException {
try {
String[] numberAndId = user.split("\\.");
return new AuthorizationHeader(new AmbiguousIdentifier(numberAndId[0]),
numberAndId.length > 1 ? Long.parseLong(numberAndId[1]) : 1,
password);
} catch (NumberFormatException nfe) {
throw new InvalidAuthorizationHeaderException(nfe);
}
}
public static AuthorizationHeader fromFullHeader(String header) throws InvalidAuthorizationHeaderException {
try {
if (header == null) {
throw new InvalidAuthorizationHeaderException("Null header");
}
String[] headerParts = header.split(" ");
if (headerParts == null || headerParts.length < 2) {
throw new InvalidAuthorizationHeaderException("Invalid authorization header: " + header);
}
if (!"Basic".equals(headerParts[0])) {
throw new InvalidAuthorizationHeaderException("Unsupported authorization method: " + headerParts[0]);
}
String concatenatedValues = new String(Base64.getDecoder().decode(headerParts[1]));
if (Util.isEmpty(concatenatedValues)) {
throw new InvalidAuthorizationHeaderException("Bad decoded value: " + concatenatedValues);
}
String[] credentialParts = concatenatedValues.split(":");
if (credentialParts == null || credentialParts.length < 2) {
throw new InvalidAuthorizationHeaderException("Badly formated credentials: " + concatenatedValues);
}
return fromUserAndPassword(credentialParts[0], credentialParts[1]);
} catch (IllegalArgumentException e) {
throw new InvalidAuthorizationHeaderException(e);
}
}
public AmbiguousIdentifier getIdentifier() {
return identifier;
}
public long getDeviceId() {
return deviceId;
}
public String getPassword() {
return password;
}
}

View File

@@ -1,49 +1,39 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import static com.codahale.metrics.MetricRegistry.name;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.basic.BasicCredentials;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Metrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.Util;
import io.micrometer.core.instrument.Tags;
import java.time.Clock;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
import static com.codahale.metrics.MetricRegistry.name;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.RefreshingAccountAndDeviceSupplier;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.Util;
public class BaseAccountAuthenticator {
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
private final Meter authenticationFailedMeter = metricRegistry.meter(name(getClass(), "authentication", "failed" ));
private final Meter authenticationSucceededMeter = metricRegistry.meter(name(getClass(), "authentication", "succeeded" ));
private final Meter noSuchAccountMeter = metricRegistry.meter(name(getClass(), "authentication", "noSuchAccount" ));
private final Meter noSuchDeviceMeter = metricRegistry.meter(name(getClass(), "authentication", "noSuchDevice" ));
private final Meter accountDisabledMeter = metricRegistry.meter(name(getClass(), "authentication", "accountDisabled"));
private final Meter deviceDisabledMeter = metricRegistry.meter(name(getClass(), "authentication", "deviceDisabled" ));
private final Meter invalidAuthHeaderMeter = metricRegistry.meter(name(getClass(), "authentication", "invalidHeader" ));
private final String daysSinceLastSeenDistributionName = name(getClass(), "authentication", "daysSinceLastSeen");
private static final String AUTHENTICATION_COUNTER_NAME = name(BaseAccountAuthenticator.class, "authentication");
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 DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME = name(BaseAccountAuthenticator.class, "daysSinceLastSeen");
private static final String IS_PRIMARY_DEVICE_TAG = "isPrimary";
private final Logger logger = LoggerFactory.getLogger(BaseAccountAuthenticator.class);
private final AccountsManager accountsManager;
private final Clock clock;
@@ -57,65 +47,100 @@ public class BaseAccountAuthenticator {
this.clock = clock;
}
public Optional<Account> authenticate(BasicCredentials basicCredentials, boolean enabledRequired) {
try {
AuthorizationHeader authorizationHeader = AuthorizationHeader.fromUserAndPassword(basicCredentials.getUsername(), basicCredentials.getPassword());
Optional<Account> account = accountsManager.get(authorizationHeader.getIdentifier());
static Pair<String, Long> getIdentifierAndDeviceId(final String basicUsername) {
final String identifier;
final long deviceId;
if (!account.isPresent()) {
noSuchAccountMeter.mark();
final int deviceIdSeparatorIndex = basicUsername.indexOf('.');
if (deviceIdSeparatorIndex == -1) {
identifier = basicUsername;
deviceId = Device.MASTER_ID;
} else {
identifier = basicUsername.substring(0, deviceIdSeparatorIndex);
deviceId = Long.parseLong(basicUsername.substring(deviceIdSeparatorIndex + 1));
}
return new Pair<>(identifier, deviceId);
}
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials, boolean enabledRequired) {
boolean succeeded = false;
String failureReason = null;
try {
final UUID accountUuid;
final long deviceId;
{
final Pair<String, Long> identifierAndDeviceId = getIdentifierAndDeviceId(basicCredentials.getUsername());
accountUuid = UUID.fromString(identifierAndDeviceId.first());
deviceId = identifierAndDeviceId.second();
}
Optional<Account> account = accountsManager.getByAccountIdentifier(accountUuid);
if (account.isEmpty()) {
failureReason = "noSuchAccount";
return Optional.empty();
}
Optional<Device> device = account.get().getDevice(authorizationHeader.getDeviceId());
Optional<Device> device = account.get().getDevice(deviceId);
if (!device.isPresent()) {
noSuchDeviceMeter.mark();
if (device.isEmpty()) {
failureReason = "noSuchDevice";
return Optional.empty();
}
if (enabledRequired) {
if (!device.get().isEnabled()) {
deviceDisabledMeter.mark();
failureReason = "deviceDisabled";
return Optional.empty();
}
if (!account.get().isEnabled()) {
accountDisabledMeter.mark();
failureReason = "accountDisabled";
return Optional.empty();
}
}
if (device.get().getAuthenticationCredentials().verify(basicCredentials.getPassword())) {
authenticationSucceededMeter.mark();
account.get().setAuthenticatedDevice(device.get());
updateLastSeen(account.get(), device.get());
return account;
succeeded = true;
final Account authenticatedAccount = updateLastSeen(account.get(), device.get());
return Optional.of(new AuthenticatedAccount(
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
}
authenticationFailedMeter.mark();
return Optional.empty();
} catch (IllegalArgumentException | InvalidAuthorizationHeaderException iae) {
invalidAuthHeaderMeter.mark();
failureReason = "invalidHeader";
return Optional.empty();
} finally {
Tags tags = Tags.of(
AUTHENTICATION_SUCCEEDED_TAG_NAME, String.valueOf(succeeded),
AUTHENTICATION_ENABLED_REQUIRED_TAG_NAME, String.valueOf(enabledRequired));
if (StringUtils.isNotBlank(failureReason)) {
tags = tags.and(AUTHENTICATION_FAILURE_REASON_TAG_NAME, failureReason);
}
Metrics.counter(AUTHENTICATION_COUNTER_NAME, tags).increment();
}
}
@VisibleForTesting
public void updateLastSeen(Account account, Device device) {
public Account updateLastSeen(Account account, Device device) {
final long lastSeenOffsetSeconds = Math.abs(account.getUuid().getLeastSignificantBits()) % ChronoUnit.DAYS.getDuration().toSeconds();
final long todayInMillisWithOffset = Util.todayInMillisGivenOffsetFromNow(clock, Duration.ofSeconds(lastSeenOffsetSeconds).negated());
if (device.getLastSeen() < todayInMillisWithOffset) {
DistributionSummary.builder(daysSinceLastSeenDistributionName)
.tags(IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isMaster()))
.publishPercentileHistogram()
.register(Metrics.globalRegistry)
Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isMaster()))
.record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays());
device.setLastSeen(Util.todayInMillis(clock));
accountsManager.update(account);
return accountsManager.updateDeviceLastSeen(account, device, Util.todayInMillis(clock));
}
return account;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.util.Base64;
import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.util.Pair;
public class BasicAuthorizationHeader {
private final String username;
private final long deviceId;
private final String password;
private BasicAuthorizationHeader(final String username, final long deviceId, final String password) {
this.username = username;
this.deviceId = deviceId;
this.password = password;
}
public static BasicAuthorizationHeader fromString(final String header) throws InvalidAuthorizationHeaderException {
try {
if (StringUtils.isBlank(header)) {
throw new InvalidAuthorizationHeaderException("Blank header");
}
final int spaceIndex = header.indexOf(' ');
if (spaceIndex == -1) {
throw new InvalidAuthorizationHeaderException("Invalid authorization header: " + header);
}
final String authorizationType = header.substring(0, spaceIndex);
if (!"Basic".equals(authorizationType)) {
throw new InvalidAuthorizationHeaderException("Unsupported authorization method: " + authorizationType);
}
final String credentials;
try {
credentials = new String(Base64.getDecoder().decode(header.substring(spaceIndex + 1)));
} catch (final IndexOutOfBoundsException e) {
throw new InvalidAuthorizationHeaderException("Missing credentials");
}
if (StringUtils.isEmpty(credentials)) {
throw new InvalidAuthorizationHeaderException("Bad decoded value: " + credentials);
}
final int credentialSeparatorIndex = credentials.indexOf(':');
if (credentialSeparatorIndex == -1) {
throw new InvalidAuthorizationHeaderException("Badly-formatted credentials: " + credentials);
}
final String usernameComponent = credentials.substring(0, credentialSeparatorIndex);
final String username;
final long deviceId;
{
final Pair<String, Long> identifierAndDeviceId =
BaseAccountAuthenticator.getIdentifierAndDeviceId(usernameComponent);
username = identifierAndDeviceId.first();
deviceId = identifierAndDeviceId.second();
}
final String password = credentials.substring(credentialSeparatorIndex + 1);
if (StringUtils.isAnyBlank(username, password)) {
throw new InvalidAuthorizationHeaderException("Username or password were blank");
}
return new BasicAuthorizationHeader(username, deviceId, password);
} catch (final IllegalArgumentException | IndexOutOfBoundsException e) {
throw new InvalidAuthorizationHeaderException(e);
}
}
public String getUsername() {
return username;
}
public long getDeviceId() {
return deviceId;
}
public String getPassword() {
return password;
}
}

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;
@@ -33,7 +31,7 @@ public class CertificateGenerator {
this.serverCertificate = ServerCertificate.parseFrom(serverCertificate);
}
public byte[] createFor(Account account, Device device, boolean includeE164) throws IOException, InvalidKeyException {
public byte[] createFor(Account account, Device device, boolean includeE164) throws InvalidKeyException {
SenderCertificate.Certificate.Builder builder = SenderCertificate.Certificate.newBuilder()
.setSenderDevice(Math.toIntExact(device.getId()))
.setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays))
@@ -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

@@ -0,0 +1,20 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that an endpoint may change the "enabled" state of one or more devices associated with an account, and that
* any websockets associated with the account may need to be refreshed after a call to that endpoint.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ChangesDeviceEnabledState {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.glassfish.jersey.server.ContainerRequest;
import org.whispersystems.textsecuregcm.storage.Account;
import javax.ws.rs.core.SecurityContext;
import java.util.Optional;
class ContainerRequestUtil {
static Optional<Account> getAuthenticatedAccount(final ContainerRequest request) {
return Optional.ofNullable(request.getSecurityContext())
.map(SecurityContext::getUserPrincipal)
.map(principal -> principal instanceof AccountAndAuthenticatedDeviceHolder
? ((AccountAndAuthenticatedDeviceHolder) principal).getAccount() : null);
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.storage.Account;
import javax.security.auth.Subject;
import java.security.Principal;
public class DisabledPermittedAccount implements Principal {
private final Account account;
public DisabledPermittedAccount(Account account) {
this.account = account;
}
public Account getAccount() {
return account;
}
// Principal implementation
@Override
public String getName() {
return null;
}
@Override
public boolean implies(Subject subject) {
return false;
}
}

View File

@@ -1,27 +1,25 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import java.util.Optional;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Optional;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
public class DisabledPermittedAccountAuthenticator extends BaseAccountAuthenticator implements Authenticator<BasicCredentials, DisabledPermittedAccount> {
public class DisabledPermittedAccountAuthenticator extends BaseAccountAuthenticator implements
Authenticator<BasicCredentials, DisabledPermittedAuthenticatedAccount> {
public DisabledPermittedAccountAuthenticator(AccountsManager accountsManager) {
super(accountsManager);
}
@Override
public Optional<DisabledPermittedAccount> authenticate(BasicCredentials credentials) {
Optional<Account> account = super.authenticate(credentials, false);
return account.map(DisabledPermittedAccount::new);
public Optional<DisabledPermittedAuthenticatedAccount> authenticate(BasicCredentials credentials) {
Optional<AuthenticatedAccount> account = super.authenticate(credentials, false);
return account.map(DisabledPermittedAuthenticatedAccount::new);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.security.Principal;
import javax.security.auth.Subject;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
public class DisabledPermittedAuthenticatedAccount implements Principal, AccountAndAuthenticatedDeviceHolder {
private final AuthenticatedAccount authenticatedAccount;
public DisabledPermittedAuthenticatedAccount(final AuthenticatedAccount authenticatedAccount) {
this.authenticatedAccount = authenticatedAccount;
}
@Override
public Account getAccount() {
return authenticatedAccount.getAccount();
}
@Override
public Device getAuthenticatedDevice() {
return authenticatedAccount.getAuthenticatedDevice();
}
// Principal implementation
@Override
public String getName() {
return null;
}
@Override
public boolean implies(Subject subject) {
return false;
}
}

View File

@@ -1,97 +1,71 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.util.Util;
import com.google.common.annotations.VisibleForTesting;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Clock;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.binary.Hex;
import org.whispersystems.textsecuregcm.util.Util;
public class ExternalServiceCredentialGenerator {
private final Logger logger = LoggerFactory.getLogger(ExternalServiceCredentialGenerator.class);
private final byte[] key;
private final byte[] userIdKey;
private final boolean usernameDerivation;
private final boolean prependUsername;
private final Clock clock;
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation) {
this.key = key;
this.userIdKey = userIdKey;
this.usernameDerivation = usernameDerivation;
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey) {
this(key, userIdKey, true, true);
}
public ExternalServiceCredentials generateFor(String number) {
Mac mac = getMacInstance();
String username = getUserId(number, mac, usernameDerivation);
long currentTimeSeconds = System.currentTimeMillis() / 1000;
String prefix = username + ":" + currentTimeSeconds;
String output = Hex.encodeHexString(Util.truncate(getHmac(key, prefix.getBytes(), mac), 10));
String token = prefix + ":" + output;
public ExternalServiceCredentialGenerator(byte[] key, boolean prependUsername) {
this(key, new byte[0], false, prependUsername);
}
@VisibleForTesting
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation) {
this(key, userIdKey, usernameDerivation, true);
}
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
boolean prependUsername) {
this(key, userIdKey, usernameDerivation, prependUsername, Clock.systemUTC());
}
@VisibleForTesting
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
boolean prependUsername, Clock clock) {
this.key = key;
this.userIdKey = userIdKey;
this.usernameDerivation = usernameDerivation;
this.prependUsername = prependUsername;
this.clock = clock;
}
public ExternalServiceCredentials generateFor(String identity) {
Mac mac = getMacInstance();
String username = getUserId(identity, mac, usernameDerivation);
long currentTimeSeconds = clock.millis() / 1000;
String prefix = username + ":" + currentTimeSeconds;
String output = Hex.encodeHexString(Util.truncate(getHmac(key, prefix.getBytes(), mac), 10));
String token = (prependUsername ? prefix : currentTimeSeconds) + ":" + output;
return new ExternalServiceCredentials(username, token);
}
public boolean isValid(String token, String number, long currentTimeMillis) {
String[] parts = token.split(":");
Mac mac = getMacInstance();
if (parts.length != 3) {
return false;
}
if (!getUserId(number, mac, usernameDerivation).equals(parts[0])) {
return false;
}
if (!isValidTime(parts[1], currentTimeMillis)) {
return false;
}
return isValidSignature(parts[0] + ":" + parts[1], parts[2], mac);
}
private String getUserId(String number, Mac mac, boolean usernameDerivation) {
if (usernameDerivation) return Hex.encodeHexString(Util.truncate(getHmac(userIdKey, number.getBytes(), mac), 10));
else return number;
}
private boolean isValidTime(String timeString, long currentTimeMillis) {
try {
long tokenTime = Long.parseLong(timeString);
long ourTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis);
return TimeUnit.SECONDS.toHours(Math.abs(ourTime - tokenTime)) < 24;
} catch (NumberFormatException e) {
logger.warn("Number Format", e);
return false;
}
}
private boolean isValidSignature(String prefix, String suffix, Mac mac) {
try {
byte[] ourSuffix = Util.truncate(getHmac(key, prefix.getBytes(), mac), 10);
byte[] theirSuffix = Hex.decodeHex(suffix.toCharArray());
return MessageDigest.isEqual(ourSuffix, theirSuffix);
} catch (DecoderException e) {
logger.warn("DirectoryCredentials", e);
return false;
}
}
private Mac getMacInstance() {
try {
return Mac.getInstance("HmacSHA256");

View File

@@ -5,12 +5,15 @@
package org.whispersystems.textsecuregcm.auth;
public class InvalidAuthorizationHeaderException extends Exception {
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
public class InvalidAuthorizationHeaderException extends WebApplicationException {
public InvalidAuthorizationHeaderException(String s) {
super(s);
super(s, Status.UNAUTHORIZED);
}
public InvalidAuthorizationHeaderException(Exception e) {
super(e);
super(e, Status.UNAUTHORIZED);
}
}

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

@@ -0,0 +1,46 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.util.Pair;
public class PhoneNumberChangeRefreshRequirementProvider implements WebsocketRefreshRequirementProvider {
private static final String INITIAL_NUMBER_KEY =
PhoneNumberChangeRefreshRequirementProvider.class.getName() + ".initialNumber";
@Override
public void handleRequestFiltered(final RequestEvent requestEvent) {
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest())
.ifPresent(account -> requestEvent.getContainerRequest().setProperty(INITIAL_NUMBER_KEY, account.getNumber()));
}
@Override
public List<Pair<UUID, Long>> handleRequestFinished(final RequestEvent requestEvent) {
final String initialNumber = (String) requestEvent.getContainerRequest().getProperty(INITIAL_NUMBER_KEY);
if (initialNumber != null) {
final Optional<Account> maybeAuthenticatedAccount =
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest());
return maybeAuthenticatedAccount
.filter(account -> !initialNumber.equals(account.getNumber()))
.map(account -> account.getDevices().stream()
.map(device -> new Pair<>(account.getUuid(), device.getId()))
.collect(Collectors.toList()))
.orElse(Collections.emptyList());
} else {
return Collections.emptyList();
}
}
}

View File

@@ -9,7 +9,6 @@ import com.google.common.annotations.VisibleForTesting;
import org.whispersystems.textsecuregcm.util.Util;
import javax.annotation.Nullable;
import java.security.MessageDigest;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -20,42 +19,33 @@ public class StoredRegistrationLock {
private final Optional<String> registrationLockSalt;
private final Optional<String> deprecatedPin;
private final long lastSeen;
public StoredRegistrationLock(Optional<String> registrationLock, Optional<String> registrationLockSalt, Optional<String> deprecatedPin, long lastSeen) {
public StoredRegistrationLock(Optional<String> registrationLock, Optional<String> registrationLockSalt, long lastSeen) {
this.registrationLock = registrationLock;
this.registrationLockSalt = registrationLockSalt;
this.deprecatedPin = deprecatedPin;
this.lastSeen = lastSeen;
}
public boolean requiresClientRegistrationLock() {
return ((registrationLock.isPresent() && registrationLockSalt.isPresent()) || deprecatedPin.isPresent()) && System.currentTimeMillis() - lastSeen < TimeUnit.DAYS.toMillis(7);
return registrationLock.isPresent() && registrationLockSalt.isPresent() && System.currentTimeMillis() - lastSeen < TimeUnit.DAYS.toMillis(7);
}
public boolean needsFailureCredentials() {
return registrationLock.isPresent() && registrationLockSalt.isPresent();
}
public boolean hasDeprecatedPin() {
return deprecatedPin.isPresent();
}
public long getTimeRemaining() {
return TimeUnit.DAYS.toMillis(7) - (System.currentTimeMillis() - lastSeen);
}
public boolean verify(@Nullable String clientRegistrationLock, @Nullable String clientDeprecatedPin) {
if (Util.isEmpty(clientRegistrationLock) && Util.isEmpty(clientDeprecatedPin)) {
public boolean verify(@Nullable String clientRegistrationLock) {
if (Util.isEmpty(clientRegistrationLock)) {
return false;
}
if (registrationLock.isPresent() && registrationLockSalt.isPresent() && !Util.isEmpty(clientRegistrationLock)) {
return new AuthenticationCredentials(registrationLock.get(), registrationLockSalt.get()).verify(clientRegistrationLock);
} else if (deprecatedPin.isPresent() && !Util.isEmpty(clientDeprecatedPin)) {
return MessageDigest.isEqual(deprecatedPin.get().getBytes(), clientDeprecatedPin.getBytes());
} else {
return false;
}
@@ -63,6 +53,6 @@ public class StoredRegistrationLock {
@VisibleForTesting
public StoredRegistrationLock forTime(long timestamp) {
return new StoredRegistrationLock(registrationLock, registrationLockSalt, deprecatedPin, timestamp);
return new StoredRegistrationLock(registrationLock, registrationLockSalt, timestamp);
}
}

View File

@@ -5,36 +5,38 @@
package org.whispersystems.textsecuregcm.auth;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.textsecuregcm.util.Util;
import java.security.MessageDigest;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.util.Util;
public class StoredVerificationCode {
@JsonProperty
private String code;
private final String code;
@JsonProperty
private long timestamp;
private final long timestamp;
@JsonProperty
private String pushCode;
private final String pushCode;
@JsonProperty
private String twilioVerificationSid;
@Nullable
private final String twilioVerificationSid;
public StoredVerificationCode() {
}
public static final Duration EXPIRATION = Duration.ofMinutes(10);
public StoredVerificationCode(String code, long timestamp, String pushCode) {
this(code, timestamp, pushCode, null);
}
@JsonCreator
public StoredVerificationCode(
@JsonProperty("code") final String code,
@JsonProperty("timestamp") final long timestamp,
@JsonProperty("pushCode") final String pushCode,
@JsonProperty("twilioVerificationSid") @Nullable final String twilioVerificationSid) {
public StoredVerificationCode(String code, long timestamp, String pushCode, String twilioVerificationSid) {
this.code = code;
this.timestamp = timestamp;
this.pushCode = pushCode;
@@ -58,10 +60,6 @@ public class StoredVerificationCode {
}
public boolean isValid(String theirCodeString) {
if (timestamp + TimeUnit.MINUTES.toMillis(10) < System.currentTimeMillis()) {
return false;
}
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

@@ -0,0 +1,38 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.glassfish.jersey.server.monitoring.ApplicationEvent;
import org.glassfish.jersey.server.monitoring.ApplicationEventListener;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.glassfish.jersey.server.monitoring.RequestEventListener;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
/**
* Delegates request events to a listener that watches for intra-request changes that require websocket refreshes
*/
public class WebsocketRefreshApplicationEventListener implements ApplicationEventListener {
private final WebsocketRefreshRequestEventListener websocketRefreshRequestEventListener;
public WebsocketRefreshApplicationEventListener(final AccountsManager accountsManager,
final ClientPresenceManager clientPresenceManager) {
this.websocketRefreshRequestEventListener = new WebsocketRefreshRequestEventListener(clientPresenceManager,
new AuthEnablementRefreshRequirementProvider(accountsManager),
new PhoneNumberChangeRefreshRequirementProvider());
}
@Override
public void onEvent(final ApplicationEvent event) {
}
@Override
public RequestEventListener onRequest(final RequestEvent requestEvent) {
return websocketRefreshRequestEventListener;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
public class WebsocketRefreshRequestEventListener implements RequestEventListener {
private final ClientPresenceManager clientPresenceManager;
private final WebsocketRefreshRequirementProvider[] providers;
private static final Counter DISPLACED_ACCOUNTS = Metrics.counter(
name(WebsocketRefreshRequestEventListener.class, "displacedAccounts"));
private static final Counter DISPLACED_DEVICES = Metrics.counter(
name(WebsocketRefreshRequestEventListener.class, "displacedDevices"));
private static final Logger logger = LoggerFactory.getLogger(WebsocketRefreshRequestEventListener.class);
public WebsocketRefreshRequestEventListener(
final ClientPresenceManager clientPresenceManager,
final WebsocketRefreshRequirementProvider... providers) {
this.clientPresenceManager = clientPresenceManager;
this.providers = providers;
}
@Context
private ResourceInfo resourceInfo;
@Override
public void onEvent(final RequestEvent event) {
if (event.getType() == Type.REQUEST_FILTERED) {
for (final WebsocketRefreshRequirementProvider provider : providers) {
provider.handleRequestFiltered(event);
}
} else if (event.getType() == Type.FINISHED) {
final AtomicInteger displacedDevices = new AtomicInteger(0);
Arrays.stream(providers)
.flatMap(provider -> provider.handleRequestFinished(event).stream())
.distinct()
.forEach(pair -> {
try {
displacedDevices.incrementAndGet();
clientPresenceManager.disconnectPresence(pair.first(), pair.second());
} catch (final Exception e) {
logger.error("Could not displace device presence", e);
}
});
if (displacedDevices.get() > 0) {
DISPLACED_ACCOUNTS.increment();
DISPLACED_DEVICES.increment(displacedDevices.get());
}
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.whispersystems.textsecuregcm.util.Pair;
/**
* A websocket refresh requirement provider watches for intra-request changes (e.g. to authentication status) that
* require a websocket refresh.
*/
public interface WebsocketRefreshRequirementProvider {
/**
* Processes a request after filters have run and the request has been mapped to a destination controller.
*
* @param requestEvent the request event to observe
*/
void handleRequestFiltered(RequestEvent requestEvent);
/**
* Processes a request after all normal request handling has been completed.
*
* @param requestEvent the request event to observe
* @return a list of pairs of account UUID/device ID pairs identifying websockets that need to be refreshed as a
* result of the observed request
*/
List<Pair<UUID, Long>> handleRequestFinished(RequestEvent requestEvent);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import java.util.List;
import java.util.Locale;
import org.whispersystems.textsecuregcm.entities.Badge;
public interface BadgeTranslator {
Badge translate(List<Locale> acceptableLanguages, String badgeId);
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import com.google.common.annotations.VisibleForTesting;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.signal.i18n.HeaderControlledResourceBundleLookup;
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.entities.Badge;
import org.whispersystems.textsecuregcm.entities.BadgeSvg;
import org.whispersystems.textsecuregcm.entities.SelfBadge;
import org.whispersystems.textsecuregcm.storage.AccountBadge;
public class ConfiguredProfileBadgeConverter implements ProfileBadgeConverter, BadgeTranslator {
@VisibleForTesting
static final String BASE_NAME = "org.signal.badges.Badges";
private final Clock clock;
private final Map<String, BadgeConfiguration> knownBadges;
private final List<String> badgeIdsEnabledForAll;
private final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup;
public ConfiguredProfileBadgeConverter(
final Clock clock,
final BadgesConfiguration badgesConfiguration,
final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup) {
this.clock = clock;
this.knownBadges = badgesConfiguration.getBadges().stream()
.collect(Collectors.toMap(BadgeConfiguration::getId, Function.identity()));
this.badgeIdsEnabledForAll = badgesConfiguration.getBadgeIdsEnabledForAll();
this.headerControlledResourceBundleLookup = headerControlledResourceBundleLookup;
}
@Override
public Badge translate(final List<Locale> acceptableLanguages, final String badgeId) {
final ResourceBundle resourceBundle = headerControlledResourceBundleLookup.getResourceBundle(BASE_NAME,
acceptableLanguages);
final BadgeConfiguration configuration = knownBadges.get(badgeId);
return newBadge(
false,
configuration.getId(),
configuration.getCategory(),
resourceBundle.getString(configuration.getId() + "_name"),
resourceBundle.getString(configuration.getId() + "_description"),
configuration.getSprites(),
configuration.getSvg(),
configuration.getSvgs(),
null,
false);
}
@Override
public List<Badge> convert(
final List<Locale> acceptableLanguages,
final List<AccountBadge> accountBadges,
final boolean isSelf) {
if (accountBadges.isEmpty() && badgeIdsEnabledForAll.isEmpty()) {
return List.of();
}
final Instant now = clock.instant();
final ResourceBundle resourceBundle = headerControlledResourceBundleLookup.getResourceBundle(BASE_NAME,
acceptableLanguages);
List<Badge> badges = accountBadges.stream()
.filter(accountBadge -> (isSelf || accountBadge.isVisible())
&& now.isBefore(accountBadge.getExpiration())
&& knownBadges.containsKey(accountBadge.getId()))
.map(accountBadge -> {
BadgeConfiguration configuration = knownBadges.get(accountBadge.getId());
return newBadge(
isSelf,
accountBadge.getId(),
configuration.getCategory(),
resourceBundle.getString(accountBadge.getId() + "_name"),
resourceBundle.getString(accountBadge.getId() + "_description"),
configuration.getSprites(),
configuration.getSvg(),
configuration.getSvgs(),
accountBadge.getExpiration(),
accountBadge.isVisible());
})
.collect(Collectors.toCollection(ArrayList::new));
badges.addAll(badgeIdsEnabledForAll.stream().filter(knownBadges::containsKey).map(id -> {
BadgeConfiguration configuration = knownBadges.get(id);
return newBadge(
isSelf,
id,
configuration.getCategory(),
resourceBundle.getString(id + "_name"),
resourceBundle.getString(id + "_description"),
configuration.getSprites(),
configuration.getSvg(),
configuration.getSvgs(),
now.plus(Duration.ofDays(1)),
true);
}).collect(Collectors.toList()));
return badges;
}
private Badge newBadge(
final boolean isSelf,
final String id,
final String category,
final String name,
final String description,
final List<String> sprites,
final String svg,
final List<BadgeSvg> svgs,
final Instant expiration,
final boolean visible) {
if (isSelf) {
return new SelfBadge(id, category, name, description, sprites, svg, svgs, expiration, visible);
} else {
return new Badge(id, category, name, description, sprites, svg, svgs);
}
}
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import java.util.List;
import java.util.Locale;
public interface LevelTranslator {
String translate(List<Locale> acceptableLanguages, String badgeId);
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import java.util.List;
import java.util.Locale;
import org.whispersystems.textsecuregcm.entities.Badge;
import org.whispersystems.textsecuregcm.storage.AccountBadge;
public interface ProfileBadgeConverter {
/**
* Converts the {@link AccountBadge}s for an account into the objects
* that can be returned on a profile fetch.
*/
List<Badge> convert(List<Locale> acceptableLanguages, List<AccountBadge> accountBadges, boolean isSelf);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
import javax.annotation.Nonnull;
import org.signal.i18n.HeaderControlledResourceBundleLookup;
public class ResourceBundleLevelTranslator implements LevelTranslator {
private static final String BASE_NAME = "org.signal.subscriptions.Subscriptions";
private final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup;
public ResourceBundleLevelTranslator(
@Nonnull final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup) {
this.headerControlledResourceBundleLookup = Objects.requireNonNull(headerControlledResourceBundleLookup);
}
@Override
public String translate(final List<Locale> acceptableLanguages, final String badgeId) {
final ResourceBundle resourceBundle = headerControlledResourceBundleLookup.getResourceBundle(BASE_NAME,
acceptableLanguages);
return resourceBundle.getString(badgeId);
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotBlank;
public class AbusiveMessageFilterConfiguration {
@JsonProperty
@NotBlank
private final String environment;
@JsonCreator
public AbusiveMessageFilterConfiguration(@JsonProperty("environment") final String environment) {
this.environment = environment;
}
public String getEnvironment() {
return environment;
}
}

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,13 +0,0 @@
package org.whispersystems.textsecuregcm.configuration;
import javax.validation.constraints.NotNull;
public class AccountsDynamoDbConfiguration extends DynamoDbConfiguration {
@NotNull
private String phoneNumberTableName;
public String getPhoneNumberTableName() {
return phoneNumberTableName;
}
}

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,68 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.entities.BadgeSvg;
import org.whispersystems.textsecuregcm.util.ExactlySize;
public class BadgeConfiguration {
public static final String CATEGORY_TESTING = "testing";
private final String id;
private final String category;
private final List<String> sprites;
private final String svg;
private final List<BadgeSvg> svgs;
@JsonCreator
public BadgeConfiguration(
@JsonProperty("id") final String id,
@JsonProperty("category") final String category,
@JsonProperty("sprites") final List<String> sprites,
@JsonProperty("svg") final String svg,
@JsonProperty("svgs") final List<BadgeSvg> svgs) {
this.id = id;
this.category = category;
this.sprites = sprites;
this.svg = svg;
this.svgs = svgs;
}
@NotEmpty
public String getId() {
return id;
}
@NotEmpty
public String getCategory() {
return category;
}
@NotNull
@ExactlySize(6)
public List<String> getSprites() {
return sprites;
}
@NotEmpty
public String getSvg() {
return svg;
}
@NotNull
public List<BadgeSvg> getSvgs() {
return svgs;
}
public boolean isTestBadge() {
return CATEGORY_TESTING.equals(category);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import io.dropwizard.validation.ValidationMethod;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
public class BadgesConfiguration {
private final List<BadgeConfiguration> badges;
private final List<String> badgeIdsEnabledForAll;
private final Map<Long, String> receiptLevels;
@JsonCreator
public BadgesConfiguration(
@JsonProperty("badges") @JsonSetter(nulls = Nulls.AS_EMPTY) final List<BadgeConfiguration> badges,
@JsonProperty("badgeIdsEnabledForAll") @JsonSetter(nulls = Nulls.AS_EMPTY) final List<String> badgeIdsEnabledForAll,
@JsonProperty("receiptLevels") @JsonSetter(nulls = Nulls.AS_EMPTY) final Map<Long, String> receiptLevels) {
this.badges = Objects.requireNonNull(badges);
this.badgeIdsEnabledForAll = Objects.requireNonNull(badgeIdsEnabledForAll);
this.receiptLevels = Objects.requireNonNull(receiptLevels);
}
@Valid
@NotNull
public List<BadgeConfiguration> getBadges() {
return badges;
}
@Valid
@NotNull
public List<String> getBadgeIdsEnabledForAll() {
return badgeIdsEnabledForAll;
}
@Valid
@NotNull
public Map<Long, String> getReceiptLevels() {
return receiptLevels;
}
@JsonIgnore
@ValidationMethod(message = "contains receipt level mappings that are not configured badges")
public boolean isAllReceiptLevelsConfigured() {
final Set<String> badgeNames = badges.stream().map(BadgeConfiguration::getId).collect(Collectors.toSet());
return badgeNames.containsAll(receiptLevels.values());
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.util.ExactlySize;
public class BoostConfiguration {
private final long level;
private final Duration expiration;
private final Map<String, List<BigDecimal>> currencies;
private final String badge;
@JsonCreator
public BoostConfiguration(
@JsonProperty("level") long level,
@JsonProperty("expiration") Duration expiration,
@JsonProperty("currencies") Map<String, List<BigDecimal>> currencies,
@JsonProperty("badge") String badge) {
this.level = level;
this.expiration = expiration;
this.currencies = currencies;
this.badge = badge;
}
public long getLevel() {
return level;
}
@NotNull
public Duration getExpiration() {
return expiration;
}
@Valid
@NotNull
public Map<@NotEmpty String, @Valid @ExactlySize(6) List<@DecimalMin("0.01") @NotNull BigDecimal>> getCurrencies() {
return currencies;
}
@NotEmpty
public String getBadge() {
return badge;
}
}

View File

@@ -7,15 +7,15 @@ package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.time.Duration;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
public class CircuitBreakerConfiguration {
@JsonProperty
@@ -39,6 +39,9 @@ public class CircuitBreakerConfiguration {
@Min(1)
private long waitDurationInOpenStateInSeconds = 10;
@JsonProperty
private List<String> ignoredExceptions = Collections.emptyList();
public int getFailureRateThreshold() {
return failureRateThreshold;
@@ -56,6 +59,18 @@ public class CircuitBreakerConfiguration {
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());
}
@VisibleForTesting
public void setFailureRateThreshold(int failureRateThreshold) {
this.failureRateThreshold = failureRateThreshold;
@@ -76,9 +91,15 @@ public class CircuitBreakerConfiguration {
this.waitDurationInOpenStateInSeconds = seconds;
}
@VisibleForTesting
public void setIgnoredExceptions(final List<String> ignoredExceptions) {
this.ignoredExceptions = ignoredExceptions;
}
public CircuitBreakerConfig toCircuitBreakerConfig() {
return CircuitBreakerConfig.custom()
.failureRateThreshold(getFailureRateThreshold())
.ignoreExceptions(getIgnoredExceptions().toArray(new Class[0]))
.ringBufferSizeInHalfOpenState(getRingBufferSizeInHalfOpenState())
.waitDurationInOpenState(Duration.ofSeconds(getWaitDurationInOpenStateInSeconds()))
.ringBufferSizeInClosedState(getRingBufferSizeInClosedState())

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