Compare commits

...

1465 Commits

Author SHA1 Message Date
Ravi Khadiwala
86f9322036 Update to the latest version of the spam filter 2024-02-23 16:08:41 -06:00
Katherine
12c6af23ee Map TransactionConflict to ContestedOptimisticLockException in username flows 2024-02-23 14:06:03 -08:00
Ravi Khadiwala
69330f47fd Explicitly call spam-filter for verification session updates
Pass in the same information to the spam-filter, but just use explicit
method calls rather than jersey request filters.
2024-02-23 16:04:24 -06:00
Ravi Khadiwala
4f40c128bf Explicitly call spam-filter for challenges
Pass in the same information to the spam-filter, but just use explicit
method calls rather than jersey request filters.
2024-02-23 15:58:52 -06:00
Jon Chambers
30b5ad1515 Fix an incorrectly-named "check keys" method 2024-02-23 13:17:10 -05:00
adel-signal
665a26d164 update call routing to return ipv6 ips in RFC3986 format 2024-02-23 11:57:58 -05:00
Jon Chambers
a5774bf6ff Introduce a (dormant) Noise/WebSocket for future client/server communication 2024-02-23 11:42:42 -05:00
Jon Chambers
d2716fe5cf Add an endpoint for checking that clients and the server have a common view of the client's repeated-use keys 2024-02-23 11:41:58 -05:00
Jon Chambers
279f877bf2 Validate pre-key signatures via the legacy "set signed pre-key" endpoint 2024-02-23 11:33:41 -05:00
Ravi Khadiwala
d51e6a43e7 Update to the latest version of the spam filter 2024-02-22 12:12:42 -06:00
Ravi Khadiwala
6a96756c87 Update to the latest version of the spam filter 2024-02-22 12:09:37 -06:00
Ravi Khadiwala
df69d9f195 Annotate authenticated endpoints with @ReadOnly or @Mutable 2024-02-22 12:05:32 -06:00
Ravi Khadiwala
26ffa19f36 Lifecycle management for Account objects reused accross websocket requests 2024-02-22 12:05:32 -06:00
Jon Chambers
29ef3f0b41 Add tests and metrics for parsing invalid keys 2024-02-21 15:32:25 -05:00
Jon Chambers
106d5e54c7 Extract a common base class for public key serializers/deserializers 2024-02-21 15:32:25 -05:00
Jon Chambers
6ac2460eb0 Drop the "key without version byte" counter 2024-02-21 15:32:25 -05:00
dependabot[bot]
79c030b138 Bump org.apache.commons:commons-compress from 1.24.0 to 1.26.0 in /service 2024-02-21 11:52:41 -08:00
Chris Eager
c8d649e8c2 Update MetricsHttpChannelListener to share MetricsRequestEventListener namespace 2024-02-21 13:32:47 -06:00
Ravi Khadiwala
1fdf82dd6c Remove unused ChangePhoneNumberRequest object
Actual request object is ChangeNumberRequest
2024-02-21 13:32:27 -06:00
Jon Chambers
4aa4246695 Clear account records from the account cache after username operations 2024-02-20 12:11:29 -05:00
Chris Eager
1bebceb29c Actually fix flaky test in MetricsHttpChannelListenerIntegrationTest 2024-02-20 10:52:04 -06:00
Jon Chambers
a2139ee236 Revert "Add diagnostic dimensions to the "get keys" counter"
This reverts commit cd64390141.
2024-02-18 20:14:05 -05:00
Jon Chambers
8c55f39cdf Revert "Use a phased enrollment strategy for the pnp compatibility flag"
This reverts commit 3e12a8780d.
2024-02-18 20:08:39 -05:00
Chris Eager
0329184c94 Fix flaky test in MetricsHttpChannelListenerIntegrationTest 2024-02-18 20:08:29 -05:00
Jon Chambers
cd64390141 Add diagnostic dimensions to the "get keys" counter 2024-02-18 18:10:58 -05:00
Jon Chambers
3e12a8780d Use a phased enrollment strategy for the pnp compatibility flag 2024-02-18 17:59:00 -05:00
Jon Chambers
11e6ff1bbe Introduce a pnp capability for backward compatibility 2024-02-18 17:59:00 -05:00
Jon Chambers
36f85fc97e Fix an inverted boolean in a counter 2024-02-16 15:18:18 -05:00
Jon Chambers
9040cfd200 Count "store key" attempts by key type/platform 2024-02-16 13:09:55 -05:00
Jon Chambers
757da3b15a Fully-qualify OpenAPI Tag, allowing for imported Micrometer Tag 2024-02-16 13:09:55 -05:00
Jon Chambers
d162590a32 Record a remote-aggregated distribution of one-time pre-key counts 2024-02-16 13:09:55 -05:00
Jon Chambers
f41e1716c6 Combine/expand existing "get keys" counter 2024-02-16 13:09:55 -05:00
Jon Chambers
4dce0f1b9d Add identity type/"stale" dimensions to the "pre-key unavailable" counter 2024-02-16 13:09:55 -05:00
Jonathan Klabunde Tomer
fef57dce0d use explicit Timer rather than micrometer annotation for send-message latency distribution 2024-02-15 14:58:43 -08:00
Jon Chambers
d884700b61 Significantly reduce default Redis command timeout 2024-02-15 17:57:17 -05:00
Jonathan Klabunde Tomer
ff9ad4bd1d export 1:1 message send latency as a full distribution 2024-02-15 10:33:02 -08:00
Chris Eager
9ce2b7555c Add static servlet paths to MetricsHttpChannelListener 2024-02-14 17:09:30 -06:00
Jon Chambers
f90ccd3391 Count attempts to fetch a one-time EC pre-key that result in a "key unavailable" response 2024-02-14 18:04:31 -05:00
Jon Chambers
5ff092e541 Retire the pni device capability 2024-02-14 18:04:23 -05:00
Ravi Khadiwala
dcdf401f64 Make DELETE /v1/device synchronous 2024-02-14 15:30:10 -05:00
Chris Eager
e4fb80b39b Add onResponseFailure handler to MetricsHttpChannelListener 2024-02-14 13:43:23 -06:00
Chris Eager
9745854ab8 Switch MetricsHttpChannelListener to ContainerResponseFilter 2024-02-14 13:43:23 -06:00
Jon Chambers
7124621f66 Use pre-calculated pre-key counts when possible 2024-02-14 14:28:33 -05:00
Jon Chambers
47fd8f5793 Assume that all devices are PNI-capable 2024-02-14 14:27:48 -05:00
Jonathan Klabunde Tomer
40d698f2db Remove last vestiges of stale capabilities.
Not for deployment until 2024-02-06!
2024-02-14 14:23:05 -05:00
adel-signal
74abe98706 initialize call routing data syncs 2024-02-13 17:05:08 -08:00
Chris Eager
86787f3bc8 Start DynamicConfigurationManager directly after construction 2024-02-13 13:08:20 -06:00
Chris Eager
699b0c775a Add dynamic configuration to enable detailed Lettuce metrics 2024-02-13 10:33:43 -06:00
Chris Eager
ff59ef8094 Add MetricHttpChannelListener 2024-02-13 10:29:03 -06:00
Jonathan Klabunde Tomer
089af7cc1f correct metric name in MaxMindDatabaseManager timer 2024-02-09 13:37:39 -08:00
Jonathan Klabunde Tomer
1591a2d9a3 CallRoutingTableManager: fix crash on startup due to typo in metric naming 2024-02-09 12:27:51 -08:00
Jonathan Klabunde Tomer
f7984ed642 CallDnsRecordsManager: fix crash on startup due to typo in metric naming 2024-02-09 12:14:50 -08:00
Jonathan Klabunde Tomer
be634c6043 Update to the latest version of the spam filter 2024-02-09 11:38:18 -08:00
adel-signal
d1f68eacd9 Add call routing API endpoint for turn servers 2024-02-09 11:12:23 -08:00
Jon Chambers
4f45f23094 Prevent old versions of the Android app from carrying out "change phone number" requests 2024-02-09 09:58:38 -05:00
Jon Chambers
c5dc01ee11 Restore high-cardinality Lettuce metrics for debugging 2024-02-08 19:27:56 -05:00
Ravi Khadiwala
587c385936 Update to the latest version of the spam filter 2024-02-07 17:19:59 -06:00
Ravi Khadiwala
3a641a58b0 Update to the latest version of the spam filter 2024-02-07 14:38:35 -06:00
Chris Eager
e944306a28 Remove obsolete dropwizard-db dependency 2024-02-07 14:35:23 -06:00
Ravi Khadiwala
3b44ed6d16 Explicitly call spam-filter for messages
Pass in the same information to the spam-filter, but just use explicit
method calls rather than jersey request filters.
2024-02-07 14:30:32 -06:00
Chris Eager
0965ab8063 Move HeaderUtils#getMostRecentyProxy to RemoteAddressFilter 2024-02-07 14:28:37 -06:00
Chris Eager
fcae100df1 Add dropwizard-http2 dependency 2024-02-07 14:28:20 -06:00
Fedor Indutny
24a7762873 Add identityType tag to SENT_MESSAGE_COUNTER_NAME 2024-02-06 17:39:00 -06:00
Chris Eager
e441ab60a2 Add metric for IPv4/IPv6 requests count 2024-02-06 17:38:25 -06:00
Chris Eager
50c2bc5edb Update to the latest version of the spam filter 2024-02-05 13:45:50 -06:00
Chris Eager
2ab14ca59e Refactor remote address/X-Forwarded-For handling 2024-02-05 13:37:06 -06:00
Chris Eager
4475d65780 Make Accounts#changeNumber exception handling more resilient to future changes 2024-02-01 15:14:15 -06:00
Ravi Khadiwala
b1d10f5817 Add lifecycle management for AwsCrt for commands 2024-02-01 15:08:45 -06:00
Chris Eager
36664f37de Update to the latest version of the spam filter 2024-01-31 18:04:03 -06:00
Chris Eager
c838df90ef Add HttpServletRequestUtil 2024-01-31 17:53:47 -06:00
Chris Eager
fb39af67e5 Allow for more TransactWriteItems when inspecting TransactionCanceledException 2024-01-31 17:46:32 -06:00
Chris Eager
2d4d37f96a Translate TransactionCanceledException to ContestedOptimisticLockException in Accounts#changeNumber 2024-01-31 17:27:16 -06:00
Jonathan Klabunde Tomer
84af984c4b remove unversioned and libsignal-0.36 versioned zk secrets from sample bundle 2024-01-31 15:25:23 -08:00
Jon Chambers
26adf20ee8 Make "fetch profile" endpoints @ManagedAsync 2024-01-31 14:38:50 -06:00
Jon Chambers
72668ed0a2 Make "send individual message" endpoint @ManagedAsync 2024-01-31 14:38:50 -06:00
Ravi Khadiwala
50f1ed7851 Add a virtual-thread backed @ManagedAsyncExecutor 2024-01-31 14:38:50 -06:00
ravi-signal
cf8f2a3463 remove synchronized locks that may be held while blocking 2024-01-31 14:29:15 -06:00
ravi-signal
b483159b3a reintroduce VirtualThreadPinEventMonitor 2024-01-31 14:28:12 -06:00
ravi-signal
480abebf7e Offload account lock updates to accountLockExecutor 2024-01-31 14:27:16 -06:00
Ravi Khadiwala
b924dea045 Remove VirtualThreadPinEventMonitor 2024-01-30 13:49:39 -06:00
Ravi Khadiwala
2c1e7e5ed6 Disable flaky VirtualThreadPinEventMonitorTest 2024-01-30 13:29:58 -06:00
Ravi Khadiwala
4dfd74906c Add timeouts to VirtualThreadPinEventMonitorTest 2024-01-30 13:10:43 -06:00
Chris Eager
fdae6ad94f Update to the latest version of the spam filter 2024-01-30 12:53:01 -06:00
Chris Eager
c80225a18c Remove NoneHealthResponder 2024-01-30 12:51:43 -06:00
ravi-signal
0e6242373e Add a monitor for virtual thread pin events 2024-01-30 12:48:07 -06:00
ravi-signal
4305db5579 Update to aws sdk 2.23.8 and use AwsCrtHttpClient 2024-01-30 12:46:27 -06:00
ravi-signal
36e7772f74 Put some validation on to profile version strings
Co-authored-by: Jon Chambers <63609320+jon-signal@users.noreply.github.com>
2024-01-26 15:24:38 -06:00
Chris Eager
ca05df5172 Revert "Add support for secondary credentials for registration service"
This reverts commit 4b8fc2950f.
2024-01-26 11:42:35 -06:00
Chris Eager
422e8e6f3e Remove CommandStopListener 2024-01-26 11:31:04 -06:00
Jon Chambers
852b285d84 Configure and instantiate a ClientPublicKeys data store/manager 2024-01-26 10:29:59 -05:00
Jon Chambers
6c13193623 Add a data store for client public keys for transport-level authentication/encryption 2024-01-26 10:29:59 -05:00
Ravi Khadiwala
61809107c8 Update to the latest version of the spam filter 2024-01-25 13:27:13 -06:00
Ravi Khadiwala
6bda9d8604 Set destination account on the request context 2024-01-25 13:02:28 -06:00
Ravi Khadiwala
1428ca73de Make identity token fetcher more async friendly
After the identity token expires a subsequent call would do a blocking
operation to retrieve the new token. Since we're making use of an async
gRPC client, this tends to block a thread we don't want to be blocking
on.

Instead, switch to periodically refreshing the token on a dedicated
thread.
2024-01-25 13:01:21 -06:00
Katherine
498ace0488 Remove iOS special case handling logic for SEPA/iDEAL 2024-01-25 10:52:17 -08:00
Chris Eager
f082b95efb Update to the latest version of the spam filter 2024-01-24 15:01:35 -06:00
Chris Eager
4b8fc2950f Add support for secondary credentials for registration service 2024-01-24 13:52:23 -06:00
Jon Chambers
595cc55578 Retire the returnPqKey flag when fetching pre-keys 2024-01-23 16:01:40 -05:00
Ravi Khadiwala
91b0c368b4 Remove unused table configuration parameter 2024-01-23 14:48:52 -06:00
Jonathan Klabunde Tomer
21d0ffc990 Allow "reserving" one's own committed username hash 2024-01-23 12:44:55 -08:00
Chris Eager
55b9d84956 Add host to DogstatsdConfiguration 2024-01-22 15:59:52 -06:00
Chris Eager
ffdb0db6c6 Migrate from host tag to dd.internal.entity_id 2024-01-22 15:59:52 -06:00
Chris Eager
a5ed07a666 Support environment variable substitution in configuration 2024-01-22 11:25:18 -06:00
Chris Eager
da02c90bad Remove AWS instance profile credentials provider 2024-01-22 11:24:03 -06:00
Ravi Khadiwala
3820a231ec Strictly enforce ACI service identifier strings have no prefix 2024-01-22 11:23:33 -06:00
Dimitris Apostolou
408b065b9e Fix typos 2024-01-20 12:40:08 -05:00
hduelme
238ab84749 use StandardCharsets UTF_8 instead of String 2024-01-20 12:39:43 -05:00
Ravi Khadiwala
6894015986 Update to the latest version of the spam filter 2024-01-19 14:32:15 -06:00
Ravi Khadiwala
f5080f9bd6 Support configurable verification code sender overrides 2024-01-19 13:58:17 -06:00
Ravi Khadiwala
db4aa99ce0 Make the ServiceIdentifier interface sealed 2024-01-19 13:57:20 -06:00
ravi-signal
70134507f8 Add metric for ServiceId string parsing 2024-01-19 13:56:47 -06:00
Ravi Khadiwala
360a4793ae Remove global lock in RateLimitChallengeManager 2024-01-19 13:56:09 -06:00
Jon Chambers
47bfb25f2c Retire a now-unused transacational update method for accounts 2024-01-18 16:40:21 -05:00
Jon Chambers
b048b0bf65 Remove signed pre-keys from Device entities 2024-01-18 12:13:00 -05:00
Jonathan Klabunde Tomer
394f9929ad limit size of multi-recipient messages 2024-01-11 16:31:37 -08:00
ravi-signal
bf39be3320 Add Content-Type header for copy uploads 2024-01-11 14:59:35 -06:00
Chris Eager
4a2cbb9ec7 Stop timers on Flux termination 2024-01-11 14:57:31 -06:00
Ravi Khadiwala
cc6cf8194f Add media deletion endpoint 2024-01-11 10:19:06 -06:00
Ravi Khadiwala
e934ead85c Fix incorrect comparison in archive usage metric 2024-01-11 10:19:06 -06:00
Ravi Khadiwala
323bfd9a6e Remove some secondary keys from account cache
Remove e164, usernameHash, and usernameLink secondary mappings from the
accounts redis cache.
2024-01-11 10:10:32 -06:00
Jon Chambers
bf05e47e26 Get accounts asynchronously when checking SVR credentials via gRPC 2024-01-11 11:09:49 -05:00
Jon Chambers
d18f576239 Revert "Revert "Treat the stand-alone signed pre-keys table as the source of truth for signed pre-keys""
This reverts commit 3f9edfe597.
2024-01-10 11:22:10 -05:00
Jon Chambers
7d483c711a Revert "Revert "Retire "migrate signed pre-keys" configuration""
This reverts commit a024949311.
2024-01-10 11:22:10 -05:00
Jon Chambers
61256d49cd Revert "Revert "Retire the "migrate signed pre-keys" command""
This reverts commit f738bc97e7.
2024-01-10 11:22:10 -05:00
Jonathan Klabunde Tomer
184cdc0331 Remove two-stage check of username availability in reserve/confirm 2024-01-09 14:01:42 -08:00
Ravi Khadiwala
ed972a0037 Fix archive listing directory prefix order 2024-01-09 16:01:11 -06:00
Ravi Khadiwala
a62a6c1cb6 Change type of Cdn3StorageManagerConfiguration.clientSecret to SecretString 2024-01-08 16:06:54 -06:00
Ravi Khadiwala
ba0c6be3e3 Update to the latest version of the spam filter 2024-01-08 15:00:29 -06:00
Ravi Khadiwala
f66566aa17 Fix default configuratiton in MonitoredS3ObjectConfiguration 2024-01-08 14:52:14 -06:00
ravi-signal
b6ecfc7131 Add archive listing 2024-01-08 13:54:57 -06:00
adel-signal
460dc6224c Update to the latest version of the spam filter 2024-01-08 10:56:43 -08:00
adel-signal
2b688b1a60 Refactor spam filter's S3MonitoredObject to server parent module.
Allows other parts of server to use S3MonitoredObjects.
2024-01-08 10:34:12 -08:00
Jon Chambers
3c64d9292f Revert "Expand the default max packet size for Dogstatsd"
This reverts commit 0f52d2e464.
2024-01-08 12:23:16 -05:00
Jon Chambers
0f52d2e464 Expand the default max packet size for Dogstatsd 2024-01-08 11:43:50 -05:00
Jonathan Klabunde Tomer
1e5fadc440 Allow reserving a hash previously reserved (but not committed) by the same user 2024-01-05 15:38:45 -08:00
Chris Eager
f495ff483a Update RemoveExpiredLinkedDevicesCommand to retry failures 2024-01-05 15:38:34 -08:00
Jonathan Klabunde Tomer
4e3b1509a8 Update to the latest version of the spam filter 2024-01-04 12:20:50 -08:00
Jonathan Klabunde Tomer
d1a80cc880 fix build-breaking typo 2024-01-04 11:46:07 -08:00
Jonathan Klabunde Tomer
e1ad25cee0 Group Send Credential support in chat 2024-01-04 11:38:57 -08:00
Chris Eager
195f23c347 Add /v1/accounts and /v2/keys to the WebSocket 2024-01-02 15:51:57 -06:00
Chris Eager
ad6b99be6a Dispatch client presence operations after device deletion to a dedicated executor 2023-12-22 10:51:17 -06:00
Chris Eager
b9dd9fc47d Reduce fan-out by processing a single stream of expired linked devices 2023-12-22 10:51:06 -06:00
Ravi Khadiwala
19a8a80a30 Update to the latest version of the spam filter 2023-12-21 16:29:54 -06:00
Chris Eager
637792c6d4 Move RemoveExpiredLinkedDevicesCommand error handling for more accurate metrics 2023-12-21 16:06:41 -06:00
Jon Chambers
4d1bca2d97 Dispatch client presence operations after account deletion to a dedicated executor 2023-12-21 13:40:49 -05:00
Chris Eager
f33a2eba50 Add buffer + shuffle and configurable concurrency to RemoveExpiredLinkedDevicesCommand 2023-12-21 11:14:02 -06:00
Jon Chambers
5d6bea5ec9 Clear Redis cache entries strictly after removing accounts from DynamoDB 2023-12-21 12:08:03 -05:00
Jon Chambers
057d1f07a8 Remove bulk "set repeated-use signed pre-keys" methods because they were only ever used for single devices 2023-12-21 12:07:42 -05:00
Ravi Khadiwala
25c3f55672 Update documentation/integration-tests actions to java 21 2023-12-20 16:41:16 -06:00
Ravi Khadiwala
c9d4091c1e Update to the latest version of the spam filter 2023-12-20 16:38:20 -06:00
Ravi Khadiwala
1d55562dc3 Update to Java 21 2023-12-20 16:37:14 -06:00
Jon Chambers
95bb9a9780 Log signed pre-key retrieval errors and mismatches 2023-12-20 14:55:12 -05:00
Chris Eager
06c391cbf6 Add counter for failed account updates to RemoveExpiredLinkedDevicesCommand 2023-12-20 13:51:28 -06:00
Chris Eager
d90dff95b1 Move MeterRegistry#close calls to after the lifecycle has fully stopped 2023-12-20 13:51:16 -06:00
Ravi Khadiwala
c93972a322 Update to the latest version of the spam filter 2023-12-19 18:33:45 -06:00
Ravi Khadiwala
ca47a7b663 handle new RegistrationService proto error 2023-12-19 18:19:26 -06:00
Jonathan Klabunde Tomer
9d3d4a3698 Add phone-number-sharing field to versioned profile
Co-authored-by: Katherine <katherine@signal.org>
2023-12-19 11:20:04 -08:00
Chris Eager
3b509bf820 Add command to remove expired linked devices 2023-12-19 13:11:26 -06:00
Jon Chambers
5b7f91827a Remove signed pre-keys transactionally when removing devices 2023-12-19 14:11:05 -05:00
Chris Eager
a44491714c Update to the latest version of the spam filter 2023-12-18 14:55:39 -06:00
Chris Eager
06800043a9 Set TLS keystore password in secondary persistent services 2023-12-15 13:39:58 -06:00
Chris Eager
3090de56b8 Set TLS keystore password from secrets configuration 2023-12-15 12:47:27 -06:00
Katherine
a37acd1f42 Add ttl for braintree writes to onetime donation table 2023-12-15 13:37:35 -05:00
Jonathan Klabunde Tomer
372e3f83d2 Update libsignal to 0.37.0 2023-12-15 10:36:59 -08:00
Chris Eager
de260a2bef Update to the latest version of the spam filter 2023-12-15 12:23:01 -06:00
Jonathan Klabunde Tomer
e9a130f976 add versioned zkconfig secrets to sample secrets bundle 2023-12-14 17:38:49 -08:00
Jonathan Klabunde Tomer
43f17414ff Make key-setting methods asynchronous again 2023-12-14 16:49:16 -08:00
Jon Chambers
b259eea8ce Refactor/clarify account creation/reclamation process 2023-12-14 16:48:57 -08:00
Katherine
9cfc2ba09a Persist onetime donation payment success timestamps for Braintree transactions 2023-12-14 16:48:29 -08:00
Chris Eager
bb347999ce Propagate another subscription processor error to clients 2023-12-14 15:40:08 -05:00
Katherine
3548c3df15 Calculate onetime badge expiration from payment success timestamp 2023-12-14 15:39:46 -05:00
Jon Chambers
1167d0ac2e Make key-setting methods synchronous 2023-12-13 17:49:55 -05:00
Jon Chambers
f738bc97e7 Revert "Retire the "migrate signed pre-keys" command"
This reverts commit c7cc3002d5.
2023-12-13 17:49:55 -05:00
Jon Chambers
3f9edfe597 Revert "Treat the stand-alone signed pre-keys table as the source of truth for signed pre-keys"
This reverts commit feb933b4df.
2023-12-13 17:49:55 -05:00
Jon Chambers
a024949311 Revert "Retire "migrate signed pre-keys" configuration"
This reverts commit 44145073f1.
2023-12-13 17:49:55 -05:00
Jon Chambers
609c901867 Refactor key-fetching to be reactive 2023-12-13 12:46:48 -05:00
Jon Chambers
4ce060a963 Count wildcard "get keys" requests by platform 2023-12-13 12:46:48 -05:00
Jon Chambers
c4ca0fee40 Synchronize access to responseItems when assembling a "get keys" response 2023-12-13 12:46:48 -05:00
Chris Eager
8d4acf0330 Remove ForkJoinPool.managedBlock in favor of async updates 2023-12-13 10:18:04 -05:00
Jon Chambers
28a981f29f Assume that all devices have signed pre-keys 2023-12-13 10:17:51 -05:00
Jon Chambers
c29113d17a Reject requests with missing device capabilities 2023-12-12 11:37:44 -05:00
Katherine
951f978447 Use start of subscription period as fallback timestamp 2023-12-12 11:34:44 -05:00
Jon Chambers
07899f35bd Return DeviceController#linkDevice to synchronous processing 2023-12-12 11:34:23 -05:00
Katherine
3cbbf37468 Use payment success timestamp to calculate recurring donation badge expiration 2023-12-12 10:01:20 -05:00
Jon Chambers
6c7a3df5ae Retire non-atomic device-linking pathways 2023-12-12 09:53:51 -05:00
Jon Chambers
2054ab2771 Revert "Count requests with missing device capabilities"
This reverts commit 6cdf8ebd2c.
2023-12-12 09:53:51 -05:00
Jon Chambers
44145073f1 Retire "migrate signed pre-keys" configuration 2023-12-12 09:53:34 -05:00
Jon Chambers
feb933b4df Treat the stand-alone signed pre-keys table as the source of truth for signed pre-keys 2023-12-11 11:39:54 -05:00
Jon Chambers
c7cc3002d5 Retire the "migrate signed pre-keys" command 2023-12-11 11:07:00 -05:00
xiaolou86
049b901d63 Fix typos 2023-12-09 10:04:12 -05:00
Jon Chambers
3cf1b92dfc Do not set one-time pre-keys if the lists of new keys are empty 2023-12-08 14:03:08 -05:00
Jon Chambers
5b0fcbe854 Always expose sequential fluxes to account crawlers 2023-12-08 13:43:42 -05:00
Jon Chambers
cca747a1f6 Treat transaction conflicts during transactional account updates as contested optimistic locks 2023-12-08 12:13:23 -05:00
Jon Chambers
417d99a17e Check story rate limits in parallel 2023-12-08 12:13:08 -05:00
Jon Chambers
e9708b9259 Use random UUIDs throughout MessageControllerTest 2023-12-08 12:13:08 -05:00
Jon Chambers
e5d3be16b0 Fetch destination accounts in parallel when sending multi-recipient messages 2023-12-08 12:13:08 -05:00
Jordan Rose
2ab3c97ee8 Replace MultiRecipientMessage parsing with libsignal's implementation
Co-authored-by: Jonathan Klabunde Tomer <jkt@signal.org>
2023-12-08 11:52:47 -05:00
Jon Chambers
f20d3043d6 Process key migrations sequentially to better control concurrency 2023-12-07 21:42:49 -05:00
Jonathan Klabunde Tomer
4efda89358 multisend cleanup 2023-12-07 12:23:02 -08:00
Jon Chambers
1fb88271e5 Invalidate cache entries for accounts after successfully adding devices 2023-12-07 13:27:26 -05:00
Jon Chambers
a843780f68 Add a (failing!) test for device-linking 2023-12-07 13:27:26 -05:00
Jon Chambers
5ad83da4e0 Remove the now-unused admin logger 2023-12-07 12:58:43 -05:00
Jon Chambers
949cc9e214 Update to the latest version of the spam filter 2023-12-07 12:58:23 -05:00
Jon Chambers
50d92265ea Add devices to accounts transactionally 2023-12-07 11:19:40 -05:00
Chris Eager
e084a9f2b6 Remove PUT/DELETE methods from RemoteConfigController 2023-12-07 10:54:19 -05:00
Jon Chambers
664f9f36e1 Use padded base64 encoding for outbound device names 2023-12-07 10:53:56 -05:00
Jon Chambers
4c9efdb936 Revert "Revert "Represent device names as byte arrays""
This reverts commit 45848e7bfe.
2023-12-07 10:53:56 -05:00
Jon Chambers
45848e7bfe Revert "Represent device names as byte arrays"
This reverts commit 5ae2e5281a.
2023-12-06 17:05:04 -05:00
Ravi Khadiwala
4fa10e5783 Fix NPE in request metrics when a finished request is missing a response 2023-12-06 15:11:41 -05:00
Ravi Khadiwala
fc0bc85f4d Add some extra request validation to /v1/archives/keys 2023-12-06 15:11:27 -05:00
Jon Chambers
5ae2e5281a Represent device names as byte arrays 2023-12-06 15:10:45 -05:00
Jon Chambers
34a943832a Align push notification types and delivery priorities 2023-12-06 12:40:16 -05:00
Jon Chambers
db17693ba7 Revert "Temporarily make registration challenge notifications "noisy""
This reverts commit 9069c5abb6.
2023-12-06 12:40:16 -05:00
Jon Chambers
6cdf8ebd2c Count requests with missing device capabilities 2023-12-06 10:40:50 -05:00
Ravi Khadiwala
072b470f46 Be more specific about encodings in /v1/archive docs 2023-12-06 10:40:20 -05:00
Ravi Khadiwala
78b2df2ecc Use long instead of int for epoch seconds 2023-12-06 10:40:06 -05:00
Jon Chambers
51a825f25c Update to the latest version of the spam filter 2023-12-06 10:39:04 -05:00
Jon Chambers
00e72a30c9 Assume all accounts have primary devices 2023-12-06 10:28:19 -05:00
Ravi Khadiwala
69990c23a5 Avoid test dep in integration-test 2023-12-05 16:35:51 -06:00
Jon Chambers
df421e0182 Update signed pre-keys in transactions 2023-12-05 14:20:16 -05:00
Jon Chambers
ede9297139 Disallow identity key changes 2023-12-05 14:14:24 -05:00
Jon Chambers
85383fe581 Remove an unused "store keys" method 2023-12-05 14:14:24 -05:00
Jon Chambers
4cca7aa4bd Normalize identity types throughout KeysController 2023-12-05 14:14:24 -05:00
Jon Chambers
e2037dea6c Rename PreKeyState to SetKeysRequest 2023-12-05 14:14:24 -05:00
Jon Chambers
f10f772e94 Convert PreKeyState to a record 2023-12-05 14:14:24 -05:00
Jon Chambers
9ecfe15ac4 Use multiline strings for PreKeyState documentation 2023-12-05 14:14:24 -05:00
Jon Chambers
5f0726af8a Perform cleanup operations before overwriting an existing account record 2023-12-05 12:18:09 -05:00
Ravi Khadiwala
331bbdd4e6 replace deprecated apache RandomUtils 2023-12-05 10:48:18 -06:00
Jon Chambers
37e3bcfc3e Move "remove device" logic into AccountsManager 2023-12-05 11:44:58 -05:00
Jon Chambers
4f42c10d60 Disallow sync messages to PNIs 2023-12-05 11:44:27 -05:00
Jonathan Klabunde Tomer
20392a567b Revert "multisend cleanup"
This reverts commit c03249b411.
2023-12-01 14:39:31 -08:00
Jonathan Klabunde Tomer
c03249b411 multisend cleanup 2023-11-30 15:50:36 -08:00
gram-signal
22e6584402 Add auth controller for SVR3 to /v3/backup. 2023-11-30 15:50:21 -08:00
Chris Eager
c18aca9215 Update to the latest version of the spam filter 2023-11-29 16:08:31 -06:00
Jon Chambers
aa23a5422a Don't modify registration IDs from the "set account attributes" endpoint 2023-11-29 16:56:47 -05:00
Jon Chambers
01fde4f9ca Require PNI-associated registration IDs for new devices 2023-11-29 16:56:47 -05:00
Jon Chambers
3980dec123 Revert "Add metrics to check presence of PNI-associated registration IDs"
This reverts commit 1da3f96d10.
2023-11-29 16:56:47 -05:00
Jon Chambers
c97f837f45 Revert "Instrument registration ID changes via the "set account attributes" endpoint"
This reverts commit 8fbc1dac74.
2023-11-29 16:56:47 -05:00
Chris Eager
9c54d2407b Update tests 2023-11-29 15:48:24 -06:00
Chris Eager
a027c4ce1f Conditionally use request.remoteAddr instead of X-Forwarded-For 2023-11-29 15:48:24 -06:00
Chris Eager
b1fd025ea6 Use EpochSecondSerializer for UserRemoteConfigList.serverEpochTime 2023-11-29 15:47:10 -06:00
Jonathan Klabunde Tomer
a05a230085 use Account-specific json serializer when reserving username hash 2023-11-29 13:40:06 -08:00
Jon Chambers
8fbc1dac74 Instrument registration ID changes via the "set account attributes" endpoint 2023-11-29 10:26:41 -05:00
Katherine
f46842c6c9 Validate registration IDs 2023-11-28 15:43:35 -08:00
Ravi Khadiwala
8b95bb0c03 Only run integration tests if integration bucket is configured 2023-11-28 15:10:49 -06:00
ravi-signal
202dd8e92d Add copy endpoint to ArchiveController
Co-authored-by: Jonathan Klabunde Tomer <125505367+jkt-signal@users.noreply.github.com>
Co-authored-by: Chris Eager <79161849+eager-signal@users.noreply.github.com>
2023-11-28 11:45:41 -06:00
Jon Chambers
1da3f96d10 Add metrics to check presence of PNI-associated registration IDs 2023-11-28 12:26:15 -05:00
Chris Eager
5f6fe4d670 Add NoneHealthResponder to replace default responder 2023-11-27 14:38:47 -06:00
Chris Eager
a74438d1ee Add test for concurrent in-flight outbound messages on WebSocket queue processing
This also elevates the implicit default concurrency (via reactor’s `Queues.SMALL_BUFFER_SIZE`) to be explicit.
2023-11-27 14:37:46 -06:00
Jon Chambers
c8033f875d Create accounts transactionally 2023-11-27 09:57:43 -05:00
Jon Chambers
07c04006df Avoid blocking calls in async account updates 2023-11-27 09:57:43 -05:00
Jon Chambers
521900c048 Always require atomic account creation 2023-11-27 09:52:57 -05:00
Jon Chambers
9069c5abb6 Temporarily make registration challenge notifications "noisy" 2023-11-22 10:25:59 -05:00
Jon Chambers
ff7a5f471b Always use "conserve power" priority for challenge notifications 2023-11-21 17:58:32 -05:00
Ravi Khadiwala
42a47406cc Fix platform tag for push challenge metric 2023-11-21 17:57:32 -05:00
Katherine
de10b6de7b Add metric to count invalid registration ID when setting account attributes 2023-11-20 14:25:27 -08:00
Chris Eager
d6ade0e1ac Send HTTP 408 on idle timeout 2023-11-20 16:24:54 -06:00
Ravi Khadiwala
e04b5e5c9f run integration-test job daily at 19:30 UTC 2023-11-20 15:31:45 -06:00
Ravi Khadiwala
15a6c46d47 use a configured number in integration test 2023-11-20 15:02:53 -06:00
Jonathan Klabunde Tomer
cb1fc734c2 report exceptions in fanned-out sends of multi-recipient messages 2023-11-20 10:46:26 -08:00
Katherine Yen
db7f18aae7 Add missing boolean parameter to getSubscriptionStatus method call 2023-11-16 19:28:10 -05:00
Katherine
7fbc327591 For Braintree, return active subscription status if latest transaction also succeeded 2023-11-16 19:15:46 -05:00
Chris Eager
84b56ae1b2 Upgrade to dropwizard 3.0 2023-11-16 17:57:48 -06:00
Katherine
041aa8639a Enforce story ratelimit 2023-11-16 12:36:43 -05:00
Katherine
216ac72ad0 Use Braintree subscription status alongside transaction status to determine 204 and 402 responses for /v1/subscription/{subscriberId}/receipt_credentials 2023-11-16 12:36:19 -05:00
Jon Chambers
c85ddaeb9c Make DynamoDB client connection pool sizes configurable 2023-11-16 12:29:43 -05:00
Jonathan Klabunde Tomer
e09dec330a Update to the latest version of the spam filter 2023-11-15 17:34:57 -08:00
Jonathan Klabunde Tomer
8f7bae54fe When persisting messages fails due to a full queue in DynamoDB, automatically unlink one device to free up room.
Co-authored-by: Chris Eager <79161849+eager-signal@users.noreply.github.com>
2023-11-15 17:15:17 -08:00
Jon Chambers
ce60f13320 Don't track IPv4/IPv6 metrics at the application level
We can measure this at the infrastructure level without observing IPs directly.
2023-11-15 17:18:54 -05:00
Jon Chambers
1ac0140666 Remove a couple of now-unused variables 2023-11-15 17:12:08 -05:00
Jon Chambers
6cc8b147a9 Remove unused X-Forwarded-For parameters 2023-11-15 17:09:47 -05:00
Jon Chambers
e078161e2f Consistently use HttpHeaders.X_FORWARDED_FOR 2023-11-15 17:09:36 -05:00
Jonathan Klabunde Tomer
7764185c57 return explicit Response rather than Void from async controllers with no expected body content 2023-11-14 21:57:25 -08:00
Katherine
d4ef2adf0a Remove low priority attempt login notification workaround for old iOS clients 2023-11-13 12:06:55 -05:00
Jonathan Klabunde Tomer
a83378a44e add an option to replace username ciphertext without rotating the link handle 2023-11-13 09:01:54 -08:00
ravi-signal
a4a4204762 Allow re-registered accounts to reclaim their usernames 2023-11-13 10:41:23 -06:00
Jon Chambers
acd1140ef6 Make registration challenge APNs payloads "silent" 2023-11-13 08:39:58 -08:00
Jon Chambers
fbf71c93ff Make rate limit challenge APNs payloads "silent" 2023-11-13 08:39:58 -08:00
Katherine
38bc0c466a Change sepaMaximumEuros field to number in JSON response 2023-11-10 10:16:03 -08:00
Katherine
71e4351743 Add sepaMaximumEuros field to subscription configuration 2023-11-10 09:13:51 -08:00
Katherine
387e4b94b4 Expand charge object on Stripe subscription to surface charge failure information 2023-11-10 09:12:59 -08:00
Katherine
201c76b861 Add charge failure details to /v1/subscription/{subscriberId}/receipt_credentials 402 response 2023-11-08 10:54:14 -08:00
Chris Eager
1c3aa87ca6 Update to the latest version of the spam filter 2023-11-06 10:11:41 -06:00
Sergey Skrobotov
db63ff6b88 gRPC validations 2023-11-03 11:30:48 -07:00
Katherine
115431a486 Un-hardcode payment activation flag 2023-11-03 11:27:34 -07:00
Jonathan Klabunde Tomer
d47ff9b7c7 don't make empty transactions 2023-11-02 16:20:19 -07:00
Chris Eager
b0818148cf Update to the latest version of the spam filter 2023-11-02 11:48:50 -05:00
Chris Eager
2bc4412d66 Encapsulate device ID in ProvisioningAddress 2023-11-02 11:48:10 -05:00
Chris Eager
6a428b4da9 Convert Device.id from long to byte 2023-11-02 11:48:10 -05:00
Jonathan Klabunde Tomer
7299067829 Don't attempt to update PNI PQ prekeys for disabled devices 2023-11-01 16:55:55 -07:00
Chris Eager
5659cb2820 Update to the latest version of the spam filter 2023-11-01 15:53:26 -05:00
Chris Eager
570aa4b9e2 Remove several unused classes 2023-11-01 15:46:10 -05:00
Chris Eager
c4079a3b11 Update to the latest version of the spam filter 2023-11-01 10:07:42 -05:00
Ravi Khadiwala
6b38b538f1 Add ArchiveController
Adds endpoints for creating and managing backup objects with ZK
anonymous credentials.
2023-10-30 14:02:19 -05:00
Chris Eager
ba139dddd8 Use all devices when checking limit 2023-10-30 12:40:06 -05:00
Chris Eager
38b581a231 Update to the latest version of the spam filter 2023-10-27 10:22:50 -05:00
Chris Eager
3c2675b41a Update libphonenumber to 8.13.23 2023-10-27 09:26:40 -05:00
Chris Eager
0f5c62ade5 Set max threads = min threads on command executor services 2023-10-27 09:26:32 -05:00
Jon Chambers
54bc3bce96 Add an authentication-required gRPC service for working with accounts 2023-10-25 14:47:20 -04:00
Jon Chambers
3d92e5b8a9 Explicitly stop and start managed dependencies 2023-10-24 16:50:02 -04:00
Chris Eager
325d145ac3 Update to the latest version of the spam filter 2023-10-24 14:33:31 -05:00
Chris Eager
b0654a416a Update maven plugins 2023-10-24 14:32:55 -05:00
Chris Eager
19930ec2e4 Update dependencies
- AWS: 2.20.130 → 2.21.5
- Braintree: 3.25.0 → 3.27.0
- commons-csv: 2.13.0 → 2.14.0
- dropwizard: 2.1.7 → 2.1.9
- Google libraries BOM: 26.22.0 → 26.25.0
- grpc: 1.56.1 → 1.58.0
- kotlin: 1.9.0 → 1.9.10
- protobuf: 3.23.2 → 3.24.3
- stripe: 23.1.1 → 23.10.0
- junit-pioneer: 2.0.1 → 2.1.0
- firebase-admin: 9.1.1 → 9.2.0
- swagger-jaxrs2: 2.2.8 → 2.2.17
- java-uuid-generator: 4.2.0 → 4.3.0
- log4j: 2.17.0 → 2.21.0
- reactor-bom: 2022.0.10 → 2022.0.12
2023-10-24 14:32:55 -05:00
Jon Chambers
e4de6bf4a7 Only update devices that aren't already disabled 2023-10-24 15:29:03 -04:00
Jon Chambers
21125c2f5a Update to the latest version of the spam filter 2023-10-20 16:38:04 -04:00
Katherine Yen
6f166425fe Fix bank mandate test 2023-10-20 16:19:31 -04:00
Chris Eager
cf2353bcf9 Remove InstrumentedExecutorService wrapping 2023-10-20 15:14:35 -05:00
Jon Chambers
744eb58071 Discard old chunk-based account crawler machinery 2023-10-20 16:09:17 -04:00
Jon Chambers
9d47a6f41f Introduce a reactive push notification feedback processor 2023-10-20 16:09:17 -04:00
Jonathan Klabunde Tomer
4f4c23b12f Update to the latest version of the spam filter 2023-10-20 09:39:46 -07:00
Jonathan Klabunde Tomer
fb02815c27 Update to the latest version of the spam filter 2023-10-20 09:12:37 -07:00
Jonathan Klabunde Tomer
fd19299ae0 Accept a captcha score threshold for challenges from the spam filter 2023-10-20 09:09:22 -07:00
Jon Chambers
9c053e20da Drop Util#isEmpty/Util#nonEmpty in favor of StringUtils 2023-10-20 12:04:15 -04:00
Jon Chambers
19d7b5c65d Drop Util#wait 2023-10-20 12:04:15 -04:00
Jon Chambers
7b9d8829da Remove entirely unused Util methods 2023-10-20 12:04:15 -04:00
Jon Chambers
3505ac498c Update to the latest version of the spam filter 2023-10-20 10:52:43 -04:00
Jon Chambers
f0ab52eb5d Rename "master device" to "primary device" 2023-10-20 10:52:13 -04:00
Jon Chambers
e8cebad27e Avoid modifying original Account instances when constructing JSON for updates 2023-10-20 10:51:50 -04:00
Jon Chambers
6441d5838d Clear username links in the same transaction when clearing username hashes 2023-10-20 10:51:50 -04:00
Jon Chambers
ac0c8b1e9a Introduce a canonical constant for UAK length 2023-10-20 10:50:44 -04:00
Katherine
8ec062fbef Define an endpoint to set the default payment method for iDEAL subscriptions 2023-10-19 10:29:40 -07:00
Katherine
5990a100db Add charge failure details to /v1/subscription/boost/receipt_credential 402 response 2023-10-19 10:21:26 -07:00
Jon Chambers
bc35278684 Drop the old AccountCleaner 2023-10-19 10:34:24 -04:00
Jon Chambers
c3c7329ebb Add a single-shot command for removing expired accounts 2023-10-19 10:34:24 -04:00
Jon Chambers
6fd1c84126 Make command namespace available to subclasses 2023-10-19 10:34:24 -04:00
Jon Chambers
0100f0fcc9 Migrate a username links test from AccountsTest to AccountsManagerUsernameIntegrationTest 2023-10-18 10:20:48 -04:00
Jon Chambers
0cdc32cf65 Really REALLY fix instrumentation for re-registration of recently-deleted accounts 2023-10-18 10:15:03 -04:00
Jon Chambers
601e9eebbd Implement an anonymous account service for looking up accounts 2023-10-18 10:14:52 -04:00
Jon Chambers
eaa868cf06 Add a remote address interceptor to base gRPC tests 2023-10-18 10:14:52 -04:00
Jon Chambers
f55504c665 Add utility methods for rate-limiting by remote address 2023-10-18 10:14:52 -04:00
Katherine Yen
b2ff016cc1 Add back story ratelimiter with counter but do not enforce 2023-10-17 12:22:17 -04:00
Jon Chambers
33b4f17945 Make username-related operations asynchronous 2023-10-17 12:21:52 -04:00
Jon Chambers
e310a3560b Remove unused configuration for the legacy Secure Backup Service 2023-10-17 12:21:14 -04:00
Jon Chambers
162b27323e Fix instrumentation for re-registration of recently-deleted accounts 2023-10-17 12:20:58 -04:00
Jon Chambers
ae976ef8d6 Retire legacy Secure Value Recovery plumbing 2023-10-13 15:32:41 -04:00
Katherine
c6b4e2b71d Support iDEAL 2023-10-12 09:54:05 -07:00
Jon Chambers
33c8bbd0ce Trim stale capabilities from the profiles gRPC service 2023-10-12 12:52:32 -04:00
Jon Chambers
f2a3b8dba4 Treat APNs team/key IDs as secrets so they can change atomically with the key itself 2023-10-12 12:52:13 -04:00
Katherine
207ae6129b Add paymentMethod and paymentProcessing fields to GET /v1/subscription/{subscriberId} endpoint 2023-10-10 09:56:50 -07:00
Katherine
e1aa734c40 Define endpoint to get localized bank mandate text 2023-10-05 09:53:33 -07:00
Jonathan Klabunde Tomer
9b1b03bbfa Update to the latest version of the spam filter 2023-10-05 09:46:27 -07:00
Jon Chambers
bb7e0528c4 Make account deletion an asynchronous operation 2023-10-04 10:44:50 -04:00
Jonathan Klabunde Tomer
010eadcd10 UnlinkDeviceCommand improvements 2023-10-03 15:14:02 -07:00
Katherine
c43e0b54f2 Exclude SEPA_DEBIT as a supported payment method for certain iOS client versions 2023-10-03 11:34:52 -07:00
Chris Eager
6522b74e20 Remove obsolete metrics 2023-10-03 11:42:25 -05:00
Chris Eager
8c7975d89a Clear presence only if the connection’s displacement listener is still present 2023-10-03 11:42:25 -05:00
Chris Eager
407070c9fc Unsubscribe from keyspace notifications only if queue still maps to the listener 2023-10-03 11:42:25 -05:00
Katherine
7821a3cd61 Accommodate PayPal with SEPA changes 2023-09-28 10:28:17 -07:00
Katherine
a00c2fcfdb Support SEPA 2023-09-28 08:26:01 -07:00
Jonathan Klabunde Tomer
9cd21d1326 count ItemCollectionSizeLimitExceededExceptions persisting messages 2023-09-27 10:58:28 -07:00
Jonathan Klabunde Tomer
aaba95f9b8 return null for empty username hash in AccountIdentityResponse 2023-09-27 10:58:04 -07:00
Chris Eager
8d1135a2a3 Refine RegistrationController logic
Local device transfer on iOS uses the `409` status code to prompt the
transfer UI. This needs to happen before sending a `423` and locking
an existing account, since the device transfer
includes the local device database verbatim.
2023-09-25 15:54:31 -05:00
Jon Chambers
f9fabbedce Convert SubscriptionController request/response entities to records 2023-09-25 12:32:49 -07:00
Chris Eager
16012e6ffe Remove obsolete ManagedPeriodicWork 2023-09-25 12:15:17 -07:00
Jon Chambers
d10a132b0c Remove unused methods in SubscriptionController 2023-09-25 12:14:56 -07:00
Sergey Skrobotov
0b3af7d824 gRPC API for external services credentials service 2023-09-25 12:14:49 -07:00
Sergey Skrobotov
d0fdae3df7 Enable header-based auth for WebSocket connections 2023-09-25 12:14:40 -07:00
Ravi Khadiwala
a263611746 editorconfig: keep_simple_classes_in_one_line 2023-09-25 10:10:44 -05:00
Chris Eager
0e989419c6 Add metric for late removal of message availability and displacement listeners 2023-09-19 12:04:24 -05:00
ravi-signal
0fa8276d2d retry hCaptcha errors
Co-authored-by: Jon Chambers <63609320+jon-signal@users.noreply.github.com>
2023-09-14 16:07:35 -05:00
Ravi Khadiwala
b594986241 Set an idle timeout on registration gRPC client 2023-09-14 16:06:49 -05:00
Sergey Skrobotov
9f3ffa3707 gRPC API for payments service 2023-09-14 11:12:00 -07:00
Jonathan Klabunde Tomer
8e598c19dc don't attempt to update KEM prekeys if we have no PQ-enabled devices 2023-09-14 11:11:22 -07:00
Katherine
2601d6e906 Convert some fields on CreateProfileRequest and VersionedProfileResponse to byte arrays 2023-09-13 14:00:03 -07:00
Jon Chambers
de41088051 Update to WireMock 2.35.1 2023-09-13 16:56:15 -04:00
Jon Chambers
f2752b2a02 Update to the latest version of the spam filter 2023-09-13 16:02:46 -04:00
Jon Chambers
f0544fab89 Update recently-deleted accounts table transactionally as part of account mutations 2023-09-13 16:02:19 -04:00
Jon Chambers
1b9bf01ab1 Absorb DeletedAccounts into Accounts 2023-09-13 16:02:19 -04:00
Ravi Khadiwala
9945367fa1 Update to the latest version of the spam filter 2023-09-11 15:19:10 -05:00
Katherine
cbc3887226 Define identity key check endpoint in keys anonymous service 2023-09-11 11:57:00 -07:00
Ravi Khadiwala
c11b74e9c0 Update to the latest version of the spam filter 2023-09-11 13:37:07 -05:00
Jon Chambers
2b764c2abd Don't allow callers to unlink their primary device 2023-09-11 14:29:48 -04:00
Jon Chambers
845fc338d7 Add a (failing) test for removing primary devices from accounts 2023-09-11 14:29:48 -04:00
Sergey Skrobotov
977243ebfd DRY gRPC tests, refactor error mapping 2023-09-08 17:12:08 -07:00
Chris Eager
29ca544c95 Revert "Set suppressCancel=true in Mono.fromFuture"
This reverts commit 8348263fab.
2023-09-07 17:03:33 -05:00
Ravi Khadiwala
94b41d3a2c Fixup default rate limits
A previous refactor left the default rate limits off by a factor of 60.
2023-09-07 16:07:42 -05:00
Chris Eager
92bb783cbb Use static exception instance when a connection is closed 2023-09-07 16:06:16 -05:00
Chris Eager
8348263fab Set suppressCancel=true in Mono.fromFuture 2023-09-07 16:06:03 -05:00
Ravi Khadiwala
48f633de11 Fix type for comparison in integration test 2023-09-07 14:41:29 -05:00
Ravi Khadiwala
b3b9a629f3 Update to the latest version of the spam filter 2023-09-07 11:18:48 -05:00
Ravi Khadiwala
5934b7344a Remove unused captcha configuration 2023-09-07 11:16:32 -05:00
Chris Eager
a9a2e40fed Move onErrorResume to individual sendMessage Mono 2023-09-07 11:15:57 -05:00
Chris Eager
656326355a Invert String.equals() to prevent NullPointerException 2023-09-07 11:14:36 -05:00
Chris Eager
b89e2e5355 Propagate certain subscription processor errors to client responses 2023-09-06 15:57:14 -05:00
Chris Eager
2d187abf13 Handle WebSocket sendMessage errors with onErrorResume 2023-09-06 15:53:01 -05:00
Chris Eager
b701412295 Update maven-wrapper.properties 2023-09-06 15:48:27 -05:00
Jonathan Klabunde Tomer
b4dad81220 Update to the latest version of the spam filter 2023-09-05 13:55:07 -07:00
Jonathan Klabunde Tomer
6bccdad998 Update to the latest version of the spam filter 2023-09-05 10:23:39 -07:00
Chris Eager
ecd6b0174a Add timeouts to crawl chunk join()s 2023-08-31 15:03:19 -05:00
Chris Eager
a1e534a515 Add default request timeout to FaultTolerantHttpClient 2023-08-31 15:03:19 -05:00
Sergey Skrobotov
ebbe19ba63 Add missing copyright headers and reorder some imports 2023-08-30 16:07:53 -07:00
Katherine Yen
6a37b73463 Profile gRPC: Define getExpiringProfileKeyCredential endpoint 2023-08-30 14:56:43 -07:00
Katherine Yen
dd18fcaea2 Profile gRPC: Define getVersionedProfile endpoint 2023-08-30 14:47:11 -07:00
Katherine Yen
5afc058f90 Profile gRPC: Define getUnversionedProfile endpoint 2023-08-30 14:24:43 -07:00
Jon Chambers
5e221fa9a3 Tests for validation of Kyber keys on PNI change/key distribution events
Co-authored-by: Jonathan Klabunde Tomer <jkt@signal.org>
2023-08-30 14:07:33 -07:00
Jon Chambers
0e0cb4d422 Drop the non-normalized account crawler 2023-08-30 13:55:41 -04:00
Jonathan Klabunde Tomer
09f6d60ae9 Update to the latest version of the spam filter 2023-08-29 15:52:42 -07:00
Jonathan Klabunde Tomer
9577d552c6 pass challenge type to rate limit reset listeners 2023-08-29 15:19:49 -07:00
Chris Eager
093f17dce2 Update to stripe-java 23.1.1 2023-08-29 15:18:16 -07:00
Jon Chambers
6089f49b9c Add a gRPC interceptor for getting client addresses 2023-08-29 15:18:06 -07:00
Sergey Skrobotov
cfb910e87e Adding copyright headers to proto files 2023-08-28 14:39:33 -07:00
Ravi Khadiwala
376cffc61d Update to the latest version of the spam filter 2023-08-25 16:49:05 -05:00
Chris Eager
d338ba5152 Convert some KeysController methods return CompletableFutures 2023-08-24 11:59:28 -05:00
Chris Eager
f181397664 Add test for round-trip AccountsManager JSON serialization 2023-08-24 11:18:01 -05:00
Chris Eager
708f23a2ee Remove deprecated identity key and signed pre-key methods 2023-08-24 11:18:01 -05:00
Chris Eager
2d1a979eba Update libphonenumber to 8.13.19 2023-08-24 11:07:18 -05:00
Chris Eager
ee0be92967 Update to the latest version of the spam filter 2023-08-24 11:06:30 -05:00
Chris Eager
7536b75508 Remove unused test fixtures 2023-08-24 11:06:11 -05:00
Jonathan Klabunde Tomer
7237ae6c54 check that pq last-resort prekeys, if submitted, match device list 2023-08-24 09:04:29 -07:00
Sergey Skrobotov
ca05753a3e adding 400 response documentation to the API call 2023-08-23 13:20:07 -07:00
Chris Eager
9ca8503eac Downgrade to stripe-java 22.30.0 2023-08-22 16:31:46 -05:00
Jon Chambers
754f71ce00 Add a gRPC service for working with devices 2023-08-22 16:31:02 -05:00
Jon Chambers
619b05e56c Add utility a method for requiring authentication with the account's primary device 2023-08-22 16:31:02 -05:00
Jon Chambers
8b13826949 Convert DeviceInfo and DeviceInfoList to a record 2023-08-22 16:31:02 -05:00
Jon Chambers
a96ee57c7e Defer asynchronous actions when deriving Mono instances from futures 2023-08-22 16:28:02 -05:00
Jon Chambers
ff1ef90a6d Defer actions taken after rate limit checks 2023-08-22 16:28:02 -05:00
Chris Eager
22905fa8ee Downgrade logstash-logback-encoder to 7.3 2023-08-21 12:44:02 -05:00
Chris Eager
9e218ddd1c Update to the latest version of the spam filter 2023-08-21 11:42:11 -05:00
Chris Eager
6f0462622b Update maven and various plugins 2023-08-21 11:34:08 -05:00
Chris Eager
2f17161163 Update various dependencies 2023-08-21 11:34:08 -05:00
Ravi Khadiwala
17d48b95ac keep lettuce metrics; strip remote tags 2023-08-18 16:28:19 -05:00
Chris Eager
eeea97e2fe Return a single OAuth2 credentials JSON 2023-08-18 16:16:31 -05:00
Chris Eager
360e101660 Update to the latest version of the spam filter 2023-08-18 16:13:30 -05:00
Jon Chambers
3501a944a3 Update to the latest version of the spam filter 2023-08-18 11:49:11 -04:00
Jon Chambers
76305190a2 Temporarily restore explicit service/version/environment/host tags 2023-08-17 18:30:59 -04:00
Jon Chambers
ab83990170 Send latency metrics as distributions 2023-08-17 17:10:16 -04:00
Jon Chambers
8103a22026 Submit Micrometer metrics via dogstatsd instead of the Datadog API 2023-08-17 17:01:36 -04:00
Jonathan Klabunde Tomer
1f8e4713ef limit concurrency of async DynamoDB ops 2023-08-17 13:56:09 -07:00
Katherine Yen
ff9fe2c1be Remove record equality test 2023-08-17 13:55:27 -07:00
Jon Chambers
7f37c8ee5e Retire now-unused HTTP transport configuration for Datadog metric reporter 2023-08-17 16:53:53 -04:00
Jon Chambers
ed0a723fef Include underlying exceptions when logging failures to write exit files 2023-08-17 12:32:45 -04:00
Jon Chambers
5c31ef43c9 Send an HTTP/440 response instead of an HTTP/502 if an upstream provider rejects a "send verification code" request 2023-08-17 12:15:00 -04:00
Katherine Yen
43fd8518c0 Add missing java.util.Base64 import to ProfileController 2023-08-16 14:02:53 -07:00
Katherine Yen
19a08f01e8 Write certain profile data as bytes instead of strings to dynamo and represent those fields as byte arrays on VersionedProfile 2023-08-16 13:45:16 -07:00
Jonathan Klabunde Tomer
33498cf147 Update to the latest version of the spam filter 2023-08-16 10:19:00 -07:00
Jon Chambers
beeb85cf8d Update to the latest version of the spam filter 2023-08-15 14:21:00 -04:00
Jon Chambers
ccd860207b Make MessagesManager#clear asynchronous 2023-08-15 14:08:16 -04:00
Jon Chambers
2c835b5c51 Make message deletion from DynamoDB asynchronous 2023-08-15 14:08:16 -04:00
Jon Chambers
5caa951c61 Make MessagesCache#clear methods asynchronous 2023-08-15 14:08:16 -04:00
Jon Chambers
4d8c4d6693 Also delete APNs VOIP tokens when clearing APNs tokens 2023-08-15 14:08:00 -04:00
Jon Chambers
a9d0574ea8 Remove most @Timed annotations 2023-08-15 14:06:31 -04:00
Jonathan Klabunde Tomer
3954494eae Update to the latest version of the spam filter 2023-08-11 15:11:58 -07:00
Ravi Khadiwala
ed6a2c55eb adjust lettuce metric denial for post-transform name 2023-08-11 09:43:41 -05:00
Ravi Khadiwala
b6ee074149 fix captcha shortening url path resolution 2023-08-10 16:01:56 -05:00
Ravi Khadiwala
f6b3500e92 remove most high cardinality lettuce metrics 2023-08-10 16:01:16 -05:00
Katherine Yen
a71dc48b9b Prepare to read profile data stored as byte arrays 2023-08-10 14:00:35 -07:00
Katherine Yen
bc5eed48c3 Add authentication interceptor to profile gRPC service 2023-08-10 13:59:46 -07:00
Jon Chambers
2ecf3cb303 Revert "Don't immediately require PNI-associated keys for "atomic" device linking"
This reverts commit 4ec97cf006.
2023-08-10 16:59:35 -04:00
Jon Chambers
bed33d042a Revert "Require PNI-associated keys if the target account has a PNI identity key"
This reverts commit 1dde612855.
2023-08-10 16:59:35 -04:00
Jonathan Klabunde Tomer
d7975626be Update to the latest version of the spam filter 2023-08-10 09:58:26 -07:00
Ravi Khadiwala
3ac7aba6b2 Add a captcha short-code expander 2023-08-09 12:41:31 -05:00
Jon Chambers
1dde612855 Require PNI-associated keys if the target account has a PNI identity key 2023-08-09 12:10:56 -04:00
Jon Chambers
4ec97cf006 Don't immediately require PNI-associated keys for "atomic" device linking 2023-08-09 12:10:56 -04:00
Jon Chambers
d51c6fd2f8 Convert Device.Capabilities to a record 2023-08-08 15:38:37 -04:00
Jon Chambers
d868e3075c Retire fully-adopted device capabilities 2023-08-08 15:38:37 -04:00
Jon Chambers
ae61ee5486 Retire AnalyzeDeviceCapabilitiesCommand 2023-08-08 15:38:37 -04:00
Katherine Yen
58fd9ddb27 Count profile data that cannot be parsed as base64 2023-08-08 10:54:25 -07:00
Katherine Yen
a953cb33b7 Define ProfileController protobufs and setProfile endpoint 2023-08-08 10:53:11 -07:00
Jon Chambers
95b90e7c5a Add a preliminary gRPC service for dealing with calling credentials 2023-08-08 12:46:55 -04:00
Jon Chambers
6a3ecb2881 Convert TurnToken to a record 2023-08-08 12:46:55 -04:00
Jon Chambers
6cf4241283 Add a reactive method for checking rate limits by UUID 2023-08-08 12:46:55 -04:00
Jon Chambers
42141e51a1 Use ACIs instead of E164s for TURN URI overrides 2023-08-08 12:46:55 -04:00
Jon Chambers
b01945ff50 Clarify parameterized tests by modifying prototype request objects; remove spurious warning suppressions 2023-08-08 10:33:29 -04:00
Jon Chambers
a131f2116f Retire verification code storage machinery 2023-08-04 17:26:55 -04:00
Jon Chambers
625637b888 Stop checking for stored verification codes when linking devices 2023-08-04 17:26:55 -04:00
Jon Chambers
c873f62025 Produce verification tokens instead of stored verification codes for linking devices 2023-08-04 16:04:47 -04:00
Jon Chambers
43d91e5bd6 Convert VerificationCode to a record 2023-08-04 16:04:47 -04:00
Jon Chambers
5c4c729703 Disallow reuse of device verification tokens 2023-08-04 13:40:37 -05:00
Jon Chambers
308da3343d Accept signed tokens in addition to randomly-generated codes for authorizing device linking 2023-08-04 13:40:37 -05:00
Chris Eager
48c7572dd5 Add CommandStopListener 2023-08-04 13:29:35 -05:00
Ravi Khadiwala
dc5f35460b Update to the latest version of the spam filter 2023-08-04 11:38:33 -05:00
Jon Chambers
69ea9b0296 Add a request counter tagged by client version 2023-08-04 12:16:48 -04:00
Jon Chambers
969c6884c0 Add a command for analyzing device capabilities 2023-08-04 12:14:08 -04:00
Jon Chambers
fcf311aab3 Retire the PendingAccounts table 2023-08-04 12:13:57 -04:00
ravi-signal
888879dfb2 Estimate message byte limit exceeded error count 2023-08-04 11:10:58 -05:00
Chris Eager
e003197f77 Update to protobuf-java 3.23.3 2023-08-03 17:09:31 -05:00
Chris Eager
f57910cd97 Update to dropwizard 2.1.7, jackson 2.13.5 2023-08-03 16:18:27 -05:00
Chris Eager
d85e25dba0 Update to the latest version of the spam filter 2023-08-02 16:17:22 -05:00
Chris Eager
89a4034fc6 Remove s3-upload from deploy phase 2023-08-02 16:16:27 -05:00
Chris Eager
f53743d287 Add configuration for Datadog UDP transport 2023-08-02 13:54:15 -05:00
Jon Chambers
2d132128e1 Switched to a composed request object model for anonymous keys gRPC operations 2023-07-28 14:20:24 -05:00
Chris Eager
6e5ffbe7b5 Restore aci field to BatchIdentityCheckRequest 2023-07-28 14:16:48 -05:00
Jonathan Klabunde Tomer
a81c9681a0 Update to the latest version of the spam filter 2023-07-26 14:57:18 -07:00
Ravi Khadiwala
baf98accd0 acquire lock before checking message listeners in MessagesCache 2023-07-26 16:45:53 -04:00
Ravi Khadiwala
901c950ee6 Add metrics to keyspace-notifier executor 2023-07-26 16:45:53 -04:00
Ravi Khadiwala
50ac7f9dc2 adjust messageDeletionAsyncExecutor core pool size 2023-07-26 16:45:41 -04:00
Jon Chambers
c2ea4a5290 Update to the latest version of the spam filter 2023-07-26 16:45:13 -04:00
Jon Chambers
b691b8d37d Log successful client version refreshes 2023-07-26 16:41:54 -04:00
Jon Chambers
4ead8527c8 Use ClientReleasesManager when deciding whether to add client version tags 2023-07-26 16:41:54 -04:00
Jon Chambers
6f4801fd6f Add a manager class for checking "liveness" of client versions 2023-07-26 16:41:54 -04:00
Jon Chambers
10689843b0 Add a repository for client release information 2023-07-26 16:41:54 -04:00
Chris Eager
60cc0c482e Add @Produces to PUT /v1/accounts/apn 2023-07-26 16:35:23 -04:00
Jon Chambers
e1a5105c28 Revert "Restore max concurrency when migrating pre-keys"
This reverts commit ed8a1ed579.
2023-07-26 12:56:35 -04:00
Jon Chambers
ed8a1ed579 Restore max concurrency when migrating pre-keys 2023-07-26 12:34:32 -04:00
Jon Chambers
c3fd2e2284 Retry key storage attempts when migrating signed pre-keys 2023-07-26 12:34:32 -04:00
Chris Eager
872ef5d0a0 Add environment variable to toggle tcp appender 2023-07-24 13:13:13 -05:00
Chris Eager
b44599cd59 Remove unused jedis library 2023-07-24 10:54:34 -04:00
Jordan Rose
7a5dcc700e Add support for AuthCredentialAsPni with pniAsServiceId=true
Update to libsignal 0.30.0, and add a new query param to
/v1/certificate/auth/group, "pniAsServiceId=true", that uses the new
encoding of PNIs in zkgroup rather than encoding PNIs the same way as
ACIs, as we have been doing.

Also includes all the adjustments for the libsignal 0.30.0 update.
2023-07-24 10:53:59 -04:00
ravi-signal
705fb93e45 Add v4 attachment controller
Add AttachmentControllerV4 which can be configured to generate upload
forms for a TUS based CDN
2023-07-21 12:09:45 -05:00
Jon Chambers
9df923d916 Update keys gRPC endpoint to use service identifiers 2023-07-21 13:03:01 -04:00
Chris Eager
dc1cb9093a Remove unused code 2023-07-21 11:08:32 -05:00
Jon Chambers
e32043ae79 Remove outdated documentation 2023-07-21 10:24:17 -04:00
Jon Chambers
881c921d56 Update to the latest version of the spam filter 2023-07-21 09:44:53 -04:00
Jon Chambers
abb32bd919 Introduce "service identifiers" 2023-07-21 09:34:10 -04:00
Katherine Yen
4a6c7152cf Update to the latest version of the spam filter 2023-07-20 14:37:12 -07:00
Sergey Skrobotov
cf92007f66 Moving Account serialization logic to storage-specific classes 2023-07-20 14:28:07 -07:00
Jon Chambers
f5c57e5741 Make ContestedOptimisticLockException extend NoStackTraceRuntimeException 2023-07-20 11:15:08 -04:00
Jon Chambers
5627209fdd Add a gRPC service for working with pre-keys 2023-07-20 11:10:26 -04:00
Jonathan Klabunde Tomer
0188d314ce minor username api refinements 2023-07-19 15:12:47 -07:00
Jonathan Klabunde Tomer
67343f6bdc accept encrypted username with confirm-username-hash requests 2023-07-19 10:54:11 -07:00
Katherine Yen
ade2e9c6cf Define asynchronous ProfilesManager operations 2023-07-19 10:43:58 -07:00
Sergey Skrobotov
352e1b2249 test classes moved to same packages with components they test 2023-07-17 13:34:58 -07:00
Jon Chambers
b8d8d349f4 Control inbound message byte limits with a dynamic configuration flag 2023-07-14 16:25:33 -04:00
Jon Chambers
e87468fbe0 Add a rate limit for inbound message bytes for a given account 2023-07-14 16:25:33 -04:00
Jon Chambers
e38a713ccc Support sub-millisecond permit regeneration durations in rate limiters 2023-07-14 16:25:33 -04:00
Jon Chambers
82ed783a2d Introduce async account updaters 2023-07-14 16:25:19 -04:00
Jon Chambers
d17c7aaba6 Add support for clearing accounts from Redis asynchronously 2023-07-14 16:25:19 -04:00
Katherine Yen
8c93368b20 Update to the latest version of the spam filter 2023-07-13 12:43:07 -07:00
Jon Chambers
41f61c66a3 Add public methods for fetching accounts asynchronously 2023-07-13 13:53:29 -04:00
Jon Chambers
1b7a20619e Add tools for testing asynchronous Redis operations 2023-07-13 13:53:29 -04:00
Jon Chambers
7d19e58953 Add parallel pathways for getting accounts asyncronously to Accounts 2023-07-13 13:53:29 -04:00
Jon Chambers
1605676509 Store signed EC pre-keys in a dedicated table when setting signed pre-keys individually 2023-07-12 14:58:10 -04:00
Jon Chambers
a0d6146ff5 Make key deletion operations asynchronous 2023-07-12 14:58:10 -04:00
Jon Chambers
f709b00be3 Make KeysManager storage/retrieval operations asynchronous 2023-07-12 14:58:10 -04:00
Jonathan Klabunde Tomer
5847300290 Revert "Allow use of the token returned with spam challenges as auth for the challenge verification request" 2023-07-12 11:45:02 -07:00
Jonathan Klabunde Tomer
9aaac0eefd don't require all devices to support PNI for PNIHW 2023-07-12 10:14:16 -07:00
Jon Chambers
c5ae9913fe Update to the latest version of the spam filter 2023-07-11 13:48:07 -04:00
Jon Chambers
fc2ad20c63 Update to the latest version of the spam filter 2023-07-11 13:36:12 -04:00
Jon Chambers
6db97f5541 Standardize client tag version handling; add client version tags to delivery latency metrics 2023-07-11 13:35:29 -04:00
Jon Chambers
adf6c751ee Use an explicit-allow model for tagging client versions in metrics 2023-07-11 13:35:29 -04:00
Jon Chambers
c315b34395 Update formatting in UserAgentTagUtil 2023-07-11 13:35:29 -04:00
Jon Chambers
f592201e4c Limit attachment controller tags to UA platform (instead of platform and version) 2023-07-11 13:35:29 -04:00
Jon Chambers
8bf5ee45ed Filter out command tags from Lettuce metrics and prepend a "chat." prefix to Lettuce metric names 2023-07-11 13:35:03 -04:00
Jon Chambers
25f759dd07 Drop ActiveUserTally 2023-07-11 13:34:36 -04:00
Jonathan Klabunde Tomer
e5f4c17148 update openapi docs for several endpoints, notably those with PQXDH changes
Co-authored-by: Katherine Yen <katherine@signal.org>
2023-07-06 15:45:33 -07:00
Jonathan Klabunde Tomer
098b177bd3 Allow use of the token returned with spam challenges as auth for the challenge verification request 2023-07-06 15:25:19 -07:00
Jon Chambers
ef1a8fc50f Use PascalCase RPC names for the registration service 2023-07-06 17:12:37 -04:00
Jon Chambers
76f2e93a2c Reduce concurrency limit for pre-key migration task 2023-07-06 16:45:03 -04:00
Jon Chambers
25ea1df299 Limit concurrency when writing signed EC pre-keys 2023-07-06 15:51:12 -04:00
Chris Eager
5ced86af1d Set consistentRead=true for registration recovery password lookup
This avoids a race condition (in integration test situations) where a lookup could return no results
2023-07-06 15:47:16 -04:00
Jon Chambers
62e02a49df Log errors from single-shot account crawlers rather than printing them to stderr 2023-07-06 15:46:28 -04:00
Jon Chambers
540550d72a Handle exceptions thrown when checking pre-key signatures 2023-07-06 15:46:11 -04:00
Jon Chambers
8cb83fb6e4 Switch to temporary registration endpoints to facilitate a change from snake_case to PascalCase 2023-07-06 15:46:00 -04:00
Jon Chambers
56db925f0e Update to the latest version of the spam filter 2023-07-06 10:33:58 -04:00
Jon Chambers
2c0fc8fe3e Remove legacy methods from RegistrationServiceClient 2023-07-06 10:32:58 -04:00
Jon Chambers
08c7baafac Remove legacy registration endpoints from AccountController 2023-07-06 10:32:58 -04:00
Jon Chambers
8edb450d73 Parallelize single-shot account crawlers 2023-07-06 10:15:16 -04:00
ravi-signal
fedeef4da5 Add an optional parameter to require atomic account creation
By default, if a registration request has no optional fields for atomic
account creation set, the request will proceed non-atomically. If a
client sets the `atomic` field, now such a request would be rejected.
2023-07-05 11:24:11 -05:00
Jon Chambers
b593d49399 Control signed pre-key deletion via a dynamic configuration flag to facilitate migration 2023-07-05 12:17:17 -04:00
Chris Eager
4a91fc3c3d Set daemon=true for pubsub topology change event thread 2023-07-05 11:15:12 -05:00
Chris Eager
bb9605d7c3 Use RedisClient#setDefaultTimeout for a non-clustered client 2023-07-05 11:09:28 -05:00
Jon Chambers
1049326a70 Turn on Lettuce latency metrics 2023-06-30 18:20:43 -04:00
Chris Eager
457ecf145f Add test for Redis timeouts 2023-06-30 12:55:37 -05:00
Chris Eager
463dd9d7d8 Update to Lettuce 6.2.4 2023-06-30 12:55:37 -05:00
Chris Eager
bdcd055aaf Configure Redis timeouts using TimeoutOptions and RediURI 2023-06-30 12:55:37 -05:00
Jon Chambers
30ae2037e8 Correct order of constructor arguments for KeysManager 2023-06-30 12:05:16 -04:00
Jon Chambers
ce4fdbfb3c Untangle metric names for RepeatedUseSignedPreKeyStore subclasses 2023-06-30 10:33:24 -04:00
Jon Chambers
2d154eb0cf Add a command to copy signed pre-keys from Account records to their own table 2023-06-30 10:33:24 -04:00
Jon Chambers
a3e82dfae8 Add a temporary method for storing signed EC pre-keys if and only if another key has not already been stored 2023-06-30 10:33:24 -04:00
Jon Chambers
97a7469432 Measure signed EC pre-key agreement 2023-06-30 10:33:24 -04:00
Jon Chambers
1a1defb055 Store signed EC pre-keys in a dedicated table 2023-06-30 10:33:24 -04:00
Jon Chambers
93c78b6e40 Introduce RepeatedUseECSignedPreKeyStore 2023-06-30 10:33:24 -04:00
Chris Eager
b852d6681d FaultTolerantHttpClient: used managed ScheduledExecutorService for retries 2023-06-30 10:24:18 -04:00
Chris Eager
8e48ac4ede Add messagesCache and clientPresenceManager to managed command dependencies 2023-06-30 10:24:18 -04:00
Ehren Kret
859f646c55 Correct timestamp resolution to intended integer value 2023-06-29 16:05:59 -05:00
Chris Eager
fb39b2edaf Improve two @Disabled flaky tests 2023-06-29 14:56:41 -05:00
Chris Eager
d7bf815bd5 Update to the latest version of the spam filter 2023-06-28 14:30:15 -05:00
Chris Eager
c93af9e31e Remove MessagePersister from WhisperServerService environment
Persistence is now exclusively done by a separate command.
2023-06-28 14:17:49 -05:00
Chris Eager
b81a0e99d4 Always have 0 ApnPushNotificationScheduler worker threads in front-end service 2023-06-28 14:17:23 -05:00
Chris Eager
f8fefe2e5e Remove AccountCrawler (and doPeriodicWork) from WhisperServerService 2023-06-28 14:16:07 -05:00
Jon Chambers
f26bc70b59 Add a basic, prototype authentication interceptor for gRPC services 2023-06-27 10:21:11 -04:00
Jon Chambers
b5fd131aba Add an abstract base class for single-shot account crawls 2023-06-27 10:18:35 -04:00
Jon Chambers
06997e19e0 Add a method for iterating across all accounts 2023-06-27 10:18:35 -04:00
Jon Chambers
97710540c0 Use Timer.Sample throughout Experiment 2023-06-27 10:18:20 -04:00
Jon Chambers
c78c109577 Drop a disused endpoint for fetching the caller's own signed pre-key 2023-06-27 10:16:39 -04:00
Jonathan Klabunde Tomer
8d995e456e initial grpc service code in chat 2023-06-26 17:10:13 -07:00
Ehren Kret
cc3cab9c88 Add server time to remote config fetch
Enable clients to very roughly adjust some actions for clock skew by
providing current server time in the remote config fetch.
2023-06-21 17:11:35 -05:00
Jon Chambers
0122b410be Include push notification urgency in push latency metrics 2023-06-21 15:10:26 -04:00
Jon Chambers
2ddd2b9476 Convert PushRecord to a record and make PushType non-optional 2023-06-21 15:10:26 -04:00
Jon Chambers
a768498250 Record general message delivery latency 2023-06-21 15:10:14 -04:00
Sergey Skrobotov
a45aadae16 Cleaning up references to the legacy format from the rate limiters lua script 2023-06-21 15:09:46 -04:00
Sergey Skrobotov
25802432c2 adding a property to skip uploading to s3 during deploy task 2023-06-21 15:09:18 -04:00
Chris Eager
98578b18aa Update to dynamodb-lock-client 1.2.0 2023-06-21 15:08:40 -04:00
Chris Eager
6d81f69785 Start and stop all lifecycle-managed objects in CrawlAccountsCommand 2023-06-17 10:17:46 -05:00
Chris Eager
7dce183170 Add worker thread pool to PushFeedbackProcessor 2023-06-16 11:36:28 -05:00
Chris Eager
f1962a03ef Parameterize worker thread count in CrawlAccountsCommand 2023-06-16 11:36:28 -05:00
Jon Chambers
cb26bfd807 Update to the latest version of the spam filter 2023-06-15 13:12:54 -04:00
Chris Eager
befd336372 Remove static Remote Config auth tokens 2023-06-15 12:11:20 -05:00
Chris Eager
8501e61eb1 Set maxThreads = minThreads on command thread pools 2023-06-15 12:11:10 -05:00
Jon Chambers
ae489e5a52 Log account ages when identity keys change 2023-06-15 13:10:35 -04:00
Chris Eager
13afdbda97 Report system resource metrics from background tasks 2023-06-14 16:48:23 -05:00
Jon Chambers
9cfd88a23f Move turn secret to static configuration 2023-06-14 10:47:17 -04:00
Jon Chambers
13456bad3a Update date math with JSR310 types 2023-06-14 10:47:17 -04:00
Jon Chambers
45be85c5ef Update formatting and resolve warnings/suggestions 2023-06-14 10:47:17 -04:00
Jonathan Klabunde Tomer
861dc0d021 reject message sends that have the same device more than once 2023-06-13 09:49:50 -07:00
Chris Eager
128d709c99 Additional counters and timers for WebSocket connections 2023-06-13 11:46:15 -05:00
Jon Chambers
e8f01be8ef Inject version bytes if missing from existing EC pre-keys 2023-06-09 11:41:51 -04:00
Jon Chambers
7f1ee015d1 Treat blank strings as null pre-keys 2023-06-09 10:39:16 -04:00
Jon Chambers
17aa5d8e74 Use strongly-typed pre-keys 2023-06-09 10:08:49 -04:00
Jon Chambers
b27334b0ff Treat blank strings as null identity keys 2023-06-09 10:08:18 -04:00
Jon Chambers
7fc6b1e802 Count invalid pre-keys 2023-06-09 09:25:31 -04:00
Jon Chambers
25b7c8f802 Update to libsignal-server 0.26.0 2023-06-09 09:25:31 -04:00
Jon Chambers
8ec6a24a2d Fix a metric name/tag set 2023-06-08 12:34:27 -04:00
Jon Chambers
234707169e Represent identity keys as IdentityKey instances 2023-06-08 11:36:58 -04:00
Jon Chambers
1c8443210a Check for missing version bytes in invalid identity keys 2023-06-08 09:56:21 -04:00
g1a55er
aaf43a592f Replace reserved "notification" key with "newMessageAlert" 2023-06-08 09:53:31 -04:00
Jon Chambers
2b08742c0a Create separate key stores for different kinds of pre-keys 2023-06-06 17:08:26 -04:00
Jon Chambers
cac04146de Identify specific cases with invalid identity keys 2023-06-06 17:08:01 -04:00
ravi-signal
2b266c7beb Validate registration ids for new accounts 2023-06-06 11:08:54 -04:00
Jonathan Klabunde Tomer
099932ae68 ApnPushNotifcationScheduler: always run worker thread at least once 2023-06-06 11:04:44 -04:00
Jon Chambers
8579babde6 Count instances where an account's identity key could not be interpreted as an IdentityKey 2023-06-06 11:01:25 -04:00
Jon Chambers
9c93d379a8 Fix a sneaky merge conflict 2023-06-05 12:38:35 -04:00
Jon Chambers
085c7a67c8 Refactor account locks/deleted account manager 2023-06-05 12:30:44 -04:00
Sergey Skrobotov
e6917d8427 minor cleanup, docs, and integration tests for username API 2023-06-02 10:35:07 -07:00
Sergey Skrobotov
47cc7fd615 username links API 2023-06-02 10:26:14 -07:00
Jonathan Klabunde Tomer
ecd207f0a1 Check structural validity of prekeys at upload time 2023-05-31 14:29:39 -07:00
Chris Eager
0ab66f2f14 Add aws-java-sdk-sts to dependencies 2023-05-31 14:57:48 -05:00
Chris Eager
d1e38737ce Support ID token at PUT /v1/config and DELETE /v1/config 2023-05-30 10:28:28 -05:00
Chris Eager
f17de58a71 Change ScheduledApnPushNotificationSenderServiceCommand to extend ServerCommand 2023-05-30 10:14:33 -05:00
Chris Eager
dd552e8e8f Change MessagePersisterServiceCommand to extend ServerCommand 2023-05-30 10:14:33 -05:00
Chris Eager
18480e9d18 Move metrics registry environment.manage() to utility 2023-05-30 10:14:33 -05:00
Chris Eager
7ffccd9c3a Initialize metrics in ScheduledApnPushNotificationSenderServiceCommand 2023-05-26 16:41:17 -05:00
Chris Eager
0edd99e9cf Initialize metrics in MessagePersisterServiceCommand 2023-05-26 16:41:17 -05:00
Chris Eager
defdc14d5e Initialize metrics in CrawlAccountsCommand 2023-05-26 16:41:17 -05:00
Chris Eager
5dcf8edd38 Factor metrics registry intialization to a utility 2023-05-26 16:41:17 -05:00
Jon Chambers
a320766bb6 Update to the latest version of the spam filter 2023-05-26 14:22:52 -04:00
Jon Chambers
91805caa9a Finalize rate limit unit inversion 2023-05-26 14:17:30 -04:00
Jon Chambers
48d39dccbd Fix rate limit division errors 2023-05-26 13:13:02 -04:00
Jon Chambers
fc9e1f59a5 Update to the latest version of the spam filter 2023-05-26 12:46:36 -04:00
Chris Eager
e7bc8bd6b9 Consistently use AWS credentials providers from WhisperServerService 2023-05-26 12:45:38 -04:00
Jon Chambers
23337d7992 Update to the latest version of the spam filter 2023-05-26 11:43:16 -04:00
Jon Chambers
f513dc0398 Invert rate limit units in default configurations 2023-05-26 11:37:06 -04:00
Jon Chambers
184969336e Allow RateLimiterConfig to accept either a leak rate per minute or a permit regeneration duration 2023-05-26 11:37:06 -04:00
Chris Eager
1534f1aa6a Add web identity token AWS SDK credentials provider 2023-05-26 11:07:41 -04:00
Chris Eager
cd8f74e60b Add support for environment-dependent secondary OAuth2 credentials JSON 2023-05-26 11:07:30 -04:00
Jon Chambers
d832eaa759 Represent identity keys as byte arrays 2023-05-26 10:12:22 -04:00
Jon Chambers
796863341d Revert "Count identity keys that are present, but can't be parsed as base64"
This reverts commit 024dd02628a7d989424273501528b52fe18c3ee9.
2023-05-26 10:12:22 -04:00
Jon Chambers
217b68a1e0 Represent pre-key public keys and signatures as byte arrays in DAOs 2023-05-26 09:58:38 -04:00
Jon Chambers
4a8ad3103c Actually write pre-keys as byte arrays 2023-05-26 08:23:54 -04:00
Jon Chambers
a5f853c67a Change inactive account age threshold from 365 to 180 days 2023-05-26 08:23:19 -04:00
Jon Chambers
70b54e227e Count the prevalence of keys stored as strings or as bytes 2023-05-25 10:04:38 -05:00
Jonathan Klabunde Tomer
1ab6bff54e add @Produces annotations to a few methods in DeviceController 2023-05-25 09:57:06 -05:00
Chris Eager
c2317e8493 Start the dynamic configuration manager in dependent commands 2023-05-25 09:52:01 -05:00
Jon Chambers
b034a088b1 Add support for "atomic" device linking/activation 2023-05-19 16:13:37 -04:00
Jon Chambers
ae7cb8036e Factor DeviceActivationRequest out into its own record 2023-05-19 16:13:37 -04:00
Jon Chambers
1a5327aece Update to the latest version of the spam filter 2023-05-19 15:59:09 -04:00
Jon Chambers
8ce2b04fe4 Discard test device codes 2023-05-19 15:57:14 -04:00
Chris Eager
a3c37aed47 Remove obsolete field from SecureValueRecovery2Configuration 2023-05-19 15:57:01 -04:00
Jon Chambers
fa8f19fd43 Group atomic account creation operations 2023-05-19 15:56:45 -04:00
Jon Chambers
c9a9409b9a Count identity keys that are present, but can't be parsed as base64 2023-05-19 15:56:27 -04:00
Jon Chambers
d3e0ba6d44 Prepare to read pre-keys stored as byte arrays 2023-05-19 15:56:13 -04:00
Jon Chambers
300ac16cf1 Handle "transport not allowed" responses from the registration service 2023-05-19 15:55:53 -04:00
Chris Eager
3e53884979 Add MessagePersisterServiceCommand 2023-05-18 15:37:54 -05:00
Chris Eager
859fbe9ab1 Update to the latest version of the spam filter 2023-05-18 11:44:38 -05:00
Chris Eager
6043c1a4e8 Add ScheduledApnPushNotificationSenderServiceCommand 2023-05-18 11:44:01 -05:00
Chris Eager
0d9fd043a4 Add container image build using Jib 2023-05-18 11:43:29 -05:00
Chris Eager
f06eaf13d1 Send 1009 for too-large message frames 2023-05-18 11:42:22 -05:00
Jon Chambers
66a619a378 Allow for atomic account creation and activation 2023-05-18 09:51:13 -04:00
Jon Chambers
fb1b1e1c04 Update libsignal-server to 0.24.0 2023-05-18 09:51:13 -04:00
Katherine Yen
9450f88c8c Add annotation to catch empty request body 2023-05-17 14:28:41 -07:00
Sergey Skrobotov
0706171264 Update to the latest version of the spam filter 2023-05-17 11:43:17 -07:00
Sergey Skrobotov
287e2fa89a Moving secret values out of the main configuration file 2023-05-17 11:25:59 -07:00
Chris Eager
8d1c26d07d Add CrawlAccountsCommand 2023-05-17 12:22:49 -05:00
Jonathan Klabunde Tomer
caae27c44c PQXDH endpoints for chat server 2023-05-16 14:34:33 -07:00
Katherine Yen
34d77e73ff Fix integer division in call link ratelimit leak rate 2023-05-16 14:34:06 -07:00
Chris Eager
0889741f34 Update GitHub Actions versions 2023-05-12 12:53:47 -05:00
Ravi Khadiwala
8c42199baf Add svr2 credentials to RegistrationLockFailure responses
Add an svr2 credential to 423 responses for:
  - PUT v2/accounts/number
  - POST v1/registration

Also add some openapi annotations to those endpoints
2023-05-12 11:02:32 -05:00
Katherine Yen
7395b5760a Remove unused call link config 2023-05-12 11:01:42 -05:00
Jon Chambers
c8f97ed065 Update to the latest version of the spam filter 2023-05-10 15:29:10 -04:00
Jon Chambers
d2baa8b8fb Stop sending API keys to the registration service 2023-05-10 15:28:12 -04:00
Jon Chambers
1beee5fd04 Update to the latest version of the spam filter 2023-05-10 15:01:37 -04:00
Chris Eager
281b91a59a Remove obsolete ContactDiscoveryWriter 2023-05-10 14:01:09 -05:00
Jon Chambers
2be2b4ff23 Authenticate with the registration service using OIDC identity tokens in addition to shared API keys 2023-05-10 14:59:07 -04:00
Jon Chambers
a83fd1d3fe Include request method as a request counter dimension 2023-05-09 15:17:46 -05:00
Jon Chambers
cb72e4f426 Simplify request counter 2023-05-09 15:17:46 -05:00
Chris Eager
3214852a41 Fix 401 on /v1/keepalive 2023-05-09 15:08:03 -05:00
Jon Chambers
1057bd7e1f Resolve warnings/suggestions throughout ProfileControllerTest 2023-05-09 10:32:32 -04:00
Jonathan Klabunde Tomer
33903553ab reinstate per-{path,status,platform,is-websocket} request counters 2023-05-09 09:49:20 -04:00
Katherine Yen
c309afc04b Displace client presence when existing account reregisters 2023-05-05 11:31:18 -07:00
Erik Osheim
f6c4ba898b Update to the latest version of the spam filter 2023-05-05 11:22:29 -04:00
Katherine Yen
7ba86b40aa Create call link credential endpoint 2023-05-04 14:33:45 -07:00
Katherine Yen
b2b0aee4b7 Call link auth credential 2023-05-04 14:17:01 -07:00
Jon Chambers
919cc7e5eb Update libsignal to 0.23 2023-05-04 14:10:51 -07:00
Jonathan Klabunde Tomer
e38911b2c5 Always check prekey signatures when new prekeys are uploaded 2023-05-04 11:31:45 -07:00
Chris Eager
bc68b67cdf account crawler: remove obsolete accelerated mode 2023-05-04 11:27:16 -05:00
Chris Eager
42a9f1b3e4 account crawler: remove set*Dynamo methods 2023-05-04 11:27:16 -05:00
ravi-signal
08333d5989 Implement /v2/backup/auth/check 2023-05-04 11:23:33 -05:00
Ravi Khadiwala
0e0c0c5dfe return 400 instead of 503 for bad verification session-id 2023-05-04 09:22:51 -07:00
Ravi Khadiwala
59ebe65643 Add counter to /v2/attachments 2023-05-04 09:22:18 -07:00
Chris Eager
4fd2422e4d Catch and close() after UninitializedMessageException in websocket messages 2023-05-03 13:36:29 -05:00
Chris Eager
6181d439f6 Update to the latest version of the spam filter 2023-05-03 13:35:35 -05:00
Chris Eager
57b6c10dd1 Remove obsolete dynamic configuration 2023-05-03 13:20:44 -05:00
Jon Chambers
3ee5ac4514 Fix a late-breaking merge conflict 2023-05-02 16:12:26 -04:00
Jonathan Klabunde Tomer
be176f98ad metric for take-prekey yielding an empty result 2023-05-02 13:03:49 -07:00
Jon Chambers
12b58a31a1 Retire integration with legacy contact discovery system 2023-05-02 15:57:03 -04:00
Jon Chambers
8d468d17e3 Add a temporary counter for profile key credential types 2023-05-02 15:56:19 -04:00
Erik Osheim
30df4c3d29 Update to the latest version of the spam filter 2023-05-02 10:37:22 -05:00
Brenden Stahle
5122a1c466 Change the copyright date from 2022 to 2023. 2023-05-02 10:31:04 -05:00
Chris Eager
e135d50d82 Add counter for ContactDiscoverWriter updates 2023-05-01 13:42:14 -05:00
Chris Eager
487b5edc75 Handle potentially null payment method when canceling subscription 2023-05-01 13:42:05 -05:00
Jonathan Klabunde Tomer
47ad5779ad new /v2/accounts endpoint to distribute PNI key material without changing phone number 2023-04-21 12:20:57 -07:00
Katherine Yen
4fb89360ce Allow registration via recovery password for reglock enabled accounts 2023-04-20 09:21:04 -07:00
Jon Chambers
6dfdbeb7bb Check for no-op APNs token changes 2023-04-19 17:01:01 -04:00
Jon Chambers
d0ccbd5526 Simplify a check for no-op FCM token changes 2023-04-19 17:01:01 -04:00
Jon Chambers
031ee57371 Convert "set push token" request objects to records 2023-04-19 17:01:01 -04:00
Jon Chambers
2043678739 Remove the removeSignalingKey API endpoint 2023-04-19 17:00:47 -04:00
Jon Chambers
dd27e3b0c8 Convert attachment descriptors to records 2023-04-19 17:00:34 -04:00
Jon Chambers
1083d8bde0 Remove the legacy group credential endpoint 2023-04-19 17:00:14 -04:00
Jon Chambers
d1eb247d8c Clarify the purpose of an addListener method 2023-04-18 12:04:54 -04:00
Jon Chambers
fd5e9ea016 Drop the old (and now unused!) redis-dispatch module 2023-04-18 12:04:54 -04:00
Jon Chambers
11829d1f9f Refactor provisioning plumbing to use Lettuce 2023-04-18 12:04:54 -04:00
Ehren Kret
ae70d1113c use same protoc version as library dependency 2023-04-17 14:41:55 -05:00
Katherine Yen
c485d317fb Mock apnPushNotificationScheduler 2023-04-17 10:55:15 -07:00
Katherine Yen
350682b83a Lock account and send notification when someone passes phone verification but fails reglock 2023-04-17 10:30:36 -07:00
ravi-signal
0fe6485038 Add a configuration to make rate limiters fail open 2023-04-14 13:08:14 -05:00
Sergey Skrobotov
a553093046 integration tests initial setup 2023-04-13 11:12:34 -07:00
Erik Osheim
af0d5adcdc Update to the latest version of the spam filter 2023-04-11 16:40:03 -04:00
Katherine Yen
61af1ba029 Clean up prohibited username references 2023-04-10 15:21:02 -07:00
ravi-signal
8847cb92ac Don't block when scheduling background apns pushes 2023-04-10 13:51:36 -05:00
Erik Osheim
5242514874 Update to the latest version of the spam filter 2023-04-07 17:13:48 -04:00
Chris Eager
33a6577b6e Decrease message delivery executor thread count to 20 2023-04-07 10:56:23 -05:00
Chris Eager
23d5006f70 Add prefix to executor metric names 2023-04-05 09:51:53 -05:00
Chris Eager
2697872bdd Use Apache StringUtils#join 2023-04-05 09:51:30 -05:00
Ravi Khadiwala
7b331edcde Separate username and signature truncation fields 2023-04-05 09:51:00 -05:00
Katherine Yen
e4da59c236 Generic credential auth endpoint for call links 2023-04-04 10:28:35 -07:00
Jonathan Klabunde Tomer
48ebafa4e0 DynamoDBExtension refactor and helpers for our schema (#1327)
There's a lot of boilerplate involved in setting up a DynamoDBExtension, and some tests were creating several extensions
rather than one with several tables, which is probably slower than it has to be.

This change adds a new DynamoDbExtensionSchema class in which we can define the Dynamo schema for tests, and refactors
DynamoDbExtension to make it easy to instantiate a single extension with all the tables one wants (and no more, both to
minimize test startup time and to ensure we explicitly test our dependencies and lack thereof).

Tests requiring a DynamoDbExtension with a table schema that's not part of the normal Signal schema can instantiate a
DynamoDbExtension.RawSchema instead.

Test timings are unaffected, at least on my machine. Before:
```[INFO] service ............................................ SUCCESS [01:18 min]```

After:
```[INFO] service ............................................ SUCCESS [01:18 min]```

Co-authored-by: Jonathan Klabunde Tomer <jkt@viola.signal.org>
2023-04-03 13:08:43 -07:00
Erik Osheim
f5726f63bd Update to the latest version of the spam filter 2023-04-03 14:34:13 -04:00
Jonathan Klabunde Tomer
391b070cff KeysController: return correct number of unsigned prekeys
When GET /v2/keys was orignally added in b263f47, prekeys were stored in
Postgres, with a user's unsigned and signed keys together in the same table.
Therefore GET /v2/keys subtracted one from the count returned by storage.

In d4d9403, we changed to a different storage schema, with unsigned prekeys in
one DynamoDB table and unsigned prekeys in the accounts Dynamo table.
Unfortunately, GET /v2/keys was not changed to stop subtracting one from the
count of prekeys in the keys table at the same time. This commit fixes that.
2023-04-03 14:32:45 -04:00
gram-signal
781cd0ca3f Truncate SVR2 IDs to 16 bytes rather than 10. 2023-03-30 17:19:18 -06:00
Erik Osheim
84355963f9 Update to the latest version of the spam filter 2023-03-29 16:51:48 -04:00
Chris Eager
3ccfeb490b Add retry after exceptions during a cluster topology change event callback 2023-03-29 11:41:19 -05:00
Chris Eager
0cc84131de Add enabled to SVR2 configuration 2023-03-29 11:40:21 -05:00
Chris Eager
4fa08fb189 Add secure value recovery 2 to AccountsManager#delete() 2023-03-29 11:40:21 -05:00
Chris Eager
2a551d1d41 Add SecureValueRecovery2Client 2023-03-29 11:40:21 -05:00
Chris Eager
391aa9c518 Wrap runtime exceptions during WebSocket auth into AuthenticationException 2023-03-29 10:08:55 -05:00
Erik Osheim
39d9fd0317 Update to the latest version of the spam filter 2023-03-28 11:20:18 -04:00
Chris Eager
18b1fcd724 Update to the latest version of the spam filter 2023-03-22 13:08:58 -05:00
Chris Eager
f5c62a3d85 Migrate from bounded elastic to dedicated executor for message delivery 2023-03-22 12:57:44 -05:00
Chris Eager
6075d5137b Add /v2/accounts/data_report 2023-03-22 12:57:21 -05:00
ravi-signal
890293e429 change v1/challenge response for invalid captcha 2023-03-21 17:38:30 -05:00
Ravi Khadiwala
05b43a878b Register unlink device command 2023-03-21 17:35:57 -05:00
Chris Eager
fe9c3982a1 Remove prepended username from /v2/backup/auth response 2023-03-21 17:35:42 -05:00
Ravi Khadiwala
82baa892f7 Update to the latest version of spam filter 2023-03-21 17:34:58 -05:00
Ravi Khadiwala
ee53260d72 Add filter-provided captcha score thresholds 2023-03-21 17:34:58 -05:00
Ravi Khadiwala
a8eb27940d Add per-action captcha site-key configuration
- reject captcha requests without valid actions
- require specific site keys for each action
2023-03-21 17:34:58 -05:00
Erik Osheim
fd8918eaff Update to the latest version of the spam filter 2023-03-21 15:47:55 -04:00
Katherine Yen
a3a7d7108b Change reglock expiration check to be > 0 instead of >= 0 2023-03-21 12:46:35 -07:00
Jon Chambers
cd27fe0409 Update to the latest version of the spam filter 2023-03-20 15:28:01 -04:00
Jon Chambers
35606a9afd Send "account already exists" flag when creating registration sessions 2023-03-20 15:18:55 -04:00
Jon Chambers
2052e62c01 Use a purpose-specific method when checking verification codes via the legacy registration API 2023-03-20 15:18:38 -04:00
Erik Osheim
8ccab5c1e0 Update to the latest version of the spam filter 2023-03-17 16:41:48 -04:00
Chris Eager
292f69256e Refactor WebSocket message sending error and completion to subscriber from “doOn…” 2023-03-17 12:42:57 -05:00
ravi-signal
fbdcb942e8 Add unlink user command 2023-03-16 11:17:36 -05:00
Sergey Skrobotov
c14ef7e6cf migrate token bucket redis record format from json to hash: phase 2 2023-03-16 09:15:22 -07:00
Jon Chambers
a04fe133b6 Fix a typo in a method name 2023-03-15 16:01:14 -07:00
Sergey Skrobotov
483e444174 migrate token bucket redis record format from json to hash: phase 1 2023-03-15 16:01:06 -07:00
Sergey Skrobotov
ebf8aa7b15 fixing embedded redis based tests 2023-03-15 13:56:40 -07:00
Katherine Yen
7c52be2ac1 Bump old registration default ratelimiter to match Bravo 2023-03-15 09:44:02 -07:00
Sergey Skrobotov
203a49975c artifact is now available in maven central 2023-03-14 12:02:16 -07:00
Sergey Skrobotov
7d45838a1e reordering maven repositories 2023-03-13 22:22:25 -07:00
Katherine Yen
2683f1c6e7 Encode username hash to base64 string without padding 2023-03-13 15:35:27 -07:00
Sergey Skrobotov
d13413aff2 Update to the latest version of the spam filter 2023-03-13 15:04:51 -07:00
Sergey Skrobotov
4c85e7ba66 Moving RateLimiter logic to Redis Lua and adding async API 2023-03-13 14:50:26 -07:00
Katherine Yen
46fef4082c Add metrics for registration lock flow 2023-03-09 09:07:21 -08:00
Ravi Khadiwala
c06313dd2e Drop tagging for legacy user agents 2023-03-09 10:43:45 -06:00
Ravi Khadiwala
59bc2c5535 Add by-action captcha score config
Enable setting different captcha score thresholds for different captcha
actions via configuration
2023-03-09 10:43:16 -06:00
Chris Eager
437bc1358b Use server timestamp for queue score 2023-03-06 11:31:11 -06:00
Katherine Yen
99e651e902 Update to the latest version of the spam filter 2023-03-03 14:10:56 -08:00
Chris Eager
757ce42a35 Update s3-upload-maven-plugin to 2.0.1 2023-03-03 13:17:28 -06:00
Chris Eager
179f3df847 Allow DisabledPermittedAuthenticatedAccount at /v1/accounts/me 2023-03-03 13:17:17 -06:00
Chris Eager
8a889516b0 Improve LoggingUnhandledExceptionMapper combination with CompletionExceptionMapper 2023-03-03 13:17:07 -06:00
Jon Chambers
7de5c0a27d Keep counts of open websockets by client platform 2023-03-03 13:16:24 -06:00
Chris Eager
71d234e1e4 Update default rate limiter config 2023-03-02 10:27:07 -06:00
Chris Eager
b5fb33e21e Remove unused metrics 2023-03-02 10:14:58 -06:00
Sergey Skrobotov
2be22c2a8e Updating documentation github action to handle no changes case 2023-02-28 14:48:09 -08:00
Chris Eager
db198237f3 Expand try-finally scope of deleted accounts reconciliation lock 2023-02-28 12:42:18 -06:00
Chris Eager
d0ccae129a Remove obsolete metric 2023-02-27 16:33:34 -06:00
Chris Eager
ecbef9c6ee Add micrometer metrics to RateLimiter 2023-02-27 16:33:27 -06:00
Chris Eager
ef2cc6620e Add @Produces annotation for validation error response 2023-02-27 16:33:18 -06:00
ravi-signal
b8f363b187 Add documentation to challenge controller 2023-02-24 17:41:15 -06:00
Sergey Skrobotov
c3f4956ead OpenAPI support 2023-02-24 13:03:30 -08:00
Chris Eager
047f4a1c00 Update metric name 2023-02-24 13:07:07 -06:00
Sergey Skrobotov
41c0fe9ffa Adding a uniform configuration for all json/yaml mapper use cases: part 2 2023-02-24 09:28:55 -08:00
Sergey Skrobotov
6edb0d49e9 Adding a uniform configuration for all json/yaml mapper use cases: bugfix 2023-02-23 20:01:32 -08:00
Sergey Skrobotov
a5e3b81a50 Update to the latest version of the spam filter 2023-02-23 17:12:12 -08:00
Sergey Skrobotov
b9b4e3fdd8 Adding a uniform configuration for all json/yaml mapper use cases: part 1 2023-02-23 16:38:48 -08:00
Jon Chambers
6ee9c6ad46 Remove deprecated registration service response fields 2023-02-23 12:41:56 -08:00
Sergey Skrobotov
6d6556eee5 Update to the latest version of the spam filter 2023-02-23 11:04:14 -08:00
Sergey Skrobotov
7529c35013 Rate limiters code refactored 2023-02-23 10:49:06 -08:00
erik-signal
378b32d44d Add missing token field to OutgoingMessageEntity 2023-02-23 11:18:07 -05:00
Chris Eager
e1fcd3e3f6 Remove Lettuce command latency recorder 2023-02-23 10:17:31 -06:00
Chris Eager
d7ad8dd448 Add micrometer timer to FaultTolerantPubSubConnection 2023-02-23 10:17:24 -06:00
Chris Eager
859f2302a9 Remove unused metrics 2023-02-23 10:17:24 -06:00
Chris Eager
a6d11789e9 Add ClosedChannelException to expected errors 2023-02-23 10:17:16 -06:00
Chris Eager
43f83076fa Update to reactor 3.5.3 2023-02-23 10:16:57 -06:00
erik-signal
71c0fc8d4a Improve metrics around spam report tokens. 2023-02-22 15:43:44 -05:00
Chris Eager
d2f723de12 Update to the latest version of the spam filter 2023-02-22 14:33:29 -06:00
Chris Eager
1f4f926ce6 Add platform tag to subscription receipt metrics 2023-02-22 14:31:30 -06:00
Chris Eager
35286f838e Add /v1/verification 2023-02-22 14:27:05 -06:00
Jon Chambers
e1ea3795bb Reuse registration sessions if possible when requesting pre-auth codes 2023-02-22 12:45:26 -05:00
erik-signal
95237a22a9 Relax validation to allow null reporting tokens. 2023-02-22 11:06:51 -05:00
Katherine Yen
11c93c5f53 Keep username hash during reregistration 2023-02-21 09:07:30 -08:00
Jon Chambers
b59b8621c5 Add reporter platform as a reported message dimension 2023-02-17 16:44:13 -05:00
Chris Eager
44c61d9a58 Allow updates if the profile already has a payment address 2023-02-17 16:44:01 -05:00
Ehren Kret
63a17bc14b add support for running tests from aarch64 2023-02-16 09:57:34 -06:00
Jon Chambers
f4f93bb24d Update to the latest version of the spam filter 2023-02-14 12:36:34 -05:00
Jon Chambers
7561622bc8 Log cases where we fall back to a no-op spam-reporting token provider 2023-02-14 12:35:56 -05:00
Jon Chambers
b041566aba Simplify construction of spam reporting token providers 2023-02-14 12:35:56 -05:00
Jon Chambers
cb72158abc Add the presence of spam reporting tokens as a dimension 2023-02-14 12:35:21 -05:00
Jon Chambers
5c432d094f Fix a typo in a metric name 2023-02-14 12:34:48 -05:00
Chris Eager
24ac48b3b1 Update counter name 2023-02-10 14:54:02 -06:00
Katherine Yen
c03060fe3c Phone number discoverability update endpoint 2023-02-10 11:52:51 -08:00
Chris Eager
3ebd5141ae Update to the latest version of the spam filter 2023-02-10 12:15:10 -06:00
Chris Eager
c16006dc4b Add PUT /v2/account/number 2023-02-10 12:09:03 -06:00
Sergey Skrobotov
8fc465b3e8 removing redundant logic in new registration flow 2023-02-09 09:06:48 -08:00
Chris Eager
ce689bdff3 Use DisabledPermittedAuthenticatedAccount at DELETE /v1/accounts/me 2023-02-09 09:05:29 -08:00
Chris Eager
e23386ddc7 Remove unused JUnit extension from test 2023-02-09 09:05:11 -08:00
Jon Chambers
0f17d63774 Add tests for ProvisioningController 2023-02-09 09:04:52 -08:00
Katherine Yen
4fc3949367 Add zkproof validation in username flow 2023-02-09 09:02:53 -08:00
Katherine Yen
e19c04377b Update to the latest version of the spam filter 2023-02-09 09:00:38 -08:00
Sergey Skrobotov
7c3f429c56 Update E164 constraint message 2023-02-08 13:22:00 -08:00
Sergey Skrobotov
7558489ad0 Registration Recovery Password support in /v1/registration 2023-02-08 13:20:23 -08:00
Katherine Yen
4a3880b5ae usernameHashes on reserve request can't be null 2023-02-07 08:44:04 -08:00
Chris Eager
ca7a4abd30 Update to the latest version of the spam filter 2023-02-06 16:40:09 -06:00
Chris Eager
a4a45de161 Add /v1/registration 2023-02-06 16:11:59 -06:00
Chris Eager
358a286523 Use java.util Hex and Base64 codecs 2023-02-06 12:16:59 -06:00
Sergey Skrobotov
3bbab0027b Update to the latest version of the spam filter 2023-02-03 16:39:34 -08:00
Sergey Skrobotov
8afe917a6c Registration recovery passwords store and manager 2023-02-03 16:33:03 -08:00
Erik Osheim
f5fec5e6bb Update to the latest version of the spam filter 2023-02-03 16:24:35 -05:00
Erik Osheim
0b81743683 Update to the latest version of the spam filter 2023-02-02 18:06:43 -05:00
Erik Osheim
9f715c3224 Update to the latest version of the spam filter 2023-02-02 18:05:02 -05:00
Katherine Yen
24f515ccb4 Revert "Revert "Stored hashed username"" 2023-02-02 11:20:44 -08:00
Erik Osheim
fd531242c9 Update to the latest version of the spam filter 2023-02-02 12:20:45 -05:00
Erik Osheim
3855bd257d Update to the latest version of the spam filter 2023-02-01 17:41:58 -05:00
Katherine Yen
c98b54ff15 Revert "Stored hashed username" 2023-02-01 14:31:44 -08:00
Katherine Yen
d93d50d038 Stored hashed username 2023-02-01 12:08:25 -08:00
Jon Chambers
448365c7a0 Preserve legacy registration API error handling 2023-01-31 15:45:23 -05:00
Sergey Skrobotov
515a863195 Update .gitmodules 2023-01-30 15:45:41 -08:00
Sergey Skrobotov
8d0e23bde1 AuthenticationCredentials name changed to SaltedTokenHash 2023-01-30 15:45:24 -08:00
Sergey Skrobotov
dc8f62a4ad /v1/backup/auth/check endpoint added 2023-01-30 15:39:42 -08:00
Jon Chambers
896e65545e Update to the latest version of the spam filter 2023-01-30 16:30:14 -05:00
Jon Chambers
cd4a4b1dcf Retire VoiceVerificationController 2023-01-30 16:28:14 -05:00
Jon Chambers
38a0737afb Retire ReportSpamTokenHandler interface in favor of ReportedMessageListener 2023-01-30 16:27:54 -05:00
Jon Chambers
4a2768b81d Add spam report token support to ReportedMessageListener 2023-01-30 16:27:54 -05:00
Jon Chambers
00e08b8402 Simplify parsing/validation of spam report tokens 2023-01-30 16:27:54 -05:00
Erik Osheim
48e8584e13 Update to current version of the spam-filter. 2023-01-27 11:41:27 -05:00
erik-signal
a89e30fe75 Clarify naming around spam filtering. 2023-01-27 11:40:33 -05:00
gram-signal
a01fcdad28 Add in controller for SVR2 auth. 2023-01-27 09:15:52 -07:00
Chris Eager
2a99529921 Remove old badge strings 2023-01-26 09:23:11 -06:00
Sergey Skrobotov
c934405a3e fixing config field names 2023-01-25 17:28:03 -08:00
Sergey Skrobotov
b8d922fcb7 Update to latest version of the spam module 2023-01-25 15:41:54 -08:00
Sergey Skrobotov
eb499833c6 refactoring of ExternalServiceCredentialGenerator 2023-01-25 15:20:28 -08:00
Chris Eager
dd98f7f043 Support changing just the currency of an existing subscription 2023-01-25 15:14:17 -06:00
Chris Eager
e8978ef91c Add tests for SubscriptionController#setSubscriptionLevel 2023-01-25 15:14:17 -06:00
Chris Eager
669ff1cadf DynamoDB Local Release Repository: snapshots.enabled = false 2023-01-25 15:12:02 -06:00
Jon Chambers
4ce85fdb19 Treat "check code" exceptions as false for legacy API compatibility 2023-01-25 14:39:29 -05:00
Jon Chambers
035ddc4834 Fix a mistake where we're looking for verification codes in place that hasn't been deployed yet (but will be soon!) 2023-01-25 11:43:06 -05:00
Chris Eager
c2f40b8503 Remove duplicate code 2023-01-25 11:09:23 -05:00
Jon Chambers
cf738a1c14 Look for registration service errors in response bodies in addition to status responses 2023-01-25 10:49:36 -05:00
erik-signal
52d40c2321 Add metrics for spam report tokens received. 2023-01-24 10:25:40 -05:00
Erik Osheim
cbf12d6b46 Update to latest version of the spam module 2023-01-19 11:20:08 -05:00
erik-signal
ab26a65b6a Introduce spam report tokens 2023-01-19 11:13:43 -05:00
erik-signal
ee5aaf5383 Ignore files created by emacs / lsp. 2023-01-18 15:44:29 -05:00
Jon Chambers
1c1714b2c2 Clarify a counter name 2023-01-17 17:13:06 -05:00
Jon Chambers
accb017ec5 Use a longer expiration window for quantile calculation 2023-01-17 17:13:06 -05:00
Chris Eager
304782d583 Use processor from SubscriptionProcessorManager for issued receipts 2023-01-17 16:12:03 -06:00
Chris Eager
f361f436d8 Support PayPal for recurring donations 2023-01-17 12:20:17 -06:00
Chris Eager
a34b5a6122 grpc, guava: use version from google cloud libraries-bom 2023-01-17 11:20:46 -06:00
Chris Eager
f75ea18ccb Add test for GoogleCloudAdminEventLogger 2023-01-17 11:20:46 -06:00
Dimitris Apostolou
9a06c40a28 Fix typos 2023-01-13 16:05:06 -06:00
Chris Eager
e6ab97dc5a Update enabled-required authenticator metrics 2023-01-13 14:05:56 -06:00
Chris Eager
ba73f757e2 Update google libraries-bom to 26.1.3, firebase-admin to 9.1.1 2023-01-13 12:22:55 -06:00
Chris Eager
30f131096d Update AWS SDK v1 to 1.12.376 2023-01-13 12:17:39 -06:00
Chris Eager
b8ce922f92 Update logstash-logback-encoder to 7.2 2023-01-13 12:17:39 -06:00
Chris Eager
11b62345e1 Update mockito to 4.11.0 2023-01-13 12:17:39 -06:00
Chris Eager
77289ecb51 Update micrometer to 1.10.3 2023-01-13 12:17:39 -06:00
Chris Eager
dfb0b68997 Update DynamoDBLocal to 1.20.0 2023-01-13 12:17:39 -06:00
Chris Eager
d545f60fc4 Update wiremock to 2.35.0 2023-01-13 12:17:39 -06:00
Chris Eager
5cda6e9d84 Update pushy to 0.15.2 2023-01-13 12:17:39 -06:00
Chris Eager
7caba89210 Update AWS SDK v2 to 2.19.8 2023-01-13 12:17:39 -06:00
Chris Eager
b8967b75c6 Update dropwizard to 2.0.34 2023-01-13 12:17:39 -06:00
Chris Eager
74d9849472 Update badge strings 2023-01-13 12:08:05 -06:00
Fedor Indutny
96b753cfd0 Add an extra kb to max sticker size 2023-01-13 12:07:45 -06:00
Jon Chambers
5a89e66fc0 Convert AccountIdentityResponse to a record 2023-01-13 12:36:17 -05:00
Jon Chambers
b4a143b9de Convert RegistrationLockFailure to a record 2023-01-13 12:36:02 -05:00
Jon Chambers
050035dd52 Convert ExternalServiceCredentials to a record 2023-01-13 12:36:02 -05:00
Jon Chambers
7018062606 Explicitly create registration sessions 2023-01-09 15:27:07 -05:00
Jon Chambers
9e1485de0a Assume stored verification codes will always have a session ID instead of a verification code 2023-01-09 15:27:07 -05:00
Jon Chambers
4e358b891f Retire StoredVerificationCode#twilioVerificationSid 2023-01-09 15:27:07 -05:00
Ehren Kret
4044a9df30 stop warning about lack of syntax specification during proto generation 2023-01-09 12:20:07 -06:00
Ehren Kret
5a7b675001 import cleanup on controllers package 2023-01-09 12:20:07 -06:00
Ehren Kret
3be4e4bc57 remove unused exception type 2023-01-09 12:20:07 -06:00
Chris Eager
5de51919bb Remove Subscriptions.PCI 2023-01-05 12:02:34 -06:00
Chris Eager
b02b00818b Remove Subscriptions.PCI attribute 2023-01-04 11:31:46 -06:00
Chris Eager
010f88a2ad Remove Subscriptions.C attribute 2023-01-04 11:31:46 -06:00
Jon Chambers
60edf4835f Add a pni capability to UserCapabilities 2022-12-21 16:26:07 -05:00
Jon Chambers
a60450d931 Convert UserCapabilities to a record 2022-12-21 16:26:07 -05:00
erik-signal
d138fa45df Handle edge cases of Math.abs on integers. 2022-12-20 12:25:04 -05:00
Katherine Yen
2c2c497c12 Define reregistrationIdleDays DistributionSummary with custom expiry 2022-12-20 09:21:24 -08:00
Katherine Yen
cb5d3840d9 Add paymentActivation capability 2022-12-20 09:20:42 -08:00
Fedor Indutny
9aceaa7a4d Introduce ArtController 2022-12-19 11:58:16 -08:00
Katherine Yen
636c8ba384 Add metric for distribution of account idle time at reregistration 2022-12-16 13:50:29 -08:00
Ravi Khadiwala
ac78eb1425 Update to the latest version of the abusive message filter 2022-12-16 11:28:30 -06:00
Ravi Khadiwala
65ad3fe623 Add hCaptcha support 2022-12-16 11:28:30 -06:00
Sergey Skrobotov
dcec90fc52 Update to the latest version of the abusive message filter 2022-12-13 13:30:47 -08:00
Chris Eager
24ac32e6e6 Add PayPalExperienceProfileInput.userAction 2022-12-13 10:03:58 -06:00
Katherine Yen
26f5ffdde3 Enable case-sensitive usernames 2022-12-13 07:59:37 -08:00
Jon Chambers
a883426402 Simplify account cleaner 2022-12-06 16:21:25 -06:00
Chris Eager
2f21e930e2 Add minimum one-time donation amont to validation error map 2022-12-06 16:21:15 -06:00
Chris Eager
5fb158635c Use existing WebApplicationException entity, if available 2022-12-06 16:21:15 -06:00
Chris Eager
6f844f9ebb Update to the latest version of the abusive message filter 2022-12-06 16:20:17 -06:00
Sergey Skrobotov
d88e358016 Update to the latest version of the abusive message filter 2022-12-05 10:07:40 -08:00
Sergey Skrobotov
9cf2635528 some accounts classes refactorings 2022-12-05 09:30:40 -08:00
Chris Eager
d0e7579f13 Revert transaction descriptor 2022-12-01 18:52:45 -06:00
Chris Eager
cda82b0ea0 Update kotlin + Apollo 2022-12-01 18:11:35 -06:00
Chris Eager
2ecbb18fe5 Add support for one-time PayPal donations 2022-12-01 18:11:35 -06:00
Chris Eager
d40d2389a9 Update to Maven 3.8.6 2022-12-01 18:09:38 -06:00
Chris Eager
df8fb5cab7 Move messages cache stale discard to a separate scheduler 2022-12-01 18:09:28 -06:00
katherine-signal
99ad211c01 Enforce minimum amount by currency for one time donations 2022-11-28 11:44:59 -08:00
katherine-signal
fb4ed20ff5 Remove groups v2 capability
* wip removing groups v2 capabilities

* comments

* finish removing groups v2 references

* hardcode gv1migration flag on user capability, remove other references
2022-11-21 09:31:47 -08:00
Jon Chambers
cb50b44d8f Allow the account cleaner to operate on multiple accounts in parallel 2022-11-18 11:15:00 -05:00
Jon Chambers
ae57853ec4 Simplify deletion reason reporting 2022-11-18 11:15:00 -05:00
Jon Chambers
2881c0fd7e Allow the account cleaner to act on all accounts in a crawled chunk 2022-11-18 11:15:00 -05:00
Chris Eager
483fb0968b Use badge name in level configuration for one-time donations 2022-11-18 11:05:23 -05:00
Jon Chambers
4d37418c15 Update to the latest version of the abusive message filter 2022-11-18 10:55:15 -05:00
Jon Chambers
e8ee4b50ff Retire the legacy "abusive hosts" system in favor of newer tools 2022-11-18 10:54:25 -05:00
Chris Eager
4f8aa2eee2 Mark flaky test @Disabled 2022-11-17 13:23:42 -06:00
Chris Eager
397d3cb45a Add consolidated subscription configuration API 2022-11-16 12:27:00 -06:00
Chris Eager
e883d727fb Note deprecation of localized string 2022-11-16 12:09:00 -06:00
Chris Eager
986545a140 Set error_if_incomplete for subscription payment behavior 2022-11-16 12:08:21 -06:00
Sergey Skrobotov
836307b0c7 adding a metric for ipv4/ipv6 requests count 2022-11-15 11:17:01 -08:00
Sergey Skrobotov
b5a75d3079 Update to the latest version of the abusive message filter 2022-11-15 11:16:55 -08:00
Sergey Skrobotov
c32067759c refactoring: use constants for header names 2022-11-15 11:16:49 -08:00
Chris Eager
7fb7abb593 Update to micrometer 1.10.0 2022-11-15 11:16:41 -08:00
Erik Osheim
0d50b58c60 Update to the latest version of the abusive message filter 2022-11-11 17:09:24 -05:00
Chris Eager
bdf4e24266 Update to the latest version of the abusive message filter 2022-11-11 13:54:19 -06:00
Chris Eager
f41bdf1acb Make MessagesController#getPendingMessages fully async 2022-11-11 13:19:57 -06:00
Chris Eager
77d691df59 Always use reactived message processing in WebSocketConnection 2022-11-11 13:14:39 -06:00
Chris Eager
12300761ab Update reactor-bom to 2020.0.24 2022-11-11 13:14:26 -06:00
Chris Eager
25efcbda81 Update lettuce to 6.2.1.RELEASE 2022-11-11 13:14:26 -06:00
Jon Chambers
a01f96e0e4 Temporarily disable account freezing on contention 2022-11-10 18:53:58 -05:00
erik-signal
1d1e3ba79d Add metric to track newly-locked accounts. 2022-11-10 12:55:08 -05:00
Jon Chambers
2c9c50711f Avoid reading from a stale Account after a contested reglock event 2022-11-10 12:41:50 -05:00
Jon Chambers
d3f0ab8c6d Introduce an alternative exchange rate data provider 2022-11-10 10:25:06 -05:00
erik-signal
80a3a8a43c Lock account when number owner lacks registration lock. 2022-11-09 14:03:09 -05:00
Chris Eager
e6e6eb323d Update metric name 2022-11-08 11:15:42 -06:00
Chris Eager
681a5bafb4 Update MessagesManager#getMessagesForDevice
- add `subscribeOn()`
- use `CompletableFuture` for consistency
2022-11-08 09:38:52 -06:00
Chris Eager
5bec89ecc8 Measure individual message timeouts 2022-11-08 09:37:37 -06:00
Chris Eager
69ed0edb74 Revert "Add more detailed queue processing rate metrics"
This reverts commit bbbab4b8a4.
2022-11-08 09:35:39 -06:00
Chris Eager
ad5925908e Change dispatch queues to LinkedBlockingQueues 2022-11-04 11:08:17 -05:00
Chris Eager
d186245c5c Move all receipt sending work to executor 2022-11-04 11:08:06 -05:00
Chris Eager
bbbab4b8a4 Add more detailed queue processing rate metrics 2022-11-04 11:06:38 -05:00
Chris Eager
f83080eb8d Update metric name 2022-11-03 14:50:20 -05:00
Chris Eager
e0178fa0ea Move additional handling of MessagesManager#delete to executor 2022-11-03 13:02:25 -05:00
Chris Eager
c6a79ca176 Enable metrics on messages fluxes 2022-11-03 13:02:25 -05:00
Chris Eager
6426e6cc49 Enable reactor Schedulers metrics 2022-11-03 13:02:25 -05:00
Chris Eager
b13cb098ce lettuce: set publishOnScheduler to true 2022-11-03 13:02:25 -05:00
Jon Chambers
afda5ca98f Add a test for checking push challenge tokens 2022-11-03 11:14:59 -05:00
Chris Eager
eb57d87513 Remove message listener key only after successfully unsubscribing 2022-11-03 11:09:11 -05:00
Chris Eager
fbf6b9826e tests: only call SQLite.setLibraryPath once 2022-11-03 11:08:43 -05:00
Chris Eager
a01b29a6bd set off_session=true for subscription updates 2022-11-02 14:34:26 -05:00
Chris Eager
102992b095 Set off_session=true when creating subscriptions 2022-11-02 11:30:29 -05:00
Chris Eager
bd69905f2e Remove obsolete donation endpoint 2022-11-02 11:29:03 -05:00
Chris Eager
ce5a4bd94a Update wiremock to 2.34.0 2022-11-02 11:24:54 -05:00
Chris Eager
f65a613815 Update jackson to 2.13.4 2022-11-02 11:24:54 -05:00
sergey-signal
d87c8468bd Update to the latest version of the abusive message filter (#1138) 2022-11-02 09:23:38 -07:00
Chris Eager
aa829af43b Handle expected case of empty flux in message deletion 2022-10-31 12:29:25 -05:00
Chris Eager
c10fda8363 Use reactive streams for WebSocket message queue
Initially, uses `ExperimentEnrollmentManager` to do a safe rollout.
2022-10-31 10:35:37 -05:00
Jon Chambers
4252284405 Update to the latest version of the abusive message filter 2022-10-28 10:50:49 -04:00
Jon Chambers
74d65b37a8 Discard old Twilio machinery and rely entirely on the stand-alone registration service 2022-10-28 10:40:37 -04:00
sergey-signal
78f95e4859 Update to the latest version of the abusive message filter (#1132) 2022-10-27 14:01:16 -07:00
Jon Chambers
91626dea45 Count accounts rather than devices that are stories-capable 2022-10-25 16:36:05 -04:00
sergey-signal
5868d9969a minor changes to utility classes (#1127) 2022-10-25 08:48:56 -07:00
erik-signal
90490c9c84 Clean up the TestClock code a bit more. 2022-10-21 15:27:15 -04:00
Chris Eager
8ea794baef Add additional handling for nullable field in recurring donation record 2022-10-21 12:56:39 -05:00
Chris Eager
70a6c3e8e5 Update to libsignal-server 0.21.1 2022-10-21 12:54:18 -05:00
Jon Chambers
4813803c49 Add .java-version to .gitignore 2022-10-21 12:40:11 -04:00
erik-signal
fe60cf003f Clean up testing with clocks. 2022-10-21 12:39:47 -04:00
erik-signal
0c357bc340 Add metrics tracking story capability adoption. 2022-10-20 12:25:03 -04:00
Chris Eager
b711288faa Run GitHub Action in a container 2022-10-18 16:59:35 -05:00
Jon Chambers
44a5d86641 Revert "Update to libsignal-server 0.21.0"
This reverts commit cccccb4dd6.
2022-10-18 11:44:50 -04:00
Jon Chambers
e7048aa9cf Allow the reconciliation client to trust multiple CA certificates to facilitate certificate rotation 2022-10-18 11:17:47 -04:00
Jon Chambers
0120a85c39 Allow HTTP clients to trust multiple certificates to support certificate rollover 2022-10-18 11:17:47 -04:00
Jon Chambers
a41d047f58 Retire CertificateExpirationGauge in favor of other expiration monitoring tools 2022-10-18 11:17:47 -04:00
Chris Eager
cccccb4dd6 Update to libsignal-server 0.21.0 2022-10-18 11:17:29 -04:00
Jon Chambers
0a64e31625 Check verification codes for changing phone numbers against the stand-alone registration service when possible 2022-10-18 11:17:15 -04:00
Jon Chambers
3c6c6c3706 Use the gRPC BOM instead of calling out dependencies individually 2022-10-18 11:16:56 -04:00
Jon Chambers
8088b58b3b Clarify default value for includeE164 2022-10-18 11:16:06 -04:00
erik-signal
a7d5d51fb4 Improve testing of MultiRecipientMessageProvider 2022-10-17 16:50:39 -04:00
Chris Eager
378d7987a8 device capabilities: prevent stories downgrade 2022-10-17 15:25:13 -04:00
erik-signal
3e0baf82a4 Filter unknown UUIDs for /multi_recipient&story=true. 2022-10-13 15:33:51 -04:00
Chris Eager
7a2683a06b Remove /.tx/config from .gitignore 2022-10-11 15:04:50 -05:00
erik-signal
17a3c90286 Add "urgent" query parameter to /v1/messages/multi_recipient endpoint. 2022-10-11 11:10:11 -04:00
Chris Eager
6341770768 Update SubscriptionManager to store processor+customerId in a single attribute and a map
- add `type` query parameter to `/v1/subscription/{subscriberId}/create_payment_method`
2022-10-07 14:26:17 -05:00
Jon Chambers
308437ec93 Resolve gRPC/Netty version conflicts 2022-10-06 16:23:47 -04:00
Jon Chambers
d3d4916d6c Update to the latest version of the abusive message filter 2022-10-06 15:43:37 -04:00
Jon Chambers
d2fa00f0c6 Add experiment to test standalone registration service 2022-10-06 15:42:53 -04:00
erik-signal
d6c9652a70 Fix internal server error when sending stories to unknown recipient. 2022-10-06 13:53:57 -04:00
Jon Chambers
0d20b73e76 Update to the latest version of the abusive message filter 2022-10-05 15:20:49 -04:00
Jon Chambers
3c655cdd5a Migrate to "regionCode" instead of "region" to avoid tag name conflicts 2022-10-05 15:15:46 -04:00
Jon Chambers
fc5cd3a9ca Update to protobuf-java 3.21.7 2022-10-05 15:15:34 -04:00
Jon Chambers
83ab926f96 Add a dimension for story messages 2022-10-05 15:15:22 -04:00
erik-signal
56e54e0724 Update to the latest version of the abusive message filter 2022-10-05 13:19:47 -04:00
erik-signal
544e4fb89a Adjust routing for stories. 2022-10-05 12:20:42 -04:00
erik-signal
966c3a8f47 Add routing for stories. 2022-10-05 10:44:50 -04:00
Ravi Khadiwala
c2ab72c77e Update to the latest version of the abusive message filter 2022-09-30 12:57:21 -05:00
Ravi Khadiwala
4468ee3142 Update to the latest version of the abusive message filter 2022-09-30 12:10:02 -05:00
Ravi Khadiwala
c82c2c0ba4 Add country tag to twilio failures 2022-09-30 12:03:46 -05:00
Ravi Khadiwala
6e595a0959 add an optionals utility and fix push challenge metric 2022-09-30 12:02:47 -05:00
Ravi Khadiwala
a79d709039 Return 403 when a push challenge is incorrect 2022-09-30 12:02:47 -05:00
Ravi Khadiwala
538a07542e Update to the latest version of the abusive message filter 2022-09-22 11:20:48 -05:00
Ravi Khadiwala
07ed765250 Update abusive message filter and filter account creates 2022-09-20 14:52:18 -05:00
Ravi Khadiwala
2e497b5834 Fix operator order in metric calculation 2022-09-15 14:04:18 -05:00
Ravi Khadiwala
61b3cecd17 Fix missing increment on recaptcha counter 2022-09-14 17:07:26 -05:00
Ravi Khadiwala
a4a666bb80 Add metrics for recaptcha reasons 2022-09-14 16:00:11 -05:00
Ravi Khadiwala
c14621a09f Add metrics for captcha scores 2022-09-14 16:00:11 -05:00
Ravi Khadiwala
d0a8899daf Change discriminator seperator and default width 2022-09-14 15:53:15 -05:00
Chris Eager
65dbcb3e5f Remove duplicate bom from dependencyManagement 2022-09-12 16:54:31 -05:00
Chris Eager
7f725b67c4 Update to the latest version of the abusive message filter 2022-09-12 11:24:37 -05:00
Chris Eager
e25252dc69 Remove unused exception 2022-09-12 11:19:15 -05:00
Chris Eager
8b65c11e1e Update batch check entities from two optional fields to a single field 2022-09-12 11:19:01 -05:00
Chris Eager
320c5eac53 Add support for PNIs at v1/profile/identity_check/batch 2022-09-09 10:55:34 -05:00
Ehren Kret
8199e0d2d5 Set resource field on log entry 2022-09-07 19:37:26 -05:00
Ehren Kret
53387f5a0c Register polymorphic serialization 2022-09-07 19:37:26 -05:00
Ehren Kret
7d171a79d7 Remove redundant @NotNull annotation 2022-09-07 19:37:26 -05:00
Ehren Kret
3b99bb9e78 Log remote config delete events 2022-09-07 19:37:26 -05:00
Ehren Kret
132f026c75 Improve readability of event code 2022-09-07 19:37:26 -05:00
Ehren Kret
abd0f9630c Create GCP Logging implementation of AdminEventLogger 2022-09-07 19:37:26 -05:00
Ehren Kret
a4508ec84f Add new event logging module 2022-09-07 19:37:26 -05:00
Ehren Kret
6119b6ab89 Upgrade java-uuid-generator dependency 2022-09-07 19:37:26 -05:00
Ehren Kret
307ac47ce0 Update DynamoDBLocal dependency version 2022-09-07 19:37:26 -05:00
Ravi Khadiwala
4032ddd4fd Add reserve/confirm for usernames 2022-09-07 11:49:49 -05:00
Chris Eager
98c8dc05f1 Update to the latest version of the abusive message filter 2022-09-07 11:49:01 -05:00
Chris Eager
4c677ec2da Remove deprecated /v1/attachments 2022-09-07 11:48:16 -05:00
Chris Eager
c05692e417 Update deprecated CircuitBreakerConfig usage 2022-09-07 11:47:15 -05:00
Chris Eager
1e7aa89664 Update resilience4j to 1.7.0 2022-09-07 11:47:15 -05:00
gram-signal
ae1edf3c5c Remove experiment associated with auth1->auth2 rollout. 2022-08-31 12:10:46 -06:00
gram-signal
b17f41c3e8 Check if dashes work in dynamic configuration keys. 2022-08-29 15:51:37 -06:00
gram-signal
08db4ba54b Update authentication to use HKDF_SHA256. 2022-08-29 14:20:47 -06:00
gram-signal
cb6cc39679 Ignore null identity key. 2022-08-29 13:26:49 -06:00
Jon Chambers
b6bf6c994c Remove a spurious @Nullable annotation 2022-08-26 15:22:23 -04:00
Jon Chambers
3bb4709563 Add CLDR region as a dimension 2022-08-26 12:41:51 -04:00
Jon Chambers
b280c768a4 Allow signup captchas to target CLDR two-letter region codes 2022-08-26 12:41:51 -04:00
Chris Eager
d23e89fb9c Update micrometer to 1.9.3 2022-08-25 13:46:36 -07:00
Chris Eager
3a27bd0318 Update test dependencies 2022-08-25 13:40:46 -07:00
Chris Eager
616513edaf Remove unused jdbi dependency 2022-08-25 13:40:46 -07:00
Chris Eager
09a51020e9 Update stripe-java to 21.2.0 2022-08-25 13:40:46 -07:00
Chris Eager
cb8cb94d1a Update aws java v1 SDK to 1.12.287 2022-08-25 13:40:46 -07:00
Chris Eager
2440dc0089 Update netty to 4.1.79.Final 2022-08-25 13:40:46 -07:00
Chris Eager
2336eef333 Update aws java v2 SDK to 2.17.258 2022-08-25 13:40:46 -07:00
Chris Eager
a0e948627c Update jackson to 2.13.3 2022-08-25 13:40:46 -07:00
Chris Eager
88159af588 Update dropwizard to 2.0.32 2022-08-25 13:40:46 -07:00
Chris Eager
38b77bb550 Update libphonenumber to 8.12.54 2022-08-25 13:40:32 -07:00
Jon Chambers
e72d1d0b6f Stop reading attribute-based messages from the messages table 2022-08-22 13:37:39 -07:00
Ravi Khadiwala
1891622e69 Zero-pad discriminators less than initial width 2022-08-22 13:36:38 -07:00
Chris Eager
628a112b38 Include country code for verify failure 2022-08-19 12:21:05 -07:00
Jon Chambers
50f5d760c9 Use existing tagging tools for keepalive counters 2022-08-16 13:16:19 -07:00
Jon Chambers
7292a88ea3 Record table performance metrics around reported messages 2022-08-16 13:15:30 -07:00
Jon Chambers
07cb3ab576 Add a "sealed sender" dimension to the sent message counter 2022-08-16 13:11:12 -07:00
Chris Eager
27b749abbd Filter expired items from Dynamo 2022-08-16 13:09:47 -07:00
Chris Eager
27f67a077c Add metrics for report-verification-succeeded response 2022-08-16 13:08:16 -07:00
Ravi Khadiwala
393e15815b Rename secondary account key namespace for usernames 2022-08-15 10:51:52 -05:00
Ravi Khadiwala
a7f1cd25b9 Remove UAK normalization code
All accounts now have UAKs in top-level attributes
2022-08-15 10:47:52 -05:00
Ravi Khadiwala
953cd2ae0c Revert "Delete any leftover usernames in the accounts db"
This reverts commit a44c18e9b7.

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

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

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

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

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

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

33
.github/workflows/documentation.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Update Documentation
on:
push:
branches:
- main
jobs:
build:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
- name: Compile and Build OpenAPI file
run: ./mvnw compile
- name: Update Documentation
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cp -r api-doc/target/openapi/signal-server-openapi.yaml /tmp/
git config user.email "github@signal.org"
git config user.name "Documentation Updater"
git fetch origin gh-pages
git checkout gh-pages
cp /tmp/signal-server-openapi.yaml .
git diff --quiet || git commit -a -m "Updating documentation"
git push origin gh-pages -q

34
.github/workflows/integration-tests.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Integration Tests
on:
schedule:
- cron: '30 19 * * MON-FRI'
workflow_dispatch:
jobs:
build:
if: ${{ vars.INTEGRATION_TESTS_BUCKET != '' }}
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
- uses: aws-actions/configure-aws-credentials@v2
name: Configure AWS credentials from Test account
with:
role-to-assume: ${{ vars.AWS_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Fetch integration utils library
run: |
mkdir -p integration-tests/.libs
mkdir -p integration-tests/src/main/resources
wget -O integration-tests/.libs/software.amazon.awssdk-sso.jar https://repo1.maven.org/maven2/software/amazon/awssdk/sso/2.19.8/sso-2.19.8.jar
aws s3 cp "s3://${{ vars.INTEGRATION_TESTS_BUCKET }}/config-latest.yml" integration-tests/src/main/resources/config.yml
- name: Run and verify integration tests
run: ./mvnw clean compile test-compile failsafe:integration-test failsafe:verify

View File

@@ -1,18 +1,26 @@
name: Service CI
on: [push]
on:
push:
branches-ignore:
- gh-pages
jobs:
build:
runs-on: ubuntu-latest
container: ubuntu:22.04
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@3bc31aaf88e8fc94dc1e632d48af61be5ca8721c
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Set up JDK 21
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
with:
distribution: 'adopt'
java-version: 11
distribution: 'temurin'
java-version: 21
cache: 'maven'
env:
# work around an issue with actions/runner setting an incorrect HOME in containers, which breaks maven caching
# https://github.com/actions/setup-java/issues/356
HOME: /root
- name: Build with Maven
run: mvn -e -B verify
run: ./mvnw -e -B verify

6
.gitignore vendored
View File

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

8
.gitmodules vendored
View File

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

View File

@@ -4,6 +4,6 @@
<extension>
<groupId>fr.brouillard.oss</groupId>
<artifactId>jgitver-maven-plugin</artifactId>
<version>1.7.1</version>
<version>1.9.0</version>
</extension>
</extensions>

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

Binary file not shown.

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

@@ -0,0 +1,20 @@
# 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.9.4/apache-maven-3.9.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
distributionSha256Sum=e896b60329a71b719d77bb4388b251a50aebcd73c62f69d510c858ce360afe0f
wrapperSha256Sum=e63a53cfb9c4d291ebe3c2b0edacb7622bbc480326beaa5a0456e412f52f066a

View File

@@ -296,7 +296,7 @@ commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
procedures, authorization keysManager, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object

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-2023 Signal Messenger, LLC
Licensed under the AGPLv3: https://www.gnu.org/licenses/agpl-3.0.html

53
api-doc/pom.xml Normal file
View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>TextSecureServer</artifactId>
<groupId>org.whispersystems.textsecure</groupId>
<version>JGITVER</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-doc</artifactId>
<dependencies>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>service</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>${swagger.version}</version>
<configuration>
<outputFileName>signal-server-openapi</outputFileName>
<outputPath>${project.build.directory}/openapi</outputPath>
<outputFormat>YAML</outputFormat>
<configurationFilePath>${project.basedir}/src/main/resources/openapi/openapi-configuration.yaml
</configurationFilePath>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>resolve</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<!-- we don't want jib to execute on this module -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.openapi;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.dropwizard.auth.Auth;
import io.swagger.v3.jaxrs2.ResolvedParameter;
import io.swagger.v3.jaxrs2.ext.AbstractOpenAPIExtension;
import io.swagger.v3.jaxrs2.ext.OpenAPIExtension;
import io.swagger.v3.oas.models.Components;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import javax.ws.rs.Consumes;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
/**
* One of the extension mechanisms of Swagger Core library (OpenAPI processor) is via custom implementations
* of the {@link AbstractOpenAPIExtension} class.
* <p/>
* The purpose of this extension is to customize certain aspects of the OpenAPI model generation on a lower level.
* This extension works in coordination with {@link OpenApiReader} that has access to the model on a higher level.
* <p/>
* The extension is enabled by being listed in {@code META-INF/services/io.swagger.v3.jaxrs2.ext.OpenAPIExtension} file.
* @see ServiceLoader
* @see OpenApiReader
* @see <a href="https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Extensions">Swagger 2.X Extensions</a>
*/
public class OpenApiExtension extends AbstractOpenAPIExtension {
public static final ResolvedParameter AUTHENTICATED_ACCOUNT = new ResolvedParameter();
public static final ResolvedParameter OPTIONAL_AUTHENTICATED_ACCOUNT = new ResolvedParameter();
/**
* When parsing endpoint methods, Swagger will treat the first parameter not annotated as header/path/query param
* as a request body (and will ignore other not annotated parameters). In our case, this behavior conflicts with
* the {@code @Auth}-annotated parameters. Here we're checking if parameters are known to be anything other than
* a body and return an appropriate {@link ResolvedParameter} representation.
*/
@Override
public ResolvedParameter extractParameters(
final List<Annotation> annotations,
final Type type,
final Set<Type> typesToSkip,
final Components components,
final Consumes classConsumes,
final Consumes methodConsumes,
final boolean includeRequestBody,
final JsonView jsonViewAnnotation,
final Iterator<OpenAPIExtension> chain) {
if (annotations.stream().anyMatch(a -> a.annotationType().equals(Auth.class))) {
// this is the case of authenticated endpoint,
if (type instanceof SimpleType simpleType
&& simpleType.getRawClass().equals(AuthenticatedAccount.class)) {
return AUTHENTICATED_ACCOUNT;
}
if (type instanceof SimpleType simpleType
&& isOptionalOfType(simpleType, AuthenticatedAccount.class)) {
return OPTIONAL_AUTHENTICATED_ACCOUNT;
}
}
return super.extractParameters(
annotations,
type,
typesToSkip,
components,
classConsumes,
methodConsumes,
includeRequestBody,
jsonViewAnnotation,
chain);
}
private static boolean isOptionalOfType(final SimpleType simpleType, final Class<?> expectedType) {
if (!simpleType.getRawClass().equals(Optional.class)) {
return false;
}
final List<JavaType> typeParameters = simpleType.getBindings().getTypeParameters();
if (typeParameters.isEmpty()) {
return false;
}
return typeParameters.get(0) instanceof SimpleType optionalParameterType
&& optionalParameterType.getRawClass().equals(expectedType);
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.openapi;
import static com.google.common.base.MoreObjects.firstNonNull;
import static org.signal.openapi.OpenApiExtension.AUTHENTICATED_ACCOUNT;
import static org.signal.openapi.OpenApiExtension.OPTIONAL_AUTHENTICATED_ACCOUNT;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.ImmutableList;
import io.swagger.v3.jaxrs2.Reader;
import io.swagger.v3.jaxrs2.ResolvedParameter;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.Consumes;
/**
* One of the extension mechanisms of Swagger Core library (OpenAPI processor) is via custom implementations
* of the {@link Reader} class.
* <p/>
* The purpose of this extension is to customize certain aspects of the OpenAPI model generation on a higher level.
* This extension works in coordination with {@link OpenApiExtension} that has access to the model on a lower level.
* <p/>
* The extension is enabled by being listed in {@code resources/openapi/openapi-configuration.yaml} file.
* @see OpenApiExtension
* @see <a href="https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Extensions">Swagger 2.X Extensions</a>
*/
public class OpenApiReader extends Reader {
private static final String AUTHENTICATED_ACCOUNT_AUTH_SCHEMA = "authenticatedAccount";
/**
* Overriding this method allows converting a resolved parameter into other operation entities,
* in this case, into security requirements.
*/
@Override
protected ResolvedParameter getParameters(
final Type type,
final List<Annotation> annotations,
final Operation operation,
final Consumes classConsumes,
final Consumes methodConsumes,
final JsonView jsonViewAnnotation) {
final ResolvedParameter resolved = super.getParameters(
type, annotations, operation, classConsumes, methodConsumes, jsonViewAnnotation);
if (resolved == AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA))
.build());
}
if (resolved == OPTIONAL_AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA))
.add(new SecurityRequirement())
.build());
}
return resolved;
}
}

View File

@@ -0,0 +1 @@
org.signal.openapi.OpenApiExtension

View File

@@ -0,0 +1,25 @@
resourcePackages:
- org.whispersystems.textsecuregcm
prettyPrint: true
cacheTTL: 0
readerClass: org.signal.openapi.OpenApiReader
openAPI:
info:
title: Signal Server API
license:
name: AGPL-3.0-only
url: https://www.gnu.org/licenses/agpl-3.0.txt
servers:
- url: https://chat.signal.org
description: Production service
- url: https://chat.staging.signal.org
description: Staging service
components:
securitySchemes:
authenticatedAccount:
type: http
scheme: basic
description: |
Account authentication is based on Basic authentication schema,
where `username` has a format of `<user_id>[.<device_id>]`. If `device_id` is not specified,
user's `main` device is assumed.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

2
integration-tests/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.libs
src/main/resources/config.yml

62
integration-tests/pom.xml Normal file
View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>TextSecureServer</artifactId>
<groupId>org.whispersystems.textsecure</groupId>
<version>JGITVER</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>integration-tests</artifactId>
<dependencies>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<excludes>
<exclude>**</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${project.basedir}/.libs/software.amazon.awssdk-sso.jar</additionalClasspathElement>
</additionalClasspathElements>
<includes>
<include>**/*.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<!-- we don't want jib to execute on this module -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.Base64;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
public final class Codecs {
private Codecs() {
// utility class
}
@FunctionalInterface
public interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
public static class Base64BasedSerializer<T> extends JsonSerializer<T> {
private final CheckedFunction<T, byte[]> mapper;
public Base64BasedSerializer(final CheckedFunction<T, byte[]> mapper) {
this.mapper = mapper;
}
@Override
public void serialize(final T value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
try {
gen.writeString(Base64.getEncoder().withoutPadding().encodeToString(mapper.apply(value)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static class Base64BasedDeserializer<T> extends JsonDeserializer<T> {
private final CheckedFunction<byte[], T> mapper;
public Base64BasedDeserializer(final CheckedFunction<byte[], T> mapper) {
this.mapper = mapper;
}
@Override
public T deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
try {
return mapper.apply(Base64.getDecoder().decode(p.getValueAsString()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static class ByteArraySerializer extends Base64BasedSerializer<byte[]> {
public ByteArraySerializer() {
super(bytes -> bytes);
}
}
public static class ByteArrayDeserializer extends Base64BasedDeserializer<byte[]> {
public ByteArrayDeserializer() {
super(bytes -> bytes);
}
}
public static class ECPublicKeySerializer extends Base64BasedSerializer<ECPublicKey> {
public ECPublicKeySerializer() {
super(ECPublicKey::serialize);
}
}
public static class ECPublicKeyDeserializer extends Base64BasedDeserializer<ECPublicKey> {
public ECPublicKeyDeserializer() {
super(bytes -> Curve.decodePoint(bytes, 0));
}
}
public static class IdentityKeySerializer extends Base64BasedSerializer<IdentityKey> {
public IdentityKeySerializer() {
super(IdentityKey::serialize);
}
}
public static class IdentityKeyDeserializer extends Base64BasedDeserializer<IdentityKey> {
public IdentityKeyDeserializer() {
super(bytes -> new IdentityKey(bytes, 0));
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import java.time.Clock;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.signal.integration.config.Config;
import org.whispersystems.textsecuregcm.registration.VerificationSession;
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
import org.whispersystems.textsecuregcm.storage.VerificationSessions;
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
public class IntegrationTools {
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
private final VerificationSessionManager verificationSessionManager;
public static IntegrationTools create(final Config config) {
final AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.builder().build();
final DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
config.dynamoDbClientConfiguration(),
credentialsProvider);
final DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
config.dynamoDbClientConfiguration(),
credentialsProvider);
final RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
config.dynamoDbTables().registrationRecovery(), Duration.ofDays(1), dynamoDbClient, dynamoDbAsyncClient);
final VerificationSessions verificationSessions = new VerificationSessions(
dynamoDbAsyncClient, config.dynamoDbTables().verificationSessions(), Clock.systemUTC());
return new IntegrationTools(
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords),
new VerificationSessionManager(verificationSessions)
);
}
private IntegrationTools(
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
final VerificationSessionManager verificationSessionManager) {
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
this.verificationSessionManager = verificationSessionManager;
}
public CompletableFuture<Void> populateRecoveryPassword(final String e164, final byte[] password) {
return registrationRecoveryPasswordsManager.storeForCurrentNumber(e164, password);
}
public CompletableFuture<Optional<String>> peekVerificationSessionPushChallenge(final String sessionId) {
return verificationSessionManager.findForId(sessionId)
.thenApply(maybeSession -> maybeSession.map(VerificationSession::pushChallenge));
}
}

View File

@@ -0,0 +1,326 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.io.Resources;
import com.google.common.net.HttpHeaders;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.net.URL;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.signal.integration.config.Config;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.signal.libsignal.protocol.kem.KEMKeyPair;
import org.signal.libsignal.protocol.kem.KEMKeyType;
import org.signal.libsignal.protocol.kem.KEMPublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
import org.whispersystems.textsecuregcm.entities.RegistrationRequest;
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.HeaderUtils;
import org.whispersystems.textsecuregcm.util.SystemMapper;
public final class Operations {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final Config CONFIG = loadConfigFromClasspath("config.yml");
private static final IntegrationTools INTEGRATION_TOOLS = IntegrationTools.create(CONFIG);
private static final String USER_AGENT = "integration-test";
private static final FaultTolerantHttpClient CLIENT = buildClient();
private Operations() {
// utility class
}
public static TestUser newRegisteredUser(final String number) {
final byte[] registrationPassword = randomBytes(32);
final String accountPassword = Base64.getEncoder().encodeToString(randomBytes(32));
final TestUser user = TestUser.create(number, accountPassword, registrationPassword);
final AccountAttributes accountAttributes = user.accountAttributes();
INTEGRATION_TOOLS.populateRecoveryPassword(number, registrationPassword).join();
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
// register account
final RegistrationRequest registrationRequest = new RegistrationRequest(null,
registrationPassword,
accountAttributes,
true,
new IdentityKey(aciIdentityKeyPair.getPublicKey()),
new IdentityKey(pniIdentityKeyPair.getPublicKey()),
generateSignedECPreKey(1, aciIdentityKeyPair),
generateSignedECPreKey(2, pniIdentityKeyPair),
generateSignedKEMPreKey(3, aciIdentityKeyPair),
generateSignedKEMPreKey(4, pniIdentityKeyPair),
Optional.empty(),
Optional.empty());
final AccountIdentityResponse registrationResponse = apiPost("/v1/registration", registrationRequest)
.authorized(number, accountPassword)
.executeExpectSuccess(AccountIdentityResponse.class);
user.setAciUuid(registrationResponse.uuid());
user.setPniUuid(registrationResponse.pni());
return user;
}
public record PrescribedVerificationNumber(String number, String verificationCode) {}
public static PrescribedVerificationNumber prescribedVerificationNumber() {
return new PrescribedVerificationNumber(
CONFIG.prescribedRegistrationNumber(),
CONFIG.prescribedRegistrationCode());
}
public static void deleteUser(final TestUser user) {
apiDelete("/v1/accounts/me").authorized(user).executeExpectSuccess();
}
public static String peekVerificationSessionPushChallenge(final String sessionId) {
return INTEGRATION_TOOLS.peekVerificationSessionPushChallenge(sessionId).join()
.orElseThrow(() -> new RuntimeException("push challenge not found for the verification session"));
}
public static <T> T sendEmptyRequestAuthenticated(
final String endpoint,
final String method,
final String username,
final String password,
final Class<T> outputType) {
try {
final HttpRequest request = HttpRequest.newBuilder()
.uri(serverUri(endpoint, Collections.emptyList()))
.method(method, HttpRequest.BodyPublishers.noBody())
.header(HttpHeaders.AUTHORIZATION, HeaderUtils.basicAuthHeader(username, password))
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.build();
return CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))
.whenComplete((response, error) -> {
if (error != null) {
logger.error("request error", error);
error.printStackTrace();
} else {
logger.info("response: {}", response.statusCode());
System.out.println("response: " + response.statusCode() + ", " + response.body());
}
})
.thenApply(response -> {
try {
return outputType.equals(Void.class)
? null
: SystemMapper.jsonMapper().readValue(response.body(), outputType);
} catch (final IOException e) {
throw new RuntimeException(e);
}
})
.get();
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private static byte[] randomBytes(int numBytes) {
final byte[] bytes = new byte[numBytes];
new SecureRandom().nextBytes(bytes);
return bytes;
}
public static RequestBuilder apiGet(final String endpoint) {
return new RequestBuilder(HttpRequest.newBuilder().GET(), endpoint);
}
public static RequestBuilder apiDelete(final String endpoint) {
return new RequestBuilder(HttpRequest.newBuilder().DELETE(), endpoint);
}
public static <R> RequestBuilder apiPost(final String endpoint, final R input) {
return RequestBuilder.withJsonBody(endpoint, "POST", input);
}
public static <R> RequestBuilder apiPut(final String endpoint, final R input) {
return RequestBuilder.withJsonBody(endpoint, "PUT", input);
}
public static <R> RequestBuilder apiPatch(final String endpoint, final R input) {
return RequestBuilder.withJsonBody(endpoint, "PATCH", input);
}
private static URI serverUri(final String endpoint, final List<String> queryParams) {
final String query = queryParams.isEmpty()
? StringUtils.EMPTY
: "?" + String.join("&", queryParams);
return URI.create("https://" + CONFIG.domain() + endpoint + query);
}
public static class RequestBuilder {
private final HttpRequest.Builder builder;
private final String endpoint;
private final List<String> queryParams = new ArrayList<>();
private RequestBuilder(final HttpRequest.Builder builder, final String endpoint) {
this.builder = builder;
this.endpoint = endpoint;
}
private static <R> RequestBuilder withJsonBody(final String endpoint, final String method, final R input) {
try {
final byte[] body = SystemMapper.jsonMapper().writeValueAsBytes(input);
return new RequestBuilder(HttpRequest.newBuilder()
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.method(method, HttpRequest.BodyPublishers.ofByteArray(body)), endpoint);
} catch (final JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public RequestBuilder authorized(final TestUser user) {
return authorized(user, Device.PRIMARY_ID);
}
public RequestBuilder authorized(final TestUser user, final byte deviceId) {
final String username = "%s.%d".formatted(user.aciUuid().toString(), deviceId);
return authorized(username, user.accountPassword());
}
public RequestBuilder authorized(final String username, final String password) {
builder.header(HttpHeaders.AUTHORIZATION, HeaderUtils.basicAuthHeader(username, password));
return this;
}
public RequestBuilder queryParam(final String key, final String value) {
queryParams.add("%s=%s".formatted(key, value));
return this;
}
public RequestBuilder header(final String name, final String value) {
builder.header(name, value);
return this;
}
public Pair<Integer, Void> execute() {
return execute(Void.class);
}
public Pair<Integer, Void> executeExpectSuccess() {
final Pair<Integer, Void> execute = execute();
Validate.isTrue(
execute.getLeft() >= 200 && execute.getLeft() < 300,
"Unexpected response code: %d",
execute.getLeft());
return execute;
}
public <T> T executeExpectSuccess(final Class<T> expectedType) {
final Pair<Integer, T> execute = execute(expectedType);
return requireNonNull(execute.getRight());
}
public void executeExpectStatusCode(final int expectedStatusCode) {
final Pair<Integer, Void> execute = execute(Void.class);
Validate.isTrue(
execute.getLeft() == expectedStatusCode,
"Unexpected response code: %d",
execute.getLeft()
);
}
public <T> Pair<Integer, T> execute(final Class<T> expectedType) {
builder.uri(serverUri(endpoint, queryParams))
.header(HttpHeaders.USER_AGENT, USER_AGENT);
return CLIENT.sendAsync(builder.build(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))
.whenComplete((response, error) -> {
if (error != null) {
logger.error("request error", error);
error.printStackTrace();
}
})
.thenApply(response -> {
try {
final T result = expectedType.equals(Void.class)
? null
: SystemMapper.jsonMapper().readValue(response.body(), expectedType);
return Pair.of(response.statusCode(), result);
} catch (final IOException e) {
throw new RuntimeException(e);
}
})
.join();
}
}
private static FaultTolerantHttpClient buildClient() {
try {
return FaultTolerantHttpClient.newBuilder()
.withName("integration-test")
.withExecutor(Executors.newFixedThreadPool(16))
.withRetryExecutor(Executors.newSingleThreadScheduledExecutor())
.withCircuitBreaker(new CircuitBreakerConfiguration())
.withTrustedServerCertificates(CONFIG.rootCert())
.build();
} catch (final CertificateException e) {
throw new RuntimeException(e);
}
}
private static Config loadConfigFromClasspath(final String filename) {
try {
final URL configFileUrl = Resources.getResource(filename);
return SystemMapper.yamlMapper().readValue(Resources.toByteArray(configFileUrl), Config.class);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
private static ECSignedPreKey generateSignedECPreKey(long id, final ECKeyPair identityKeyPair) {
final ECPublicKey pubKey = Curve.generateKeyPair().getPublicKey();
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey.serialize());
return new ECSignedPreKey(id, pubKey, sig);
}
private static KEMSignedPreKey generateSignedKEMPreKey(long id, final ECKeyPair identityKeyPair) {
final KEMPublicKey pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey();
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey.serialize());
return new KEMSignedPreKey(id, pubKey, sig);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.tuple.Pair;
import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
public class TestDevice {
private final byte deviceId;
private final Map<Integer, Pair<IdentityKeyPair, SignedPreKeyRecord>> signedPreKeys = new ConcurrentHashMap<>();
public static TestDevice create(
final byte deviceId,
final IdentityKeyPair aciIdentityKeyPair,
final IdentityKeyPair pniIdentityKeyPair) {
final TestDevice device = new TestDevice(deviceId);
device.addSignedPreKey(aciIdentityKeyPair);
device.addSignedPreKey(pniIdentityKeyPair);
return device;
}
public TestDevice(final byte deviceId) {
this.deviceId = deviceId;
}
public byte deviceId() {
return deviceId;
}
public SignedPreKeyRecord latestSignedPreKey(final IdentityKeyPair identity) {
final int id = signedPreKeys.entrySet()
.stream()
.filter(p -> p.getValue().getLeft().equals(identity))
.mapToInt(Map.Entry::getKey)
.max()
.orElseThrow();
return signedPreKeys.get(id).getRight();
}
public SignedPreKeyRecord addSignedPreKey(final IdentityKeyPair identity) {
try {
final int nextId = signedPreKeys.keySet().stream().mapToInt(k -> k + 1).max().orElse(0);
final ECKeyPair keyPair = Curve.generateKeyPair();
final byte[] signature = Curve.calculateSignature(identity.getPrivateKey(), keyPair.getPublicKey().serialize());
final SignedPreKeyRecord signedPreKeyRecord = new SignedPreKeyRecord(nextId, System.currentTimeMillis(), keyPair, signature);
signedPreKeys.put(nextId, Pair.of(identity, signedPreKeyRecord));
return signedPreKeyRecord;
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.security.SecureRandom;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
import org.signal.libsignal.protocol.util.KeyHelper;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.storage.Device;
public class TestUser {
private final int registrationId;
private final int pniRegistrationId;
private final IdentityKeyPair aciIdentityKey;
private final Map<Byte, TestDevice> devices = new ConcurrentHashMap<>();
private final byte[] unidentifiedAccessKey;
private String phoneNumber;
private IdentityKeyPair pniIdentityKey;
private String accountPassword;
private byte[] registrationPassword;
private UUID aciUuid;
private UUID pniUuid;
public static TestUser create(final String phoneNumber, final String accountPassword, final byte[] registrationPassword) {
// ACI identity key pair
final IdentityKeyPair aciIdentityKey = IdentityKeyPair.generate();
// PNI identity key pair
final IdentityKeyPair pniIdentityKey = IdentityKeyPair.generate();
// registration id
final int registrationId = KeyHelper.generateRegistrationId(false);
final int pniRegistrationId = KeyHelper.generateRegistrationId(false);
// uak
final byte[] unidentifiedAccessKey = new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH];
new SecureRandom().nextBytes(unidentifiedAccessKey);
return new TestUser(
registrationId,
pniRegistrationId,
aciIdentityKey,
phoneNumber,
pniIdentityKey,
unidentifiedAccessKey,
accountPassword,
registrationPassword);
}
public TestUser(
final int registrationId,
final int pniRegistrationId,
final IdentityKeyPair aciIdentityKey,
final String phoneNumber,
final IdentityKeyPair pniIdentityKey,
final byte[] unidentifiedAccessKey,
final String accountPassword,
final byte[] registrationPassword) {
this.registrationId = registrationId;
this.pniRegistrationId = pniRegistrationId;
this.aciIdentityKey = aciIdentityKey;
this.phoneNumber = phoneNumber;
this.pniIdentityKey = pniIdentityKey;
this.unidentifiedAccessKey = unidentifiedAccessKey;
this.accountPassword = accountPassword;
this.registrationPassword = registrationPassword;
devices.put(Device.PRIMARY_ID, TestDevice.create(Device.PRIMARY_ID, aciIdentityKey, pniIdentityKey));
}
public int registrationId() {
return registrationId;
}
public IdentityKeyPair aciIdentityKey() {
return aciIdentityKey;
}
public String phoneNumber() {
return phoneNumber;
}
public IdentityKeyPair pniIdentityKey() {
return pniIdentityKey;
}
public String accountPassword() {
return accountPassword;
}
public byte[] registrationPassword() {
return registrationPassword;
}
public UUID aciUuid() {
return aciUuid;
}
public UUID pniUuid() {
return pniUuid;
}
public AccountAttributes accountAttributes() {
return new AccountAttributes(true, registrationId, pniRegistrationId, "".getBytes(StandardCharsets.UTF_8), "", true, new Device.DeviceCapabilities(false, false, false))
.withUnidentifiedAccessKey(unidentifiedAccessKey)
.withRecoveryPassword(registrationPassword);
}
public void setAciUuid(final UUID aciUuid) {
this.aciUuid = aciUuid;
}
public void setPniUuid(final UUID pniUuid) {
this.pniUuid = pniUuid;
}
public void setPhoneNumber(final String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public void setPniIdentityKey(final IdentityKeyPair pniIdentityKey) {
this.pniIdentityKey = pniIdentityKey;
}
public void setAccountPassword(final String accountPassword) {
this.accountPassword = accountPassword;
}
public void setRegistrationPassword(final byte[] registrationPassword) {
this.registrationPassword = registrationPassword;
}
public PreKeySetPublicView preKeys(final byte deviceId, final boolean pni) {
final IdentityKeyPair identity = pni
? pniIdentityKey
: aciIdentityKey;
final TestDevice device = requireNonNull(devices.get(deviceId));
final SignedPreKeyRecord signedPreKeyRecord = device.latestSignedPreKey(identity);
return new PreKeySetPublicView(
Collections.emptyList(),
identity.getPublicKey(),
new SignedPreKeyPublicView(
signedPreKeyRecord.getId(),
signedPreKeyRecord.getKeyPair().getPublicKey(),
signedPreKeyRecord.getSignature()
)
);
}
public record SignedPreKeyPublicView(
int keyId,
@JsonSerialize(using = Codecs.ECPublicKeySerializer.class)
@JsonDeserialize(using = Codecs.ECPublicKeyDeserializer.class)
ECPublicKey publicKey,
@JsonSerialize(using = Codecs.ByteArraySerializer.class)
@JsonDeserialize(using = Codecs.ByteArrayDeserializer.class)
byte[] signature) {
}
public record PreKeySetPublicView(
List<String> preKeys,
@JsonSerialize(using = Codecs.IdentityKeySerializer.class)
@JsonDeserialize(using = Codecs.IdentityKeyDeserializer.class)
IdentityKey identityKey,
SignedPreKeyPublicView signedPreKey) {
}
}

View File

@@ -0,0 +1,16 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration.config;
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
public record Config(String domain,
String rootCert,
DynamoDbClientConfiguration dynamoDbClientConfiguration,
DynamoDbTables dynamoDbTables,
String prescribedRegistrationNumber,
String prescribedRegistrationCode) {
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration.config;
public record DynamoDbTables(String registrationRecovery,
String verificationSessions) {
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;
import org.signal.libsignal.usernames.BaseUsernameException;
import org.signal.libsignal.usernames.Username;
import org.whispersystems.textsecuregcm.entities.AccountIdentifierResponse;
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.ConfirmUsernameHashRequest;
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashRequest;
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashResponse;
import org.whispersystems.textsecuregcm.entities.UsernameHashResponse;
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
public class AccountTest {
@Test
public void testCreateAccount() throws Exception {
final TestUser user = Operations.newRegisteredUser("+19995550101");
try {
final Pair<Integer, AccountIdentityResponse> execute = Operations.apiGet("/v1/accounts/whoami")
.authorized(user)
.execute(AccountIdentityResponse.class);
assertEquals(HttpStatus.SC_OK, execute.getLeft());
} finally {
Operations.deleteUser(user);
}
}
@Test
public void testCreateAccountAtomic() throws Exception {
final TestUser user = Operations.newRegisteredUser("+19995550201");
try {
final Pair<Integer, AccountIdentityResponse> execute = Operations.apiGet("/v1/accounts/whoami")
.authorized(user)
.execute(AccountIdentityResponse.class);
assertEquals(HttpStatus.SC_OK, execute.getLeft());
} finally {
Operations.deleteUser(user);
}
}
@Test
public void testUsernameOperations() throws Exception {
final TestUser user = Operations.newRegisteredUser("+19995550102");
try {
verifyFullUsernameLifecycle(user);
// no do it again to check changing usernames
verifyFullUsernameLifecycle(user);
} finally {
Operations.deleteUser(user);
}
}
private static void verifyFullUsernameLifecycle(final TestUser user) throws BaseUsernameException {
final String preferred = "test";
final List<Username> candidates = Username.candidatesFrom(preferred, preferred.length(), preferred.length() + 1);
// reserve a username
final ReserveUsernameHashRequest reserveUsernameHashRequest = new ReserveUsernameHashRequest(
candidates.stream().map(Username::getHash).toList());
// try unauthorized
Operations
.apiPut("/v1/accounts/username_hash/reserve", reserveUsernameHashRequest)
.executeExpectStatusCode(HttpStatus.SC_UNAUTHORIZED);
final ReserveUsernameHashResponse reserveUsernameHashResponse = Operations
.apiPut("/v1/accounts/username_hash/reserve", reserveUsernameHashRequest)
.authorized(user)
.executeExpectSuccess(ReserveUsernameHashResponse.class);
// find which one is the reserved username
final byte[] reservedHash = reserveUsernameHashResponse.usernameHash();
final Username reservedUsername = candidates.stream()
.filter(u -> Arrays.equals(u.getHash(), reservedHash))
.findAny()
.orElseThrow();
// confirm a username
final ConfirmUsernameHashRequest confirmUsernameHashRequest = new ConfirmUsernameHashRequest(
reservedUsername.getHash(),
reservedUsername.generateProof(),
"cluck cluck i'm a parrot".getBytes()
);
// try unauthorized
Operations
.apiPut("/v1/accounts/username_hash/confirm", confirmUsernameHashRequest)
.executeExpectStatusCode(HttpStatus.SC_UNAUTHORIZED);
Operations
.apiPut("/v1/accounts/username_hash/confirm", confirmUsernameHashRequest)
.authorized(user)
.executeExpectSuccess(UsernameHashResponse.class);
// lookup username
final AccountIdentifierResponse accountIdentifierResponse = Operations
.apiGet("/v1/accounts/username_hash/" + Base64.getUrlEncoder().encodeToString(reservedHash))
.executeExpectSuccess(AccountIdentifierResponse.class);
assertEquals(new AciServiceIdentifier(user.aciUuid()), accountIdentifierResponse.uuid());
// try authorized
Operations
.apiGet("/v1/accounts/username_hash/" + Base64.getUrlEncoder().encodeToString(reservedHash))
.authorized(user)
.executeExpectStatusCode(HttpStatus.SC_BAD_REQUEST);
// delete username
Operations
.apiDelete("/v1/accounts/username_hash")
.authorized(user)
.executeExpectSuccess();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.entities.IncomingMessage;
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntityList;
import org.whispersystems.textsecuregcm.entities.SendMessageResponse;
import org.whispersystems.textsecuregcm.storage.Device;
public class MessagingTest {
@Test
public void testSendMessageUnsealed() {
final TestUser userA = Operations.newRegisteredUser("+19995550102");
final TestUser userB = Operations.newRegisteredUser("+19995550103");
try {
final byte[] expectedContent = "Hello, World!".getBytes(StandardCharsets.UTF_8);
final String contentBase64 = Base64.getEncoder().encodeToString(expectedContent);
final IncomingMessage message = new IncomingMessage(1, Device.PRIMARY_ID, userB.registrationId(), contentBase64);
final IncomingMessageList messages = new IncomingMessageList(List.of(message), false, true, System.currentTimeMillis());
final Pair<Integer, SendMessageResponse> sendMessage = Operations
.apiPut("/v1/messages/%s".formatted(userB.aciUuid().toString()), messages)
.authorized(userA)
.execute(SendMessageResponse.class);
final Pair<Integer, OutgoingMessageEntityList> receiveMessages = Operations.apiGet("/v1/messages")
.authorized(userB)
.execute(OutgoingMessageEntityList.class);
final byte[] actualContent = receiveMessages.getRight().messages().get(0).content();
assertArrayEquals(expectedContent, actualContent);
} finally {
Operations.deleteUser(userA);
Operations.deleteUser(userB);
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.entities.CreateVerificationSessionRequest;
import org.whispersystems.textsecuregcm.entities.SubmitVerificationCodeRequest;
import org.whispersystems.textsecuregcm.entities.UpdateVerificationSessionRequest;
import org.whispersystems.textsecuregcm.entities.VerificationCodeRequest;
import org.whispersystems.textsecuregcm.entities.VerificationSessionResponse;
public class RegistrationTest {
@Test
public void testRegistration() throws Exception {
final UpdateVerificationSessionRequest originalRequest = new UpdateVerificationSessionRequest(
"test", UpdateVerificationSessionRequest.PushTokenType.FCM, null, null, null, null);
final Operations.PrescribedVerificationNumber params = Operations.prescribedVerificationNumber();
final CreateVerificationSessionRequest input = new CreateVerificationSessionRequest(params.number(),
originalRequest);
final VerificationSessionResponse verificationSessionResponse = Operations
.apiPost("/v1/verification/session", input)
.executeExpectSuccess(VerificationSessionResponse.class);
final String sessionId = verificationSessionResponse.id();
final String pushChallenge = Operations.peekVerificationSessionPushChallenge(sessionId);
// supply push challenge
final UpdateVerificationSessionRequest updatedRequest = new UpdateVerificationSessionRequest(
"test", UpdateVerificationSessionRequest.PushTokenType.FCM, pushChallenge, null, null, null);
final VerificationSessionResponse pushChallengeSupplied = Operations
.apiPatch("/v1/verification/session/%s".formatted(sessionId), updatedRequest)
.executeExpectSuccess(VerificationSessionResponse.class);
Assertions.assertTrue(pushChallengeSupplied.allowedToRequestCode());
// request code
final VerificationCodeRequest verificationCodeRequest = new VerificationCodeRequest(
VerificationCodeRequest.Transport.SMS, "android-ng");
final VerificationSessionResponse codeRequested = Operations
.apiPost("/v1/verification/session/%s/code".formatted(sessionId), verificationCodeRequest)
.executeExpectSuccess(VerificationSessionResponse.class);
// verify code
final SubmitVerificationCodeRequest submitVerificationCodeRequest = new SubmitVerificationCodeRequest(
params.verificationCode());
final VerificationSessionResponse codeVerified = Operations
.apiPut("/v1/verification/session/%s/code".formatted(sessionId), submitVerificationCodeRequest)
.executeExpectSuccess(VerificationSessionResponse.class);
}
}

308
mvnw vendored Executable file
View File

@@ -0,0 +1,308 @@
#!/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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# 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
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -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 "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); 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 2>/dev/null; \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
# 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/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# 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.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
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 "$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
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

205
mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,205 @@
@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 Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@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 WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_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 WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_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('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@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%

289
pom.xml
View File

@@ -14,47 +14,68 @@
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>dynamodb-local-oregon</id>
<name>DynamoDB Local Release Repository</name>
<url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url>
</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>api-doc</module>
<module>integration-tests</module>
<module>service</module>
<module>websocket-resources</module>
</modules>
<properties>
<aws.sdk.version>1.11.939</aws.sdk.version>
<aws.sdk2.version>2.16.66</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.22</dropwizard.version>
<aws.sdk2.version>2.23.8</aws.sdk2.version>
<braintree.version>3.27.0</braintree.version>
<commons-csv.version>1.10.0</commons-csv.version>
<commons-io.version>2.14.0</commons-io.version>
<dropwizard.version>3.0.4</dropwizard.version>
<dropwizard-metrics-datadog.version>1.1.13</dropwizard-metrics-datadog.version>
<gson.version>2.8.8</gson.version>
<guava.version>30.1.1-jre</guava.version>
<google-cloud-libraries.version>26.25.0</google-cloud-libraries.version>
<grpc.version>1.58.0</grpc.version> <!-- should be kept in sync with the value from Google libraries-bom -->
<gson.version>2.10.1</gson.version>
<!-- several libraries (AWS, Google Cloud) use Apache http components transitively, and we need to align them -->
<httpcore.version>4.4.16</httpcore.version>
<httpclient.version>4.5.14</httpclient.version>
<jackson.version>2.16.0</jackson.version>
<jaxb.version>2.3.1</jaxb.version>
<jedis.version>2.9.0</jedis.version>
<lettuce.version>6.0.4.RELEASE</lettuce.version>
<libphonenumber.version>8.12.33</libphonenumber.version>
<logstash.logback.version>6.6</logstash.logback.version>
<micrometer.version>1.5.3</micrometer.version>
<mockito.version>3.11.1</mockito.version>
<netty.version>4.1.65.Final</netty.version>
<netty.tcnative-boringssl-static.version>2.0.39.Final</netty.tcnative-boringssl-static.version>
<opentest4j.version>1.2.0</opentest4j.version>
<postgresql.version>9.4-1201-jdbc41</postgresql.version>
<protobuf.version>3.17.1</protobuf.version>
<pushy.version>0.15.0</pushy.version>
<resilience4j.version>1.5.0</resilience4j.version>
<junit-pioneer.version>2.1.0</junit-pioneer.version>
<jsr305.version>3.0.2</jsr305.version>
<kotlin.version>1.9.10</kotlin.version>
<kotlinx-serialization.version>1.5.1</kotlinx-serialization.version>
<lettuce.version>6.2.6.RELEASE</lettuce.version>
<libphonenumber.version>8.13.23</libphonenumber.version>
<logstash.logback.version>7.3</logstash.logback.version>
<log4j-bom.version>2.21.0</log4j-bom.version>
<luajava.version>3.4.0</luajava.version>
<micrometer.version>1.10.10</micrometer.version>
<netty.version>4.1.96.Final</netty.version>
<opentest4j.version>1.3.0</opentest4j.version>
<protobuf.version>3.24.3</protobuf.version> <!-- should be kept in sync with the value from Google libraries-bom -->
<pushy.version>0.15.2</pushy.version>
<reactive.grpc.version>1.2.4</reactive.grpc.version>
<reactor-bom.version>2022.0.12</reactor-bom.version> <!-- 3.5.x, see https://github.com/reactor/reactor#bom-versioning-scheme -->
<resilience4j.version>1.7.0</resilience4j.version>
<semver4j.version>3.1.0</semver4j.version>
<slf4j.version>1.7.30</slf4j.version>
<stripe.version>20.79.0</stripe.version>
<slf4j.version>2.0.9</slf4j.version>
<stripe.version>23.10.0</stripe.version>
<swagger.version>2.2.17</swagger.version>
<vavr.version>0.10.4</vavr.version>
<!-- 21.0.1_12-jre-jammy -->
<docker.image.sha256>2d00f6910282a7a20ae7747b8f5e2371f7d55f06daed6bf60a323fcc7eaa3da8</docker.image.sha256>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@@ -65,6 +86,13 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>${jackson.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-dependencies</artifactId>
@@ -72,6 +100,13 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Needed for gRPC with Java 9+ -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
@@ -79,13 +114,6 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
<version>${aws.sdk.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
@@ -96,10 +124,15 @@
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>20.9.0</version>
<version>${google-cloud-libraries.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.salesforce.servicelibs</groupId>
<artifactId>reactor-grpc-stub</artifactId>
<version>${reactive.grpc.version}</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bom</artifactId>
@@ -114,7 +147,20 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>${reactor-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-bom</artifactId>
<version>${kotlin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
@@ -125,11 +171,6 @@
<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>
@@ -145,11 +186,6 @@
<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>
@@ -161,10 +197,9 @@
<version>${lettuce.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${netty.tcnative-boringssl-static.version}</version>
<scope>runtime</scope>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>${vavr.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
@@ -192,30 +227,12 @@
<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.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -227,11 +244,6 @@
<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>
@@ -240,7 +252,7 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.2</version>
<version>9.5</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -248,11 +260,54 @@
<artifactId>stripe-java</artifactId>
<version>${stripe.version}</version>
</dependency>
<dependency>
<groupId>com.braintreepayments.gateway</groupId>
<artifactId>braintree-java</artifactId>
<version>${braintree.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>${jsr305.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.39.0</version>
</dependency>
<dependency>
<groupId>org.signal.forks</groupId>
<artifactId>noise-java</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>${log4j-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>${httpcore.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -264,9 +319,14 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.31.0</version>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-crt-client</artifactId>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<!-- use standalone until Dropwizard 4 + jakarta.* -->
<artifactId>wiremock-standalone</artifactId>
<version>3.3.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
@@ -282,7 +342,6 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -291,8 +350,14 @@
<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>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>${junit-pioneer.version}</version>
<scope>test</scope>
</dependency>
@@ -300,22 +365,22 @@
<profiles>
<profile>
<id>include-abusive-message-filter</id>
<id>include-spam-filter</id>
<activation>
<file>
<exists>abusive-message-filter/pom.xml</exists>
<exists>spam-filter/pom.xml</exists>
</file>
</activation>
<modules>
<module>abusive-message-filter</module>
<module>spam-filter</module>
</modules>
</profile>
<profile>
<id>exclude-abusive-message-filter</id>
<id>exclude-spam-filter</id>
<activation>
<file>
<missing>abusive-message-filter/pom.xml</missing>
<missing>spam-filter/pom.xml</missing>
</file>
</activation>
</profile>
@@ -329,6 +394,15 @@
<version>1.7.0</version>
</extension>
</extensions>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
@@ -336,14 +410,28 @@
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.18.0:exe:${os.detected.classifier}</protocArtifact>
<checkStaleness>true</checkStaleness>
<checkStaleness>false</checkStaleness>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protocPlugins>
<protocPlugin>
<id>reactor-grpc</id>
<groupId>com.salesforce.servicelibs</groupId>
<artifactId>reactor-grpc</artifactId>
<version>${reactive.grpc.version}</version>
<mainClass>com.salesforce.reactorgrpc.ReactorGrpcGenerator</mainClass>
</protocPlugin>
</protocPlugins>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
<goal>test-compile</goal>
<goal>test-compile-custom</goal>
</goals>
</execution>
</executions>
@@ -352,17 +440,16 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
<release>21</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
@@ -375,7 +462,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
<version>3.3.0</version>
<executions>
<execution>
<id>copy</id>
@@ -395,7 +482,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<version>3.1.2</version>
<configuration>
<systemProperties>
<property>
@@ -409,7 +496,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M3</version>
<version>3.3.0</version>
<executions>
<execution>
<goals>
@@ -419,7 +506,7 @@
<rules>
<dependencyConvergence/>
<requireMavenVersion>
<version>3.0.0</version>
<version>3.8.6</version>
</requireMavenVersion>
</rules>
</configuration>
@@ -430,7 +517,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.0.0-M1</version>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
@@ -439,7 +526,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.0.0-M1</version>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>TextSecureServer</artifactId>
<groupId>org.whispersystems.textsecure</groupId>
<version>JGITVER</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redis-dispatch</artifactId>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,11 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch;
public interface DispatchChannel {
void onDispatchMessage(String channel, byte[] message);
void onDispatchSubscribed(String channel);
void onDispatchUnsubscribed(String channel);
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class DispatchManager extends Thread {
private final Logger logger = LoggerFactory.getLogger(DispatchManager.class);
private final Executor executor = Executors.newCachedThreadPool();
private final Map<String, DispatchChannel> subscriptions = new ConcurrentHashMap<>();
private final Optional<DispatchChannel> deadLetterChannel;
private final RedisPubSubConnectionFactory redisPubSubConnectionFactory;
private PubSubConnection pubSubConnection;
private volatile boolean running;
public DispatchManager(RedisPubSubConnectionFactory redisPubSubConnectionFactory,
Optional<DispatchChannel> deadLetterChannel)
{
this.redisPubSubConnectionFactory = redisPubSubConnectionFactory;
this.deadLetterChannel = deadLetterChannel;
}
@Override
public void start() {
this.pubSubConnection = redisPubSubConnectionFactory.connect();
this.running = true;
super.start();
}
public void shutdown() {
this.running = false;
this.pubSubConnection.close();
}
public synchronized void subscribe(String name, DispatchChannel dispatchChannel) {
Optional<DispatchChannel> previous = Optional.ofNullable(subscriptions.get(name));
subscriptions.put(name, dispatchChannel);
try {
pubSubConnection.subscribe(name);
} catch (IOException e) {
logger.warn("Subscription error", e);
}
previous.ifPresent(channel -> dispatchUnsubscription(name, channel));
}
public synchronized void unsubscribe(String name, DispatchChannel channel) {
Optional<DispatchChannel> subscription = Optional.ofNullable(subscriptions.get(name));
if (subscription.isPresent() && subscription.get() == channel) {
subscriptions.remove(name);
try {
pubSubConnection.unsubscribe(name);
} catch (IOException e) {
logger.warn("Unsubscribe error", e);
}
dispatchUnsubscription(name, subscription.get());
}
}
public boolean hasSubscription(String name) {
return subscriptions.containsKey(name);
}
@Override
public void run() {
while (running) {
try {
PubSubReply reply = pubSubConnection.read();
switch (reply.getType()) {
case UNSUBSCRIBE: break;
case SUBSCRIBE: dispatchSubscribe(reply); break;
case MESSAGE: dispatchMessage(reply); break;
default: throw new AssertionError("Unknown pubsub reply type! " + reply.getType());
}
} catch (IOException e) {
logger.warn("***** PubSub Connection Error *****", e);
if (running) {
this.pubSubConnection.close();
this.pubSubConnection = redisPubSubConnectionFactory.connect();
resubscribeAll();
}
}
}
logger.warn("DispatchManager Shutting Down...");
}
private void dispatchSubscribe(final PubSubReply reply) {
Optional<DispatchChannel> subscription = Optional.ofNullable(subscriptions.get(reply.getChannel()));
if (subscription.isPresent()) {
dispatchSubscription(reply.getChannel(), subscription.get());
} else {
logger.info("Received subscribe event for non-existing channel: " + reply.getChannel());
}
}
private void dispatchMessage(PubSubReply reply) {
Optional<DispatchChannel> subscription = Optional.ofNullable(subscriptions.get(reply.getChannel()));
if (subscription.isPresent()) {
dispatchMessage(reply.getChannel(), subscription.get(), reply.getContent().get());
} else if (deadLetterChannel.isPresent()) {
dispatchMessage(reply.getChannel(), deadLetterChannel.get(), reply.getContent().get());
} else {
logger.warn("Received message for non-existing channel, with no dead letter handler: " + reply.getChannel());
}
}
private void resubscribeAll() {
new Thread(() -> {
synchronized (DispatchManager.this) {
try {
for (String name : subscriptions.keySet()) {
pubSubConnection.subscribe(name);
}
} catch (IOException e) {
logger.warn("***** RESUBSCRIPTION ERROR *****", e);
}
}
}).start();
}
private void dispatchMessage(final String name, final DispatchChannel channel, final byte[] message) {
executor.execute(() -> channel.onDispatchMessage(name, message));
}
private void dispatchSubscription(final String name, final DispatchChannel channel) {
executor.execute(() -> channel.onDispatchSubscribed(name));
}
private void dispatchUnsubscription(final String name, final DispatchChannel channel) {
executor.execute(() -> channel.onDispatchUnsubscribed(name));
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.io;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class RedisInputStream {
private static final byte CR = 0x0D;
private static final byte LF = 0x0A;
private final InputStream inputStream;
public RedisInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public String readLine() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
boolean foundCr = false;
while (true) {
int character = inputStream.read();
if (character == -1) {
throw new IOException("Stream closed!");
}
baos.write(character);
if (foundCr && character == LF) break;
else if (character == CR) foundCr = true;
else if (foundCr) foundCr = false;
}
byte[] data = baos.toByteArray();
return new String(data, 0, data.length-2);
}
public byte[] readFully(int size) throws IOException {
byte[] result = new byte[size];
int offset = 0;
int remaining = result.length;
while (remaining > 0) {
int read = inputStream.read(result, offset, remaining);
if (read < 0) {
throw new IOException("Stream closed!");
}
offset += read;
remaining -= read;
}
return result;
}
public void close() throws IOException {
inputStream.close();
}
}

View File

@@ -1,13 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.io;
import org.whispersystems.dispatch.redis.PubSubConnection;
public interface RedisPubSubConnectionFactory {
PubSubConnection connect();
}

View File

@@ -1,123 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.dispatch.io.RedisInputStream;
import org.whispersystems.dispatch.redis.protocol.ArrayReplyHeader;
import org.whispersystems.dispatch.redis.protocol.IntReply;
import org.whispersystems.dispatch.redis.protocol.StringReplyHeader;
import org.whispersystems.dispatch.util.Util;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
public class PubSubConnection {
private final Logger logger = LoggerFactory.getLogger(PubSubConnection.class);
private static final byte[] UNSUBSCRIBE_TYPE = {'u', 'n', 's', 'u', 'b', 's', 'c', 'r', 'i', 'b', 'e' };
private static final byte[] SUBSCRIBE_TYPE = {'s', 'u', 'b', 's', 'c', 'r', 'i', 'b', 'e' };
private static final byte[] MESSAGE_TYPE = {'m', 'e', 's', 's', 'a', 'g', 'e' };
private static final byte[] SUBSCRIBE_COMMAND = {'S', 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E', ' ' };
private static final byte[] UNSUBSCRIBE_COMMAND = {'U', 'N', 'S', 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E', ' '};
private static final byte[] CRLF = {'\r', '\n' };
private final OutputStream outputStream;
private final RedisInputStream inputStream;
private final Socket socket;
private final AtomicBoolean closed;
public PubSubConnection(Socket socket) throws IOException {
this.socket = socket;
this.outputStream = socket.getOutputStream();
this.inputStream = new RedisInputStream(new BufferedInputStream(socket.getInputStream()));
this.closed = new AtomicBoolean(false);
}
public void subscribe(String channelName) throws IOException {
if (closed.get()) throw new IOException("Connection closed!");
byte[] command = Util.combine(SUBSCRIBE_COMMAND, channelName.getBytes(), CRLF);
outputStream.write(command);
}
public void unsubscribe(String channelName) throws IOException {
if (closed.get()) throw new IOException("Connection closed!");
byte[] command = Util.combine(UNSUBSCRIBE_COMMAND, channelName.getBytes(), CRLF);
outputStream.write(command);
}
public PubSubReply read() throws IOException {
if (closed.get()) throw new IOException("Connection closed!");
ArrayReplyHeader replyHeader = new ArrayReplyHeader(inputStream.readLine());
if (replyHeader.getElementCount() != 3) {
throw new IOException("Received array reply header with strange count: " + replyHeader.getElementCount());
}
StringReplyHeader replyTypeHeader = new StringReplyHeader(inputStream.readLine());
byte[] replyType = inputStream.readFully(replyTypeHeader.getStringLength());
inputStream.readLine();
if (Arrays.equals(SUBSCRIBE_TYPE, replyType)) return readSubscribeReply();
else if (Arrays.equals(UNSUBSCRIBE_TYPE, replyType)) return readUnsubscribeReply();
else if (Arrays.equals(MESSAGE_TYPE, replyType)) return readMessageReply();
else throw new IOException("Unknown reply type: " + new String(replyType));
}
public void close() {
try {
this.closed.set(true);
this.inputStream.close();
this.outputStream.close();
this.socket.close();
} catch (IOException e) {
logger.warn("Exception while closing", e);
}
}
private PubSubReply readMessageReply() throws IOException {
StringReplyHeader channelNameHeader = new StringReplyHeader(inputStream.readLine());
byte[] channelName = inputStream.readFully(channelNameHeader.getStringLength());
inputStream.readLine();
StringReplyHeader messageHeader = new StringReplyHeader(inputStream.readLine());
byte[] message = inputStream.readFully(messageHeader.getStringLength());
inputStream.readLine();
return new PubSubReply(PubSubReply.Type.MESSAGE, new String(channelName), Optional.of(message));
}
private PubSubReply readUnsubscribeReply() throws IOException {
String channelName = readSubscriptionReply();
return new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, channelName, Optional.empty());
}
private PubSubReply readSubscribeReply() throws IOException {
String channelName = readSubscriptionReply();
return new PubSubReply(PubSubReply.Type.SUBSCRIBE, channelName, Optional.empty());
}
private String readSubscriptionReply() throws IOException {
StringReplyHeader channelNameHeader = new StringReplyHeader(inputStream.readLine());
byte[] channelName = inputStream.readFully(channelNameHeader.getStringLength());
inputStream.readLine();
IntReply subscriptionCount = new IntReply(inputStream.readLine());
return new String(channelName);
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis;
import java.util.Optional;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class PubSubReply {
public enum Type {
MESSAGE,
SUBSCRIBE,
UNSUBSCRIBE
}
private final Type type;
private final String channel;
private final Optional<byte[]> content;
public PubSubReply(Type type, String channel, Optional<byte[]> content) {
this.type = type;
this.channel = channel;
this.content = content;
}
public Type getType() {
return type;
}
public String getChannel() {
return channel;
}
public Optional<byte[]> getContent() {
return content;
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis.protocol;
import java.io.IOException;
public class ArrayReplyHeader {
private final int elementCount;
public ArrayReplyHeader(String header) throws IOException {
if (header == null || header.length() < 2 || header.charAt(0) != '*') {
throw new IOException("Invalid array reply header: " + header);
}
try {
this.elementCount = Integer.parseInt(header.substring(1));
} catch (NumberFormatException e) {
throw new IOException(e);
}
}
public int getElementCount() {
return elementCount;
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis.protocol;
import java.io.IOException;
public class IntReply {
private final int value;
public IntReply(String reply) throws IOException {
if (reply == null || reply.length() < 2 || reply.charAt(0) != ':') {
throw new IOException("Invalid int reply: " + reply);
}
try {
this.value = Integer.parseInt(reply.substring(1));
} catch (NumberFormatException e) {
throw new IOException(e);
}
}
public int getValue() {
return value;
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis.protocol;
import java.io.IOException;
public class StringReplyHeader {
private final int stringLength;
public StringReplyHeader(String header) throws IOException {
if (header == null || header.length() < 2 || header.charAt(0) != '$') {
throw new IOException("Invalid string reply header: " + header);
}
try {
this.stringLength = Integer.parseInt(header.substring(1));
} catch (NumberFormatException e) {
throw new IOException(e);
}
}
public int getStringLength() {
return stringLength;
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class Util {
public static byte[] combine(byte[]... elements) {
try {
int sum = 0;
for (byte[] element : elements) {
sum += element.length;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(sum);
for (byte[] element : elements) {
baos.write(element);
}
return baos.toByteArray();
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
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.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;
public class DispatchManagerTest {
private PubSubConnection pubSubConnection;
private RedisPubSubConnectionFactory socketFactory;
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();
when(socketFactory.connect()).thenReturn(pubSubConnection);
when(pubSubConnection.read()).thenAnswer(new Answer<PubSubReply>() {
@Override
public PubSubReply answer(InvocationOnMock invocationOnMock) throws Throwable {
return pubSubReplyInputStream.read();
}
});
dispatchManager = new DispatchManager(socketFactory, Optional.empty());
dispatchManager.start();
}
@Override
protected void after() {
}
};
@Test
public void testConnect() {
verify(socketFactory).connect();
}
@Test
public void testSubscribe() throws IOException {
DispatchChannel dispatchChannel = mock(DispatchChannel.class);
dispatchManager.subscribe("foo", dispatchChannel);
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
verify(dispatchChannel, timeout(1000)).onDispatchSubscribed(eq("foo"));
}
@Test
public void testSubscribeUnsubscribe() throws IOException {
DispatchChannel dispatchChannel = mock(DispatchChannel.class);
dispatchManager.subscribe("foo", dispatchChannel);
dispatchManager.unsubscribe("foo", dispatchChannel);
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, "foo", Optional.empty()));
verify(dispatchChannel, timeout(1000)).onDispatchUnsubscribed(eq("foo"));
}
@Test
public void testMessages() throws IOException {
DispatchChannel fooChannel = mock(DispatchChannel.class);
DispatchChannel barChannel = mock(DispatchChannel.class);
dispatchManager.subscribe("foo", fooChannel);
dispatchManager.subscribe("bar", barChannel);
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "bar", Optional.empty()));
verify(fooChannel, timeout(1000)).onDispatchSubscribed(eq("foo"));
verify(barChannel, timeout(1000)).onDispatchSubscribed(eq("bar"));
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.MESSAGE, "foo", Optional.of("hello".getBytes())));
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.MESSAGE, "bar", Optional.of("there".getBytes())));
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
verify(fooChannel, timeout(1000)).onDispatchMessage(eq("foo"), captor.capture());
assertArrayEquals("hello".getBytes(), captor.getValue());
verify(barChannel, timeout(1000)).onDispatchMessage(eq("bar"), captor.capture());
assertArrayEquals("there".getBytes(), captor.getValue());
}
private static class PubSubReplyInputStream {
private final List<PubSubReply> pubSubReplyList = new LinkedList<>();
public synchronized PubSubReply read() {
try {
while (pubSubReplyList.isEmpty()) wait();
return pubSubReplyList.remove(0);
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
public synchronized void write(PubSubReply pubSubReply) {
pubSubReplyList.add(pubSubReply);
notifyAll();
}
}
}

View File

@@ -1,265 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.SecureRandom;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class PubSubConnectionTest {
private static final String REPLY = "*3\r\n" +
"$9\r\n" +
"subscribe\r\n" +
"$5\r\n" +
"abcde\r\n" +
":1\r\n" +
"*3\r\n" +
"$9\r\n" +
"subscribe\r\n" +
"$5\r\n" +
"fghij\r\n" +
":2\r\n" +
"*3\r\n" +
"$9\r\n" +
"subscribe\r\n" +
"$5\r\n" +
"klmno\r\n" +
":2\r\n" +
"*3\r\n" +
"$7\r\n" +
"message\r\n" +
"$5\r\n" +
"abcde\r\n" +
"$10\r\n" +
"1234567890\r\n" +
"*3\r\n" +
"$7\r\n" +
"message\r\n" +
"$5\r\n" +
"klmno\r\n" +
"$10\r\n" +
"0987654321\r\n";
@Test
public void testSubscribe() throws IOException {
// ByteChannel byteChannel = mock(ByteChannel.class);
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
when(socket.getOutputStream()).thenReturn(outputStream);
PubSubConnection connection = new PubSubConnection(socket);
connection.subscribe("foobar");
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
verify(outputStream).write(captor.capture());
assertArrayEquals(captor.getValue(), "SUBSCRIBE foobar\r\n".getBytes());
}
@Test
public void testUnsubscribe() throws IOException {
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
when(socket.getOutputStream()).thenReturn(outputStream);
PubSubConnection connection = new PubSubConnection(socket);
connection.unsubscribe("bazbar");
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
verify(outputStream).write(captor.capture());
assertArrayEquals(captor.getValue(), "UNSUBSCRIBE bazbar\r\n".getBytes());
}
@Test
public void testTricklyResponse() throws Exception {
InputStream inputStream = mockInputStreamFor(new TrickleInputStream(REPLY.getBytes()));
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
when(socket.getOutputStream()).thenReturn(outputStream);
when(socket.getInputStream()).thenReturn(inputStream);
PubSubConnection pubSubConnection = new PubSubConnection(socket);
readResponses(pubSubConnection);
}
@Test
public void testFullResponse() throws Exception {
InputStream inputStream = mockInputStreamFor(new FullInputStream(REPLY.getBytes()));
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
when(socket.getOutputStream()).thenReturn(outputStream);
when(socket.getInputStream()).thenReturn(inputStream);
PubSubConnection pubSubConnection = new PubSubConnection(socket);
readResponses(pubSubConnection);
}
@Test
public void testRandomLengthResponse() throws Exception {
InputStream inputStream = mockInputStreamFor(new RandomInputStream(REPLY.getBytes()));
OutputStream outputStream = mock(OutputStream.class);
Socket socket = mock(Socket.class );
when(socket.getOutputStream()).thenReturn(outputStream);
when(socket.getInputStream()).thenReturn(inputStream);
PubSubConnection pubSubConnection = new PubSubConnection(socket);
readResponses(pubSubConnection);
}
private InputStream mockInputStreamFor(final MockInputStream stub) throws IOException {
InputStream result = mock(InputStream.class);
when(result.read()).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
return stub.read();
}
});
when(result.read(any(byte[].class))).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
byte[] buffer = (byte[])invocationOnMock.getArguments()[0];
return stub.read(buffer, 0, buffer.length);
}
});
when(result.read(any(byte[].class), anyInt(), anyInt())).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
byte[] buffer = (byte[]) invocationOnMock.getArguments()[0];
int offset = (int) invocationOnMock.getArguments()[1];
int length = (int) invocationOnMock.getArguments()[2];
return stub.read(buffer, offset, length);
}
});
return result;
}
private void readResponses(PubSubConnection pubSubConnection) throws Exception {
PubSubReply reply = pubSubConnection.read();
assertEquals(reply.getType(), PubSubReply.Type.SUBSCRIBE);
assertEquals(reply.getChannel(), "abcde");
assertFalse(reply.getContent().isPresent());
reply = pubSubConnection.read();
assertEquals(reply.getType(), PubSubReply.Type.SUBSCRIBE);
assertEquals(reply.getChannel(), "fghij");
assertFalse(reply.getContent().isPresent());
reply = pubSubConnection.read();
assertEquals(reply.getType(), PubSubReply.Type.SUBSCRIBE);
assertEquals(reply.getChannel(), "klmno");
assertFalse(reply.getContent().isPresent());
reply = pubSubConnection.read();
assertEquals(reply.getType(), PubSubReply.Type.MESSAGE);
assertEquals(reply.getChannel(), "abcde");
assertArrayEquals(reply.getContent().get(), "1234567890".getBytes());
reply = pubSubConnection.read();
assertEquals(reply.getType(), PubSubReply.Type.MESSAGE);
assertEquals(reply.getChannel(), "klmno");
assertArrayEquals(reply.getContent().get(), "0987654321".getBytes());
}
private interface MockInputStream {
public int read();
public int read(byte[] input, int offset, int length);
}
private static class TrickleInputStream implements MockInputStream {
private final byte[] data;
private int index = 0;
private TrickleInputStream(byte[] data) {
this.data = data;
}
public int read() {
return data[index++];
}
public int read(byte[] input, int offset, int length) {
input[offset] = data[index++];
return 1;
}
}
private static class FullInputStream implements MockInputStream {
private final byte[] data;
private int index = 0;
private FullInputStream(byte[] data) {
this.data = data;
}
public int read() {
return data[index++];
}
public int read(byte[] input, int offset, int length) {
int amount = Math.min(data.length - index, length);
System.arraycopy(data, index, input, offset, amount);
index += length;
return amount;
}
}
private static class RandomInputStream implements MockInputStream {
private final byte[] data;
private int index = 0;
private RandomInputStream(byte[] data) {
this.data = data;
}
public int read() {
return data[index++];
}
public int read(byte[] input, int offset, int length) {
int maxCopy = Math.min(data.length - index, length);
int randomCopy = new SecureRandom().nextInt(maxCopy) + 1;
int copyAmount = Math.min(maxCopy, randomCopy);
System.arraycopy(data, index, input, offset, copyAmount);
index += copyAmount;
return copyAmount;
}
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis.protocol;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
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
public void testValid() throws IOException {
assertEquals(4, new ArrayReplyHeader("*4").getElementCount());
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis.protocol;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
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
public void testValid() throws IOException {
assertEquals(23, new IntReply(":23").getValue());
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.dispatch.redis.protocol;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
public class StringReplyHeaderTest {
@Test
public void testNull() {
try {
new StringReplyHeader(null);
throw new AssertionError();
} catch (IOException e) {
// good
}
}
@Test
public void testBadNumber() {
try {
new StringReplyHeader("$100A");
throw new AssertionError();
} catch (IOException e) {
// good
}
}
@Test
public void testBadPrefix() {
try {
new StringReplyHeader("*");
throw new AssertionError();
} catch (IOException e) {
// good
}
}
@Test
public void testValid() throws IOException {
assertEquals(1000, new StringReplyHeader("$1000").getStringLength());
}
}

View File

@@ -0,0 +1,97 @@
datadog.apiKey: unset
stripe.apiKey: unset
stripe.idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
braintree.privateKey: unset
directoryV2.client.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth tokens for Signal users
directoryV2.client.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth identity tokens for Signal users
svr2.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
svr2.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
svr3.userAuthenticationTokenSharedSecret: cbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR3 to generate auth tokens for Signal users
svr3.userIdTokenSharedSecret: dbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR3 to generate auth identity tokens for Signal users
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
awsAttachments.accessKey: test
awsAttachments.accessSecret: test
gcpAttachments.rsaSigningKey: |
-----BEGIN PRIVATE KEY-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAA
-----END PRIVATE KEY-----
apn.teamId: team-id
apn.keyId: key-id
apn.signingKey: |
-----BEGIN PRIVATE KEY-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAA
-----END PRIVATE KEY-----
fcm.credentials: |
{ "json": true }
cdn.accessKey: test # AWS Access Key ID
cdn.accessSecret: test # AWS Access Secret
cdn3StorageManager.clientSecret: test
unidentifiedDelivery.certificate: ABCD1234
unidentifiedDelivery.privateKey: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
hCaptcha.apiKey: unset
storageService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
zkConfig-libsignal-0.37.serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
genericZkConfig.serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
callingZkConfig.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==
backupsZkConfig.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==
paymentsService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # base64-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
paymentsService.fixerApiKey: unset
paymentsService.coinMarketCapApiKey: unset
artService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # base64-encoded 32-byte secret not shared with any external service, but used in ArtController
artService.userAuthenticationTokenUserIdSecret: AAAAAAAAAAA= # base64-encoded secret to obscure user phone numbers from Sticker Creator
currentReportingKey.secret: AAAAAAAAAAA=
currentReportingKey.salt: AAAAAAAAAAA=
turn.secret: AAAAAAAAAAA=
linkDevice.secret: AAAAAAAAAAA=
tlsKeyStore.password: unset

View File

@@ -1,296 +1,474 @@
# 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.
logging:
level: INFO
appenders:
- type: console
threshold: ALL
timeZone: UTC
target: stdout
- type: logstashtcpsocket
destination: example.com:10516
apiKey: secret://datadog.apiKey
environment: staging
metrics:
reporters:
- type: signal-datadog
frequency: 10 seconds
tags:
- "env:staging"
- "service:chat"
udpTransport:
statsdHost: localhost
port: 8125
excludesAttributes:
- m1_rate
- m5_rate
- m15_rate
- mean_rate
- stddev
useRegexFilters: true
excludes:
- ^.+\.total$
- ^.+\.request\.filtering$
- ^.+\.response\.filtering$
- ^executor\..+$
- ^lettuce\..+$
reportOnStop: true
tlsKeyStore:
password: secret://tlsKeyStore.password
stripe:
apiKey:
idempotencyKeyGenerator:
apiKey: secret://stripe.apiKey
idempotencyKeyGenerator: secret://stripe.idempotencyKeyGenerator
boostDescription: >
Example
supportedCurrenciesByPaymentMethod:
CARD:
- usd
- eur
SEPA_DEBIT:
- eur
braintree:
merchantId: unset
publicKey: unset
privateKey: secret://braintree.privateKey
environment: unset
graphqlUrl: unset
merchantAccounts:
# ISO 4217 currency code and its corresponding sub-merchant account
'xts': unset
supportedCurrenciesByPaymentMethod:
PAYPAL:
- usd
dynamoDbClientConfiguration:
region: # AWS Region
region: us-west-2 # AWS Region
dynamoDbTables:
accounts:
tableName: Example_Accounts
phoneNumberTableName: Example_Accounts_PhoneNumbers
phoneNumberIdentifierTableName: Example_Accounts_PhoneNumberIdentifiers
usernamesTableName: Example_Accounts_Usernames
backups:
tableName: Example_Backups
clientReleases:
tableName: Example_ClientReleases
deletedAccounts:
tableName: Example_DeletedAccounts
deletedAccountsLock:
tableName: Example_DeletedAccountsLock
issuedReceipts:
tableName: # DDB Table Name
expiration: # Duration of time until rows expire
generator: # random binary sequence
tableName: Example_IssuedReceipts
expiration: P30D # Duration of time until rows expire
generator: abcdefg12345678= # random base64-encoded binary sequence
ecKeys:
tableName: Example_Keys
ecSignedPreKeys:
tableName: Example_EC_Signed_Pre_Keys
pqKeys:
tableName: Example_PQ_Keys
pqLastResortKeys:
tableName: Example_PQ_Last_Resort_Keys
messages:
tableName: Example_Messages
expiration: P30D # Duration of time until rows expire
onetimeDonations:
tableName: Example_OnetimeDonations
expiration: P90D
phoneNumberIdentifiers:
tableName: Example_PhoneNumberIdentifiers
profiles:
tableName: Example_Profiles
pushChallenge:
tableName: Example_PushChallenge
redeemedReceipts:
tableName: # DDB Table Name
expiration: # Duration of time until rows expire
tableName: Example_RedeemedReceipts
expiration: P30D # Duration of time until rows expire
registrationRecovery:
tableName: Example_RegistrationRecovery
expiration: P300D # Duration of time until rows expire
remoteConfig:
tableName: Example_RemoteConfig
reportMessage:
tableName: Example_ReportMessage
subscriptions:
tableName: # DDB Table Name
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.
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.
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
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
tableName: Example_Subscriptions
clientPublicKeys:
tableName: Example_ClientPublicKeys
verificationSessions:
tableName: Example_VerificationSessions
cacheCluster: # Redis server configuration for cache cluster
urls:
- redis://redis.example.com:6379/
configurationUri: redis://redis.example.com:6379/
clientPresenceCluster: # Redis server configuration for client presence cluster
urls:
- redis://redis.example.com:6379/
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/
uri: redis://redis.example.com:6379/
pushSchedulerCluster: # Redis server configuration for push scheduler cluster
urls:
- redis://redis.example.com:6379/
configurationUri: redis://redis.example.com:6379/
rateLimitersCluster: # Redis server configuration for rate limiters cluster
urls:
- 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
sqs:
accessKey: # AWS SQS accessKey
accessSecret: # AWS SQS accessSecret
queueUrls: # AWS SQS queue urls
- https://sqs.example.com/directory.fifo
server: # One or more CDS servers
- replicationName: # CDS replication name
replicationUrl: # CDS replication endpoint base url
replicationPassword: # CDS replication endpoint password
replicationCaCertificate: # CDS replication endpoint TLS certificate trust root
configurationUri: redis://redis.example.com:6379/
directoryV2:
client: # Configuration for interfacing with Contact Discovery Service v2 cluster
userAuthenticationTokenSharedSecret: # base64-encoded secret shared with CDS to generate auth tokens for Signal users
userAuthenticationTokenSharedSecret: secret://directoryV2.client.userAuthenticationTokenSharedSecret
userIdTokenSharedSecret: secret://directoryV2.client.userIdTokenSharedSecret
svr2:
uri: svr2.example.com
userAuthenticationTokenSharedSecret: secret://svr2.userAuthenticationTokenSharedSecret
userIdTokenSharedSecret: secret://svr2.userIdTokenSharedSecret
svrCaCertificates:
- |
-----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-----
svr3:
uri: svr3.example.com
userAuthenticationTokenSharedSecret: secret://svr3.userAuthenticationTokenSharedSecret
userIdTokenSharedSecret: secret://svr3.userIdTokenSharedSecret
svrCaCertificates:
- |
-----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-----
messageCache: # Redis server configuration for message store cache
persistDelayMinutes:
persistDelayMinutes: 1
cluster:
urls:
- redis://redis.example.com:6379/
configurationUri: redis://redis.example.com:6379/
metricsCluster:
urls:
- redis://redis.example.com:6379/
messageDynamoDb: # DynamoDB table configuration
region:
tableName:
keysDynamoDb: # DynamoDB table configuration
region:
tableName:
accountsDynamoDb: # DynamoDB table configuration
region:
tableName:
phoneNumberTableName:
deletedAccountsDynamoDb: # DynamoDb table configuration
region:
tableName:
needsReconciliationIndexName:
deletedAccountsLockDynamoDb: # DynamoDb table configuration
region:
tableName:
redeemedReceiptsDynamoDb: # DynamoDB table configuration
region:
tableName:
expirationTime: # ISO8601 Duration
migrationDeletedAccountsDynamoDb: # DynamoDB table configuration
region:
tableName:
migrationRetryAccountsDynamoDb: # DynamoDB table configuration
region:
tableName:
pendingAccountsDynamoDb: # DynamoDB table configuration
region:
tableName:
pendingDevicesDynamoDb: # DynamoDB table configuration
region:
tableName:
pushChallengeDynamoDb: # DynamoDB table configuration
region:
tableName:
reportMessageDynamoDb: # DynamoDB table configuration
region:
tableName:
configurationUri: redis://redis.example.com:6379/
awsAttachments: # AWS S3 configuration
accessKey:
accessSecret:
bucket:
region:
accessKey: secret://awsAttachments.accessKey
accessSecret: secret://awsAttachments.accessSecret
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: secret://gcpAttachments.rsaSigningKey
abuseDatabase: # Postgresql database configuration
driverClass: org.postgresql.Driver
user:
password:
url:
accountsDatabase: # Postgresql database configuration
driverClass: org.postgresql.Driver
user:
password:
url:
accountDatabaseCrawler:
chunkSize: # accounts per run
chunkIntervalMs: # time per run
dynamoDbMigrationCrawler:
chunkSize: # accounts per run
chunkIntervalMs: # time per run
tus:
uploadUri: https://example.org/upload
userAuthenticationTokenSharedSecret: secret://tus.userAuthenticationTokenSharedSecret
apn: # Apple Push Notifications configuration
sandbox: true
bundleId:
keyId:
teamId:
signingKey:
bundleId: com.example.textsecuregcm
keyId: secret://apn.keyId
teamId: secret://apn.teamId
signingKey: secret://apn.signingKey
gcm: # GCM Configuration
senderId:
apiKey:
fcm: # FCM configuration
credentials: secret://fcm.credentials
cdn:
accessKey: # AWS Access Key ID
accessSecret: # AWS Access Secret
bucket: # S3 Bucket name
region: # AWS region
accessKey: secret://cdn.accessKey
accessSecret: secret://cdn.accessSecret
bucket: cdn # S3 Bucket name
region: us-west-2 # AWS region
datadog:
apiKey:
environment:
clientCdn:
attachmentUrls:
2: https://cdn2.example.com/attachments/
caCertificates:
- |
-----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-----
cdn3StorageManager:
baseUri: https://storage-manager.example.com
clientId: example
clientSecret: secret://cdn3StorageManager.clientSecret
dogstatsd:
environment: dev
host: 127.0.0.1
unidentifiedDelivery:
certificate:
privateKey:
expiresDays:
voiceVerification:
url: https://cdn-ca.signal.org/verification/
locales:
- en
certificate: secret://unidentifiedDelivery.certificate
privateKey: secret://unidentifiedDelivery.privateKey
expiresDays: 7
recaptcha:
secret:
projectPath: projects/example
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
recaptchaV2:
siteKey:
scoreFloor:
projectPath:
credentialConfigurationJson:
hCaptcha:
apiKey: secret://hCaptcha.apiKey
shortCode:
baseUrl: https://example.com/shortcodes/
storageService:
uri:
userAuthenticationTokenSharedSecret:
storageCaCertificate:
backupService:
uri:
userAuthenticationTokenSharedSecret:
backupCaCertificate:
uri: storage.example.com
userAuthenticationTokenSharedSecret: secret://storageService.userAuthenticationTokenSharedSecret
storageCaCertificates:
- |
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
zkConfig:
serverPublic:
serverSecret:
enabled:
serverPublic: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
serverSecret: secret://zkConfig-libsignal-0.37.serverSecret
callingZkConfig:
serverSecret: secret://callingZkConfig.serverSecret
backupsZkConfig:
serverSecret: secret://backupsZkConfig.serverSecret
appConfig:
application:
environment:
configuration:
application: example
environment: example
configuration: example
remoteConfig:
authorizedTokens:
- # 1st authorized token
- # 2nd authorized token
- # ...
- # Nth authorized token
globalConfig: # keys and values that are given to clients on GET /v1/config
EXAMPLE_KEY: VALUE
paymentsService:
userAuthenticationTokenSharedSecret: # hex-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
fixerApiKey:
userAuthenticationTokenSharedSecret: secret://paymentsService.userAuthenticationTokenSharedSecret
fixerApiKey: secret://paymentsService.fixerApiKey
coinMarketCapApiKey: secret://paymentsService.coinMarketCapApiKey
coinMarketCapCurrencyIds:
MOB: 7878
paymentCurrencies:
-
# list of symbols for supported currencies
- MOB
torExitNodeList:
s3Region:
s3Bucket:
objectKey:
maxSize:
asnTable:
s3Region:
s3Bucket:
objectKey:
maxSize:
donation:
uri: # 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
artService:
userAuthenticationTokenSharedSecret: secret://artService.userAuthenticationTokenSharedSecret
userAuthenticationTokenUserIdSecret: secret://artService.userAuthenticationTokenUserIdSecret
badges:
badges:
- id: TEST
imageUrl: https://example.com/test-badge
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
badgeExpiration: P30D
badgeGracePeriod: P15D
levels:
500:
badge: EXAMPLE
prices:
# list of ISO 4217 currency codes and amounts for the given badge level
xts:
amount: '10'
processorIds:
STRIPE: price_example # stripe Price ID
BRAINTREE: plan_example # braintree Plan ID
oneTimeDonations:
sepaMaximumEuros: '10000'
boost:
level: 1
expiration: P90D
badge: EXAMPLE
gift:
level: 10
expiration: P90D
badge: EXAMPLE
currencies:
# ISO 4217 currency codes and amounts in those currencies
xts:
minimum: '0.5'
gift: '2'
boosts:
- '1'
- '2'
- '4'
- '8'
- '20'
- '40'
registrationService:
host: registration.example.com
port: 443
credentialConfigurationJson: |
{
"example": "example"
}
identityTokenAudience: https://registration.example.com
registrationCaCertificate: | # Registration service TLS certificate trust root
-----BEGIN CERTIFICATE-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE-----
turn:
secret: secret://turn.secret
linkDevice:
secret: secret://linkDevice.secret
maxmindCityDatabase:
s3Region: a-region
s3Bucket: a-bucket
objectKey: an-object.tar.gz
maxSize: 32777216
callingTurnDnsRecords:
s3Region: a-region
s3Bucket: a-bucket
objectKey: an-object.tar.gz
maxSize: 32777216
callingTurnPerformanceTable:
s3Region: a-region
s3Bucket: a-bucket
objectKey: an-object.tar.gz
maxSize: 32777216
callingTurnManualTable:
s3Region: a-region
s3Bucket: a-bucket
objectKey: an-object.tar.gz
maxSize: 32777216

View File

@@ -10,20 +10,25 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>service</artifactId>
<pluginRepositories>
<pluginRepository>
<id>ossrh-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<properties>
<firebase-admin.version>9.2.0</firebase-admin.version>
<java-uuid-generator.version>4.3.0</java-uuid-generator.version>
<sqlite4java.version>1.0.392</sqlite4java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<!-- org.yaml:snakeyaml is causing a dependency convergence error -->
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
@@ -37,40 +42,25 @@
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>redis-dispatch</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>websocket-resources</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>gcm-sender-async</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>zkgroup-java</artifactId>
<version>0.8.2</version>
<artifactId>libsignal-server</artifactId>
</dependency>
<dependency>
<groupId>org.whispersystems</groupId>
<artifactId>curve25519-java</artifactId>
<version>0.5.0</version>
<groupId>org.signal.forks</groupId>
<artifactId>noise-java</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>
@@ -81,7 +71,7 @@
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-db</artifactId>
<artifactId>dropwizard-http2</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
@@ -142,24 +132,10 @@
<artifactId>logstash-logback-encoder</artifactId>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-core</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jdbi3</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-healthchecks</artifactId>
@@ -191,26 +167,64 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>party.iroiro.luajava</groupId>
<artifactId>luajava</artifactId>
<version>${luajava.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>party.iroiro.luajava</groupId>
<artifactId>lua51</artifactId>
<version>${luajava.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>party.iroiro.luajava</groupId>
<artifactId>lua51-platform</artifactId>
<version>${luajava.version}</version>
<classifier>natives-desktop</classifier>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-api</artifactId>
<artifactId>websocket-jetty-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<artifactId>commons-compress</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>${firebase-admin.version}</version>
</dependency>
<dependency>
@@ -226,6 +240,29 @@
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-reactor</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<!-- Needed for gRPC with Java 9+ -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
@@ -233,7 +270,7 @@
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-datadog</artifactId>
<artifactId>micrometer-registry-statsd</artifactId>
</dependency>
<dependency>
<groupId>org.coursera</groupId>
@@ -264,6 +301,19 @@
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>com.salesforce.servicelibs</groupId>
<artifactId>reactor-grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
@@ -272,10 +322,6 @@
<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>
@@ -285,17 +331,13 @@
<artifactId>appconfig</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<groupId>software.amazon.awssdk</groupId>
<artifactId>appconfigdata</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>dynamodb-lock-client</artifactId>
<version>1.1.0</version>
<version>1.2.0</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -304,22 +346,11 @@
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>pushy</artifactId>
@@ -329,12 +360,6 @@
<artifactId>pushy-dropwizard-metrics-listener</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>semver4j</artifactId>
@@ -374,74 +399,70 @@
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</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>
<version>1.0.392</version>
<version>${sqlite4java.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core-micrometer</artifactId>
</dependency>
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>3.2.0</version>
<version>${java-uuid-generator.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>1.16.0</version>
<version>1.23.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.github.ganadist.sqlite4java</groupId>
<artifactId>libsqlite4java-osx-aarch64</artifactId>
<version>${sqlite4java.version}</version>
<type>dylib</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
</dependency>
<dependency>
@@ -455,22 +476,34 @@
</dependency>
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.1</version>
<scope>test</scope>
<groupId>com.braintreepayments.gateway</groupId>
<artifactId>braintree-java</artifactId>
</dependency>
<dependency>
<groupId>com.apollographql.apollo3</groupId>
<artifactId>apollo-api-jvm</artifactId>
<version>3.8.2</version>
<exclusions>
<exclusion>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<profiles>
<profile>
<id>exclude-abusive-message-filter</id>
<id>exclude-spam-filter</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<version>3.5.1</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
@@ -505,7 +538,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<version>3.6.0</version>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
@@ -525,7 +558,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<version>1.2.0</version>
<executions>
<execution>
<id>read-deploy-configuration</id>
@@ -541,24 +574,64 @@
</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>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<executions>
<execution>
<id>deploy-to-s3</id>
<phase>deploy</phase>
<goals>
<goal>s3-upload</goal>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<from>
<image>eclipse-temurin@sha256:${docker.image.sha256}</image>
</from>
<to>
<image>${docker.repo}:${project.version}</image>
</to>
<container>
<mainClass>org.whispersystems.textsecuregcm.WhisperServerService</mainClass>
<jvmFlags>
<jvmFlag>-server</jvmFlag>
<jvmFlag>-Djava.awt.headless=true</jvmFlag>
<jvmFlag>-Djdk.nio.maxCachedBufferSize=262144</jvmFlag>
<jvmFlag>-Dlog4j2.formatMsgNoLookups=true</jvmFlag>
<jvmFlag>-XX:MaxRAMPercentage=75</jvmFlag>
<jvmFlag>-XX:+HeapDumpOnOutOfMemoryError</jvmFlag>
<jvmFlag>-XX:HeapDumpPath=/tmp/heapdump.bin</jvmFlag>
</jvmFlags>
<ports>
<port>8080</port>
</ports>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
<extraDirectories>
<paths>
<path>
<from>${project.basedir}/config</from>
<includes>*.yml</includes>
<into>/usr/share/signal/</into>
</path>
</paths>
</extraDirectories>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>include-spam-filter</id>
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<!-- we don't want jib to execute on this module -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
@@ -582,6 +655,16 @@
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<!-- work around PATCH not being a supported method on HttpUrlConnection -->
<argLine>--add-opens=java.base/java.net=ALL-UNNAMED</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
@@ -593,6 +676,53 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.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>
<plugin>
<groupId>com.github.aoudiamoncef</groupId>
<artifactId>apollo-client-maven-plugin</artifactId>
<version>5.0.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<services>
<braintree>
<compilationUnit>
<name>braintree</name>
<compilerParams>
<schemaPackageName>com.braintree.graphql.client</schemaPackageName>
</compilerParams>
</compilationUnit>
</braintree>
</services>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,9 @@
# https://graphql.braintreepayments.com/reference/#Mutation--chargePaymentMethod
mutation ChargePayPalOneTimePayment($input: ChargePaymentMethodInput!) {
chargePaymentMethod(input: $input) {
transaction {
id,
status
}
}
}

View File

@@ -0,0 +1,6 @@
mutation CreatePayPalBillingAgreement($input: CreatePayPalBillingAgreementInput!) {
createPayPalBillingAgreement(input: $input) {
approvalUrl,
billingAgreementToken
}
}

View File

@@ -0,0 +1,7 @@
# https://graphql.braintreepayments.com/reference/#Mutation--createPayPalOneTimePayment
mutation CreatePayPalOneTimePayment($input: CreatePayPalOneTimePaymentInput!) {
createPayPalOneTimePayment(input: $input) {
approvalUrl,
paymentId
}
}

View File

@@ -0,0 +1,7 @@
mutation TokenizePayPalBillingAgreement($input: TokenizePayPalBillingAgreementInput!) {
tokenizePayPalBillingAgreement(input: $input) {
paymentMethod {
id
}
}
}

View File

@@ -0,0 +1,8 @@
# https://graphql.braintreepayments.com/reference/#Mutation--tokenizePayPalOneTimePayment
mutation TokenizePayPalOneTimePayment($input: TokenizePayPalOneTimePaymentInput!) {
tokenizePayPalOneTimePayment(input: $input) {
paymentMethod {
id
}
}
}

View File

@@ -0,0 +1,7 @@
mutation VaultPaymentMethod($input: VaultPaymentMethodInput!) {
vaultPaymentMethod(input: $input) {
paymentMethod {
id
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,71 +1,85 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* Copyright 2013 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 io.dropwizard.core.Configuration;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.attachments.TusConfiguration;
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
import org.whispersystems.textsecuregcm.configuration.AppConfigConfiguration;
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
import org.whispersystems.textsecuregcm.configuration.BraintreeConfiguration;
import org.whispersystems.textsecuregcm.configuration.Cdn3StorageManagerConfiguration;
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatabaseConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration;
import org.whispersystems.textsecuregcm.configuration.DeletedAccountsDynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
import org.whispersystems.textsecuregcm.configuration.ClientCdnConfiguration;
import org.whispersystems.textsecuregcm.configuration.ClientReleaseConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
import org.whispersystems.textsecuregcm.configuration.DonationConfiguration;
import org.whispersystems.textsecuregcm.configuration.DogstatsdConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
import org.whispersystems.textsecuregcm.configuration.GcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.GenericZkConfig;
import org.whispersystems.textsecuregcm.configuration.HCaptchaConfiguration;
import org.whispersystems.textsecuregcm.configuration.LinkDeviceSecretConfiguration;
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageByteLimitCardinalityEstimatorConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageDynamoDbConfiguration;
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
import org.whispersystems.textsecuregcm.configuration.RecaptchaConfiguration;
import org.whispersystems.textsecuregcm.configuration.RecaptchaV2Configuration;
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery3Configuration;
import org.whispersystems.textsecuregcm.configuration.ShortCodeExpanderConfiguration;
import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
import org.whispersystems.textsecuregcm.configuration.TestDeviceConfiguration;
import org.whispersystems.textsecuregcm.configuration.TurnConfiguration;
import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration;
import org.whispersystems.textsecuregcm.configuration.TlsKeyStoreConfiguration;
import org.whispersystems.textsecuregcm.configuration.TurnSecretConfiguration;
import org.whispersystems.textsecuregcm.configuration.UnidentifiedDeliveryConfiguration;
import org.whispersystems.textsecuregcm.configuration.VoiceVerificationConfiguration;
import org.whispersystems.textsecuregcm.configuration.VirtualThreadConfiguration;
import org.whispersystems.textsecuregcm.configuration.ZkConfig;
import org.whispersystems.textsecuregcm.limits.RateLimiterConfig;
import org.whispersystems.websocket.configuration.WebSocketConfiguration;
/** @noinspection MismatchedQueryAndUpdateOfCollection, WeakerAccess */
public class WhisperServerConfiguration extends Configuration {
@NotNull
@Valid
@JsonProperty
private TlsKeyStoreConfiguration tlsKeyStore;
@NotNull
@Valid
@JsonProperty
private StripeConfiguration stripe;
@NotNull
@Valid
@JsonProperty
private BraintreeConfiguration braintree;
@NotNull
@Valid
@JsonProperty
@@ -76,16 +90,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private DynamoDbTables dynamoDbTables;
@NotNull
@Valid
@JsonProperty
private TwilioConfiguration twilio;
@NotNull
@Valid
@JsonProperty
private PushConfiguration push;
@NotNull
@Valid
@JsonProperty
@@ -104,7 +108,17 @@ public class WhisperServerConfiguration extends Configuration {
@NotNull
@Valid
@JsonProperty
private DatadogConfiguration datadog;
private ClientCdnConfiguration clientCdn;
@NotNull
@Valid
@JsonProperty
private Cdn3StorageManagerConfiguration cdn3StorageManager;
@NotNull
@Valid
@JsonProperty
private DogstatsdConfiguration dogstatsd = new DogstatsdConfiguration();
@NotNull
@Valid
@@ -121,11 +135,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private RedisClusterConfiguration metricsCluster;
@NotNull
@Valid
@JsonProperty
private DirectoryConfiguration directory;
@NotNull
@Valid
@JsonProperty
@@ -134,7 +143,11 @@ public class WhisperServerConfiguration extends Configuration {
@NotNull
@Valid
@JsonProperty
private AccountDatabaseCrawlerConfiguration accountDatabaseCrawler;
private SecureValueRecovery2Configuration svr2;
@NotNull
@Valid
@JsonProperty
private SecureValueRecovery3Configuration svr3;
@NotNull
@Valid
@@ -159,62 +172,7 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@NotNull
@JsonProperty
private MessageDynamoDbConfiguration messageDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration keysDynamoDb;
@Valid
@NotNull
@JsonProperty
private AccountsDynamoDbConfiguration accountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration phoneNumberIdentifiersDynamoDb;
@Valid
@NotNull
@JsonProperty
private DeletedAccountsDynamoDbConfiguration deletedAccountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration deletedAccountsLockDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration pushChallengeDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration reportMessageDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration pendingAccountsDynamoDb;
@Valid
@NotNull
@JsonProperty
private DynamoDbConfiguration pendingDevicesDynamoDb;
@Valid
@NotNull
@JsonProperty
private DatabaseConfiguration abuseDatabase;
@Valid
@NotNull
@JsonProperty
private List<TestDeviceConfiguration> testDevices = new LinkedList<>();
private Set<String> testDevices = new HashSet<>();
@Valid
@NotNull
@@ -224,17 +182,7 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@NotNull
@JsonProperty
private AccountsDatabaseConfiguration accountsDatabase;
@Valid
@NotNull
@JsonProperty
private RateLimitsConfiguration limits = new RateLimitsConfiguration();
@Valid
@NotNull
@JsonProperty
private JerseyClientConfiguration httpClient = new JerseyClientConfiguration();
private Map<String, RateLimiterConfig> limits = new HashMap<>();
@Valid
@NotNull
@@ -244,12 +192,7 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@NotNull
@JsonProperty
private TurnConfiguration turn;
@Valid
@NotNull
@JsonProperty
private GcmConfiguration gcm;
private FcmConfiguration fcm;
@Valid
@NotNull
@@ -261,11 +204,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private UnidentifiedDeliveryConfiguration unidentifiedDelivery;
@Valid
@NotNull
@JsonProperty
private VoiceVerificationConfiguration voiceVerification;
@Valid
@NotNull
@JsonProperty
@@ -274,28 +212,43 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@NotNull
@JsonProperty
private RecaptchaV2Configuration recaptchaV2;
private HCaptchaConfiguration hCaptcha;
@Valid
@NotNull
@JsonProperty
private ShortCodeExpanderConfiguration shortCode;
@Valid
@NotNull
@JsonProperty
private SecureStorageServiceConfiguration storageService;
@Valid
@NotNull
@JsonProperty
private SecureBackupServiceConfiguration backupService;
@Valid
@NotNull
@JsonProperty
private PaymentsServiceConfiguration paymentsService;
@Valid
@NotNull
@JsonProperty
private ArtServiceConfiguration artService;
@Valid
@NotNull
@JsonProperty
private ZkConfig zkConfig;
@Valid
@NotNull
@JsonProperty
private GenericZkConfig callingZkConfig;
@Valid
@NotNull
@JsonProperty
private GenericZkConfig backupsZkConfig;
@Valid
@NotNull
@JsonProperty
@@ -306,11 +259,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private AppConfigConfiguration appConfig;
@Valid
@NotNull
@JsonProperty
private DonationConfiguration donation;
@Valid
@NotNull
@JsonProperty
@@ -324,7 +272,7 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@JsonProperty
@NotNull
private BoostConfiguration boost;
private OneTimeDonationConfiguration oneTimeDonations;
@Valid
@NotNull
@@ -333,14 +281,76 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@JsonProperty
private AbusiveMessageFilterConfiguration abusiveMessageFilter;
private SpamFilterConfiguration spamFilterConfiguration;
private Map<String, String> transparentDataIndex = new HashMap<>();
@Valid
@NotNull
@JsonProperty
private RegistrationServiceConfiguration registrationService;
@Valid
@NotNull
@JsonProperty
private TurnSecretConfiguration turn;
@Valid
@NotNull
@JsonProperty
private TusConfiguration tus;
@Valid
@NotNull
@JsonProperty
private ClientReleaseConfiguration clientRelease = new ClientReleaseConfiguration(Duration.ofHours(4));
@Valid
@NotNull
@JsonProperty
private MessageByteLimitCardinalityEstimatorConfiguration messageByteLimitCardinalityEstimator = new MessageByteLimitCardinalityEstimatorConfiguration(Duration.ofDays(1));
@Valid
@NotNull
@JsonProperty
private LinkDeviceSecretConfiguration linkDevice;
@Valid
@NotNull
@JsonProperty
private VirtualThreadConfiguration virtualThreadConfiguration = new VirtualThreadConfiguration(Duration.ofMillis(1));
@Valid
@NotNull
@JsonProperty
private MonitoredS3ObjectConfiguration maxmindCityDatabase;
@Valid
@NotNull
@JsonProperty
private MonitoredS3ObjectConfiguration callingTurnDnsRecords;
@Valid
@NotNull
@JsonProperty
private MonitoredS3ObjectConfiguration callingTurnPerformanceTable;
@Valid
@NotNull
@JsonProperty
private MonitoredS3ObjectConfiguration callingTurnManualTable;
public TlsKeyStoreConfiguration getTlsKeyStoreConfiguration() {
return tlsKeyStore;
}
public StripeConfiguration getStripe() {
return stripe;
}
public BraintreeConfiguration getBraintree() {
return braintree;
}
public DynamoDbClientConfiguration getDynamoDbClientConfiguration() {
return dynamoDbClientConfiguration;
}
@@ -353,30 +363,18 @@ public class WhisperServerConfiguration extends Configuration {
return recaptcha;
}
public RecaptchaV2Configuration getRecaptchaV2Configuration() {
return recaptchaV2;
public HCaptchaConfiguration getHCaptchaConfiguration() {
return hCaptcha;
}
public VoiceVerificationConfiguration getVoiceVerificationConfiguration() {
return voiceVerification;
public ShortCodeExpanderConfiguration getShortCodeRetrieverConfiguration() {
return shortCode;
}
public WebSocketConfiguration getWebSocketConfiguration() {
return webSocket;
}
public TwilioConfiguration getTwilioConfiguration() {
return twilio;
}
public PushConfiguration getPushConfiguration() {
return push;
}
public JerseyClientConfiguration getJerseyClientConfiguration() {
return httpClient;
}
public AwsAttachmentsConfiguration getAwsAttachmentsConfiguration() {
return awsAttachments;
}
@@ -397,8 +395,12 @@ public class WhisperServerConfiguration extends Configuration {
return metricsCluster;
}
public DirectoryConfiguration getDirectoryConfiguration() {
return directory;
public SecureValueRecovery2Configuration getSvr2Configuration() {
return svr2;
}
public SecureValueRecovery3Configuration getSvr3Configuration() {
return svr3;
}
public DirectoryV2Configuration getDirectoryV2Configuration() {
@@ -409,10 +411,6 @@ public class WhisperServerConfiguration extends Configuration {
return storageService;
}
public AccountDatabaseCrawlerConfiguration getAccountDatabaseCrawlerConfiguration() {
return accountDatabaseCrawler;
}
public MessageCacheConfiguration getMessageCacheConfiguration() {
return messageCache;
}
@@ -429,48 +427,12 @@ public class WhisperServerConfiguration extends Configuration {
return rateLimitersCluster;
}
public MessageDynamoDbConfiguration getMessageDynamoDbConfiguration() {
return messageDynamoDb;
}
public DynamoDbConfiguration getKeysDynamoDbConfiguration() {
return keysDynamoDb;
}
public AccountsDynamoDbConfiguration getAccountsDynamoDbConfiguration() {
return accountsDynamoDb;
}
public DynamoDbConfiguration getPhoneNumberIdentifiersDynamoDbConfiguration() {
return phoneNumberIdentifiersDynamoDb;
}
public DeletedAccountsDynamoDbConfiguration getDeletedAccountsDynamoDbConfiguration() {
return deletedAccountsDynamoDb;
}
public DynamoDbConfiguration getDeletedAccountsLockDynamoDbConfiguration() {
return deletedAccountsLockDynamoDb;
}
public DatabaseConfiguration getAbuseDatabaseConfiguration() {
return abuseDatabase;
}
public AccountsDatabaseConfiguration getAccountsDatabaseConfiguration() {
return accountsDatabase;
}
public RateLimitsConfiguration getLimitsConfiguration() {
public Map<String, RateLimiterConfig> getLimitsConfiguration() {
return limits;
}
public TurnConfiguration getTurnConfiguration() {
return turn;
}
public GcmConfiguration getGcmConfiguration() {
return gcm;
public FcmConfiguration getFcmConfiguration() {
return fcm;
}
public ApnConfiguration getApnConfiguration() {
@@ -481,23 +443,24 @@ public class WhisperServerConfiguration extends Configuration {
return cdn;
}
public DatadogConfiguration getDatadogConfiguration() {
return datadog;
public ClientCdnConfiguration getClientCdnConfiguration() {
return clientCdn;
}
public Cdn3StorageManagerConfiguration getCdn3StorageManagerConfiguration() {
return cdn3StorageManager;
}
public DogstatsdConfiguration getDatadogConfiguration() {
return dogstatsd;
}
public UnidentifiedDeliveryConfiguration getDeliveryCertificate() {
return unidentifiedDelivery;
}
public Map<String, Integer> getTestDevices() {
Map<String, Integer> results = new HashMap<>();
for (TestDeviceConfiguration testDeviceConfiguration : testDevices) {
results.put(testDeviceConfiguration.getNumber(),
testDeviceConfiguration.getCode());
}
return results;
public Set<String> getTestDevices() {
return testDevices;
}
public Map<String, Integer> getMaxDevices() {
@@ -511,22 +474,26 @@ public class WhisperServerConfiguration extends Configuration {
return results;
}
public Map<String, String> getTransparentDataIndex() {
return transparentDataIndex;
}
public SecureBackupServiceConfiguration getSecureBackupServiceConfiguration() {
return backupService;
}
public PaymentsServiceConfiguration getPaymentsServiceConfiguration() {
return paymentsService;
}
public ArtServiceConfiguration getArtServiceConfiguration() {
return artService;
}
public ZkConfig getZkConfig() {
return zkConfig;
}
public GenericZkConfig getCallingZkConfig() {
return callingZkConfig;
}
public GenericZkConfig getBackupsZkConfig() {
return backupsZkConfig;
}
public RemoteConfigConfiguration getRemoteConfigConfiguration() {
return remoteConfig;
}
@@ -535,26 +502,6 @@ public class WhisperServerConfiguration extends Configuration {
return appConfig;
}
public DynamoDbConfiguration getPushChallengeDynamoDbConfiguration() {
return pushChallengeDynamoDb;
}
public DynamoDbConfiguration getReportMessageDynamoDbConfiguration() {
return reportMessageDynamoDb;
}
public DynamoDbConfiguration getPendingAccountsDynamoDbConfiguration() {
return pendingAccountsDynamoDb;
}
public DynamoDbConfiguration getPendingDevicesDynamoDbConfiguration() {
return pendingDevicesDynamoDb;
}
public DonationConfiguration getDonationConfiguration() {
return donation;
}
public BadgesConfiguration getBadges() {
return badges;
}
@@ -563,15 +510,59 @@ public class WhisperServerConfiguration extends Configuration {
return subscription;
}
public BoostConfiguration getBoost() {
return boost;
public OneTimeDonationConfiguration getOneTimeDonations() {
return oneTimeDonations;
}
public ReportMessageConfiguration getReportMessageConfiguration() {
return reportMessage;
}
public AbusiveMessageFilterConfiguration getAbusiveMessageFilterConfiguration() {
return abusiveMessageFilter;
public SpamFilterConfiguration getSpamFilterConfiguration() {
return spamFilterConfiguration;
}
public RegistrationServiceConfiguration getRegistrationServiceConfiguration() {
return registrationService;
}
public TurnSecretConfiguration getTurnSecretConfiguration() {
return turn;
}
public TusConfiguration getTus() {
return tus;
}
public ClientReleaseConfiguration getClientReleaseConfiguration() {
return clientRelease;
}
public MessageByteLimitCardinalityEstimatorConfiguration getMessageByteLimitCardinalityEstimator() {
return messageByteLimitCardinalityEstimator;
}
public LinkDeviceSecretConfiguration getLinkDeviceSecretConfiguration() {
return linkDevice;
}
public VirtualThreadConfiguration getVirtualThreadConfiguration() {
return virtualThreadConfiguration;
}
public MonitoredS3ObjectConfiguration getMaxmindCityDatabase() {
return maxmindCityDatabase;
}
public MonitoredS3ObjectConfiguration getCallingTurnDnsRecords() {
return callingTurnDnsRecords;
}
public MonitoredS3ObjectConfiguration getCallingTurnPerformanceTable() {
return callingTurnPerformanceTable;
}
public MonitoredS3ObjectConfiguration getCallingTurnManualTable() {
return callingTurnManualTable;
}
}

View File

@@ -1,33 +0,0 @@
/*
* 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

@@ -1,21 +0,0 @@
/*
* 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,15 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import java.util.Map;
public interface AttachmentGenerator {
record Descriptor(Map<String, String> headers, String signedUploadLocation) {}
Descriptor generateAttachment(final String key);
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import org.whispersystems.textsecuregcm.gcp.CanonicalRequest;
import org.whispersystems.textsecuregcm.gcp.CanonicalRequestGenerator;
import org.whispersystems.textsecuregcm.gcp.CanonicalRequestSigner;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.spec.InvalidKeySpecException;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Map;
public class GcsAttachmentGenerator implements AttachmentGenerator {
@Nonnull
private final CanonicalRequestGenerator canonicalRequestGenerator;
@Nonnull
private final CanonicalRequestSigner canonicalRequestSigner;
public GcsAttachmentGenerator(@Nonnull String domain, @Nonnull String email,
int maxSizeInBytes, @Nonnull String pathPrefix, @Nonnull String rsaSigningKey)
throws IOException, InvalidKeyException, InvalidKeySpecException {
this.canonicalRequestGenerator = new CanonicalRequestGenerator(domain, email, maxSizeInBytes, pathPrefix);
this.canonicalRequestSigner = new CanonicalRequestSigner(rsaSigningKey);
}
@Override
public Descriptor generateAttachment(final String key) {
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
final CanonicalRequest canonicalRequest = canonicalRequestGenerator.createFor(key, now);
return new Descriptor(getHeaderMap(canonicalRequest), getSignedUploadLocation(canonicalRequest));
}
private String getSignedUploadLocation(@Nonnull CanonicalRequest canonicalRequest) {
return "https://" + canonicalRequest.getDomain() + canonicalRequest.getResourcePath()
+ '?' + canonicalRequest.getCanonicalQuery()
+ "&X-Goog-Signature=" + canonicalRequestSigner.sign(canonicalRequest);
}
private static Map<String, String> getHeaderMap(@Nonnull CanonicalRequest canonicalRequest) {
return Map.of(
"host", canonicalRequest.getDomain(),
"x-goog-content-length-range", "1," + canonicalRequest.getMaxSizeInBytes(),
"x-goog-resumable", "start");
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import org.apache.http.HttpHeaders;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.util.HeaderUtils;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.util.Base64;
import java.util.Map;
public class TusAttachmentGenerator implements AttachmentGenerator {
private static final String ATTACHMENTS = "attachments";
final ExternalServiceCredentialsGenerator credentialsGenerator;
final String tusUri;
public TusAttachmentGenerator(final TusConfiguration cfg) {
this.tusUri = cfg.uploadUri();
this.credentialsGenerator = credentialsGenerator(Clock.systemUTC(), cfg);
}
private static ExternalServiceCredentialsGenerator credentialsGenerator(final Clock clock, final TusConfiguration cfg) {
return ExternalServiceCredentialsGenerator
.builder(cfg.userAuthenticationTokenSharedSecret())
.prependUsername(false)
.withClock(clock)
.build();
}
@Override
public Descriptor generateAttachment(final String key) {
final ExternalServiceCredentials credentials = credentialsGenerator.generateFor(ATTACHMENTS + "/" + key);
final String b64Key = Base64.getEncoder().encodeToString(key.getBytes(StandardCharsets.UTF_8));
final Map<String, String> headers = Map.of(
HttpHeaders.AUTHORIZATION, HeaderUtils.basicAuthHeader(credentials),
"Upload-Metadata", String.format("filename %s", b64Key)
);
return new Descriptor(headers, tusUri + "/" + ATTACHMENTS);
}
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
import org.whispersystems.textsecuregcm.util.ExactlySize;
import javax.validation.constraints.NotEmpty;
public record TusConfiguration(
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
@NotEmpty String uploadUri
){}

View File

@@ -1,38 +1,153 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* Copyright 2013 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Optional;
import io.micrometer.core.instrument.Metrics;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
package org.whispersystems.textsecuregcm.auth;
import static com.codahale.metrics.MetricRegistry.name;
public class AccountAuthenticator extends BaseAccountAuthenticator implements
Authenticator<BasicCredentials, AuthenticatedAccount> {
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags;
import java.time.Clock;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
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.util.Pair;
import org.whispersystems.textsecuregcm.util.Util;
private static final String AUTHENTICATION_COUNTER_NAME = name(AccountAuthenticator.class, "authenticate");
public class AccountAuthenticator implements Authenticator<BasicCredentials, AuthenticatedAccount> {
private static final String LEGACY_NAME_PREFIX = "org.whispersystems.textsecuregcm.auth.BaseAccountAuthenticator";
private static final String AUTHENTICATION_COUNTER_NAME = name(LEGACY_NAME_PREFIX, "authentication");
private static final String AUTHENTICATION_SUCCEEDED_TAG_NAME = "succeeded";
private static final String AUTHENTICATION_FAILURE_REASON_TAG_NAME = "reason";
private static final String DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME = name(LEGACY_NAME_PREFIX, "daysSinceLastSeen");
private static final String IS_PRIMARY_DEVICE_TAG = "isPrimary";
@VisibleForTesting
static final char DEVICE_ID_SEPARATOR = '.';
private final AccountsManager accountsManager;
private final Clock clock;
public AccountAuthenticator(AccountsManager accountsManager) {
super(accountsManager);
this(accountsManager, Clock.systemUTC());
}
@VisibleForTesting
public AccountAuthenticator(AccountsManager accountsManager, Clock clock) {
this.accountsManager = accountsManager;
this.clock = clock;
}
static Pair<String, Byte> getIdentifierAndDeviceId(final String basicUsername) {
final String identifier;
final byte deviceId;
final int deviceIdSeparatorIndex = basicUsername.indexOf(DEVICE_ID_SEPARATOR);
if (deviceIdSeparatorIndex == -1) {
identifier = basicUsername;
deviceId = Device.PRIMARY_ID;
} else {
identifier = basicUsername.substring(0, deviceIdSeparatorIndex);
deviceId = Byte.parseByte(basicUsername.substring(deviceIdSeparatorIndex + 1));
}
return new Pair<>(identifier, deviceId);
}
@Override
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials) {
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = super.authenticate(basicCredentials, true);
boolean succeeded = false;
String failureReason = null;
// TODO Remove after announcement groups have launched
maybeAuthenticatedAccount.ifPresent(authenticatedAccount ->
Metrics.counter(AUTHENTICATION_COUNTER_NAME,
"supportsAnnouncementGroups",
String.valueOf(authenticatedAccount.getAccount().isAnnouncementGroupSupported()))
.increment());
try {
final UUID accountUuid;
final byte deviceId;
{
final Pair<String, Byte> identifierAndDeviceId = getIdentifierAndDeviceId(basicCredentials.getUsername());
return maybeAuthenticatedAccount;
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(deviceId);
if (device.isEmpty()) {
failureReason = "noSuchDevice";
return Optional.empty();
}
SaltedTokenHash deviceSaltedTokenHash = device.get().getAuthTokenHash();
if (deviceSaltedTokenHash.verify(basicCredentials.getPassword())) {
succeeded = true;
Account authenticatedAccount = updateLastSeen(account.get(), device.get());
if (deviceSaltedTokenHash.getVersion() != SaltedTokenHash.CURRENT_VERSION) {
authenticatedAccount = accountsManager.updateDeviceAuthentication(
authenticatedAccount,
device.get(),
SaltedTokenHash.generateFor(basicCredentials.getPassword())); // new credentials have current version
}
return Optional.of(new AuthenticatedAccount(authenticatedAccount, device.get()));
}
return Optional.empty();
} catch (IllegalArgumentException | InvalidAuthorizationHeaderException iae) {
failureReason = "invalidHeader";
return Optional.empty();
} finally {
Tags tags = Tags.of(
AUTHENTICATION_SUCCEEDED_TAG_NAME, String.valueOf(succeeded));
if (StringUtils.isNotBlank(failureReason)) {
tags = tags.and(AUTHENTICATION_FAILURE_REASON_TAG_NAME, failureReason);
}
Metrics.counter(AUTHENTICATION_COUNTER_NAME, tags).increment();
}
}
@VisibleForTesting
public Account updateLastSeen(Account account, Device device) {
// compute a non-negative integer between 0 and 86400.
long n = Util.ensureNonNegativeLong(account.getUuid().getLeastSignificantBits());
final long lastSeenOffsetSeconds = n % ChronoUnit.DAYS.getDuration().toSeconds();
// produce a truncated timestamp which is either today at UTC midnight
// or yesterday at UTC midnight, based on per-user randomized offset used.
final long todayInMillisWithOffset = Util.todayInMillisGivenOffsetFromNow(clock,
Duration.ofSeconds(lastSeenOffsetSeconds).negated());
// only update the device's last seen time when it falls behind the truncated timestamp.
// this ensures a few things:
// (1) each account will only update last-seen at most once per day
// (2) these updates will occur throughout the day rather than all occurring at UTC midnight.
if (device.getLastSeen() < todayInMillisWithOffset) {
Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isPrimary()))
.record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays());
return accountsManager.updateDeviceLastSeen(account, device, Util.todayInMillis(clock));
}
return account;
}
}

View File

@@ -5,7 +5,6 @@
package org.whispersystems.textsecuregcm.auth;
import com.google.common.annotations.VisibleForTesting;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -31,7 +30,6 @@ import org.whispersystems.textsecuregcm.util.Pair;
* {@link io.dropwizard.auth.Auth} object with a current device list.
*
* @see AuthenticatedAccount
* @see DisabledPermittedAuthenticatedAccount
*/
public class AuthEnablementRefreshRequirementProvider implements WebsocketRefreshRequirementProvider {
@@ -46,10 +44,6 @@ public class AuthEnablementRefreshRequirementProvider implements WebsocketRefres
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) {
@@ -61,40 +55,46 @@ public class AuthEnablementRefreshRequirementProvider implements WebsocketRefres
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));
setAccount(containerRequest, ContainerRequestUtil.AccountInfo.fromAccount(account));
}
private static void setAccount(final ContainerRequest containerRequest, final ContainerRequestUtil.AccountInfo info) {
containerRequest.setProperty(ACCOUNT_UUID, info.accountId());
containerRequest.setProperty(DEVICES_ENABLED, info.devicesEnabled());
}
@Override
public List<Pair<UUID, Long>> handleRequestFinished(final RequestEvent requestEvent) {
public List<Pair<UUID, Byte>> 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);
@SuppressWarnings("unchecked") final Map<Byte, Boolean> initialDevicesEnabled =
(Map<Byte, 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);
return accountsManager.getByAccountIdentifier((UUID) requestEvent.getContainerRequest().getProperty(ACCOUNT_UUID))
.map(ContainerRequestUtil.AccountInfo::fromAccount)
.map(account -> {
final Set<Byte> deviceIdsToDisplace;
final Map<Byte, Boolean> currentDevicesEnabled = account.devicesEnabled();
if (!initialDevicesEnabled.equals(currentDevicesEnabled)) {
deviceIdsToDisplace = new HashSet<>(initialDevicesEnabled.keySet());
deviceIdsToDisplace.addAll(currentDevicesEnabled.keySet());
} else {
deviceIdsToDisplace = Collections.emptySet();
}
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 deviceIdsToDisplace.stream()
.map(deviceId -> new Pair<>(account.accountId(), 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

@@ -10,24 +10,24 @@ 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 Account account;
private final Device device;
private final Supplier<Pair<Account, Device>> accountAndDevice;
public AuthenticatedAccount(final Supplier<Pair<Account, Device>> accountAndDevice) {
this.accountAndDevice = accountAndDevice;
public AuthenticatedAccount(final Account account, final Device device) {
this.account = account;
this.device = device;
}
@Override
public Account getAccount() {
return accountAndDevice.get().first();
return account;
}
@Override
public Device getAuthenticatedDevice() {
return accountAndDevice.get().second();
return device;
}
// Principal implementation

View File

@@ -0,0 +1,10 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.backup.BackupTier;
public record AuthenticatedBackupUser(byte[] backupId, BackupTier backupTier) {}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.apache.commons.codec.binary.Hex;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class AuthenticationCredentials {
private final String hashedAuthenticationToken;
private final String salt;
public AuthenticationCredentials(String hashedAuthenticationToken, String salt) {
this.hashedAuthenticationToken = hashedAuthenticationToken;
this.salt = salt;
}
public AuthenticationCredentials(String authenticationToken) {
this.salt = String.valueOf(Math.abs(new SecureRandom().nextInt()));
this.hashedAuthenticationToken = getHashedValue(salt, authenticationToken);
}
public String getHashedAuthenticationToken() {
return hashedAuthenticationToken;
}
public String getSalt() {
return salt;
}
public boolean verify(String authenticationToken) {
String theirValue = getHashedValue(salt, authenticationToken);
return MessageDigest.isEqual(theirValue.getBytes(StandardCharsets.UTF_8), this.hashedAuthenticationToken.getBytes(StandardCharsets.UTF_8));
}
private static String getHashedValue(String salt, String token) {
try {
return new String(Hex.encodeHex(MessageDigest.getInstance("SHA1").digest((salt + token).getBytes(StandardCharsets.UTF_8))));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -1,146 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import static com.codahale.metrics.MetricRegistry.name;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.basic.BasicCredentials;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags;
import java.time.Clock;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
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 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 AccountsManager accountsManager;
private final Clock clock;
public BaseAccountAuthenticator(AccountsManager accountsManager) {
this(accountsManager, Clock.systemUTC());
}
@VisibleForTesting
public BaseAccountAuthenticator(AccountsManager accountsManager, Clock clock) {
this.accountsManager = accountsManager;
this.clock = clock;
}
static Pair<String, Long> getIdentifierAndDeviceId(final String basicUsername) {
final String identifier;
final long deviceId;
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(deviceId);
if (device.isEmpty()) {
failureReason = "noSuchDevice";
return Optional.empty();
}
if (enabledRequired) {
if (!device.get().isEnabled()) {
failureReason = "deviceDisabled";
return Optional.empty();
}
if (!account.get().isEnabled()) {
failureReason = "accountDisabled";
return Optional.empty();
}
}
if (device.get().getAuthenticationCredentials().verify(basicCredentials.getPassword())) {
succeeded = true;
final Account authenticatedAccount = updateLastSeen(account.get(), device.get());
return Optional.of(new AuthenticatedAccount(
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
}
return Optional.empty();
} catch (IllegalArgumentException | InvalidAuthorizationHeaderException iae) {
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 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) {
Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isMaster()))
.record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays());
return accountsManager.updateDeviceLastSeen(account, device, Util.todayInMillis(clock));
}
return account;
}
}

View File

@@ -5,18 +5,16 @@
package org.whispersystems.textsecuregcm.auth;
import java.util.Base64;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Pair;
public class BasicAuthorizationHeader {
private final String username;
private final long deviceId;
private final byte deviceId;
private final String password;
private BasicAuthorizationHeader(final String username, final long deviceId, final String password) {
private BasicAuthorizationHeader(final String username, final byte deviceId, final String password) {
this.username = username;
this.deviceId = deviceId;
this.password = password;
@@ -61,10 +59,10 @@ public class BasicAuthorizationHeader {
final String usernameComponent = credentials.substring(0, credentialSeparatorIndex);
final String username;
final long deviceId;
final byte deviceId;
{
final Pair<String, Long> identifierAndDeviceId =
BaseAccountAuthenticator.getIdentifierAndDeviceId(usernameComponent);
final Pair<String, Byte> identifierAndDeviceId =
AccountAuthenticator.getIdentifierAndDeviceId(usernameComponent);
username = identifierAndDeviceId.first();
deviceId = identifierAndDeviceId.second();

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.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.identity.IdentityType;
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;
@@ -35,18 +33,23 @@ public class CertificateGenerator {
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))
.setIdentityKey(ByteString.copyFrom(Base64.getDecoder().decode(account.getIdentityKey())))
.setSigner(serverCertificate)
.setSenderUuid(account.getUuid().toString());
.setSenderDevice(Math.toIntExact(device.getId()))
.setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays))
.setIdentityKey(ByteString.copyFrom(account.getIdentityKey(IdentityType.ACI).serialize()))
.setSigner(serverCertificate)
.setSenderUuid(account.getUuid().toString());
if (includeE164) {
builder.setSender(account.getNumber());
}
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 2024 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 changes the phone number and PNI keys 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 ChangesPhoneNumber {
}

View File

@@ -16,7 +16,7 @@ public class CombinedUnidentifiedSenderAccessKeys {
public CombinedUnidentifiedSenderAccessKeys(String header) {
try {
this.combinedUnidentifiedSenderAccessKeys = Base64.getDecoder().decode(header);
if (this.combinedUnidentifiedSenderAccessKeys == null || this.combinedUnidentifiedSenderAccessKeys.length != 16) {
if (this.combinedUnidentifiedSenderAccessKeys == null || this.combinedUnidentifiedSenderAccessKeys.length != UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH) {
throw new WebApplicationException("Invalid combined unidentified sender access keys", Status.UNAUTHORIZED);
}
} catch (IllegalArgumentException e) {

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