pusher 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/stale.yml +26 -0
- data/.travis.yml +6 -6
- data/CHANGELOG.md +19 -1
- data/README.md +73 -24
- data/examples/presence_channels/presence_channels.rb +56 -0
- data/examples/presence_channels/public/presence_channels.html +28 -0
- data/lib/pusher/client.rb +49 -3
- data/lib/pusher/version.rb +1 -1
- data/pusher.gemspec +10 -9
- data/spec/client_spec.rb +106 -1
- metadata +42 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d9eaaf4fa5fbc3d5223411cb0e1375ccee4f93275f27c20cb7bdd8f31e371fa1
|
4
|
+
data.tar.gz: be5be46dc24a06be058606742ee56497a6128e39af6cf7f03ddaadc86ac95f12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd778cf80415b3d2af763583ebf41a4b1164339d0a458203cb55225aab078bdcfe4bf8801a824eeb6802a2f2b12db49df00569ceb631eb3ca20251ead29d3987
|
7
|
+
data.tar.gz: 187c968e1d1afef5a572bfe4bc22929843ef2843969fef9ac89862ff77b0d415b50cae82daa1b0d992374be2749053c0d8d52c1f1efa22ce7412e3c727c3edee
|
data/.github/stale.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Configuration for probot-stale - https://github.com/probot/stale
|
2
|
+
|
3
|
+
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
4
|
+
daysUntilStale: 365
|
5
|
+
|
6
|
+
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
7
|
+
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
8
|
+
daysUntilClose: 7
|
9
|
+
|
10
|
+
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
11
|
+
onlyLabels: []
|
12
|
+
|
13
|
+
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
14
|
+
exemptLabels:
|
15
|
+
- pinned
|
16
|
+
- security
|
17
|
+
|
18
|
+
# Set to true to ignore issues with an assignee (defaults to false)
|
19
|
+
exemptAssignees: true
|
20
|
+
|
21
|
+
# Comment to post when marking as stale. Set to `false` to disable
|
22
|
+
markComment: >
|
23
|
+
This issue has been automatically marked as stale because it has not had
|
24
|
+
recent activity. It will be closed if no further activity occurs. If you'd
|
25
|
+
like this issue to stay open please leave a comment indicating how this issue
|
26
|
+
is affecting you. Thankyou.
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
1.4.0 / 2020-09-29
|
2
|
+
==================
|
3
|
+
|
4
|
+
* Support for end-to-end encryption.
|
5
|
+
|
6
|
+
1.3.3 / 2019-07-02
|
7
|
+
==================
|
8
|
+
|
9
|
+
* Rewording to clarify "Pusher Channels" or simply "Channels" product name.
|
10
|
+
|
11
|
+
1.3.2 / 2018-10-17
|
12
|
+
==================
|
13
|
+
|
14
|
+
* Return a specific error for "Request Entity Too Large" (body over 10KB).
|
15
|
+
* Add a `use_tls` option for SSL (defaults to false).
|
16
|
+
* Add a `from_url` client method (in addition to existing `from_env` option).
|
17
|
+
* Improved documentation and fixed typos.
|
18
|
+
* Add Ruby 2.4 to test matrix.
|
19
|
+
|
1
20
|
1.3.1 / 2017-03-15
|
2
21
|
==================
|
3
22
|
|
@@ -100,4 +119,3 @@ First release with a changelog !
|
|
100
119
|
|
101
120
|
* Bump httpclient to v2.4. See #62 (POODLE SSL)
|
102
121
|
* Fix limited channel count at README.md. Thanks @tricknotes
|
103
|
-
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Gem for Pusher Channels
|
2
2
|
|
3
|
-
This Gem provides a Ruby interface to [the Pusher HTTP API for Pusher Channels](https://pusher.com/docs/
|
3
|
+
This Gem provides a Ruby interface to [the Pusher HTTP API for Pusher Channels](https://pusher.com/docs/channels/library_auth_reference/rest-api).
|
4
4
|
|
5
|
-
[](http://travis-ci.org/pusher/pusher-http-ruby)
|
5
|
+
[](http://travis-ci.org/pusher/pusher-http-ruby) [](https://badge.fury.io/rb/pusher)
|
6
6
|
|
7
7
|
## Installation and Configuration
|
8
8
|
|
@@ -18,7 +18,7 @@ or install via gem
|
|
18
18
|
gem install pusher
|
19
19
|
```
|
20
20
|
|
21
|
-
After registering at
|
21
|
+
After registering at [Pusher](https://dashboard.pusher.com/accounts/sign_up), configure your Channels app with the security credentials.
|
22
22
|
|
23
23
|
### Instantiating a Pusher Channels client
|
24
24
|
|
@@ -27,7 +27,7 @@ Creating a new Pusher Channels `client` can be done as follows.
|
|
27
27
|
``` ruby
|
28
28
|
require 'pusher'
|
29
29
|
|
30
|
-
|
30
|
+
pusher = Pusher::Client.new(
|
31
31
|
app_id: 'your-app-id',
|
32
32
|
key: 'your-app-key',
|
33
33
|
secret: 'your-app-secret',
|
@@ -43,7 +43,7 @@ If you want to set a custom `host` value for your client then you can do so when
|
|
43
43
|
``` ruby
|
44
44
|
require 'pusher'
|
45
45
|
|
46
|
-
|
46
|
+
pusher = Pusher::Client.new(
|
47
47
|
app_id: 'your-app-id',
|
48
48
|
key: 'your-app-key',
|
49
49
|
secret: 'your-app-secret',
|
@@ -57,7 +57,7 @@ Finally, if you have the configuration set in an `PUSHER_URL` environment
|
|
57
57
|
variable, you can use:
|
58
58
|
|
59
59
|
``` ruby
|
60
|
-
|
60
|
+
pusher = Pusher::Client.from_env
|
61
61
|
```
|
62
62
|
|
63
63
|
### Global configuration
|
@@ -102,7 +102,7 @@ Handle errors by rescuing `Pusher::Error` (all errors are descendants of this er
|
|
102
102
|
|
103
103
|
``` ruby
|
104
104
|
begin
|
105
|
-
|
105
|
+
pusher.trigger('a_channel', 'an_event', :some => 'data')
|
106
106
|
rescue Pusher::Error => e
|
107
107
|
# (Pusher::AuthenticationError, Pusher::HTTPError, or Pusher::Error)
|
108
108
|
end
|
@@ -121,14 +121,14 @@ Pusher.logger = Rails.logger
|
|
121
121
|
An event can be published to one or more channels (limited to 10) in one API call:
|
122
122
|
|
123
123
|
``` ruby
|
124
|
-
|
125
|
-
|
124
|
+
pusher.trigger('channel', 'event', foo: 'bar')
|
125
|
+
pusher.trigger(['channel_1', 'channel_2'], 'event_name', foo: 'bar')
|
126
126
|
```
|
127
127
|
|
128
|
-
An optional fourth argument may be used to send additional parameters to the API, for example to [exclude a single connection from receiving the event](
|
128
|
+
An optional fourth argument may be used to send additional parameters to the API, for example to [exclude a single connection from receiving the event](https://pusher.com/docs/channels/server_api/excluding-event-recipients).
|
129
129
|
|
130
130
|
``` ruby
|
131
|
-
|
131
|
+
pusher.trigger('channel', 'event', {foo: 'bar'}, {socket_id: '123.456'})
|
132
132
|
```
|
133
133
|
|
134
134
|
#### Batches
|
@@ -137,7 +137,7 @@ It's also possible to send multiple events with a single API call (max 10
|
|
137
137
|
events per call on multi-tenant clusters):
|
138
138
|
|
139
139
|
``` ruby
|
140
|
-
|
140
|
+
pusher.trigger_batch([
|
141
141
|
{channel: 'channel_1', name: 'event_name', data: { foo: 'bar' }},
|
142
142
|
{channel: 'channel_1', name: 'event_name', data: { hello: 'world' }}
|
143
143
|
])
|
@@ -151,19 +151,19 @@ Most examples and documentation will refer to the following syntax for triggerin
|
|
151
151
|
Pusher['a_channel'].trigger('an_event', :some => 'data')
|
152
152
|
```
|
153
153
|
|
154
|
-
This will continue to work, but has been replaced by `
|
154
|
+
This will continue to work, but has been replaced by `pusher.trigger` which supports one or multiple channels.
|
155
155
|
|
156
156
|
### Getting information about the channels in your Pusher Channels app
|
157
157
|
|
158
|
-
This gem provides methods for accessing information from the [Channels HTTP API](https://pusher.com/docs/
|
158
|
+
This gem provides methods for accessing information from the [Channels HTTP API](https://pusher.com/docs/channels/library_auth_reference/rest-api). The documentation also shows an example of the responses from each of the API endpoints.
|
159
159
|
|
160
160
|
The following methods are provided by the gem.
|
161
161
|
|
162
|
-
- `
|
162
|
+
- `pusher.channel_info('channel_name', {info:"user_count,subscription_count"})` returns a hash describing the state of the channel([docs](https://pusher.com/docs/channels/library_auth_reference/rest-api#get-channels-fetch-info-for-multiple-channels-)).
|
163
163
|
|
164
|
-
- `
|
164
|
+
- `pusher.channel_users('presence-channel_name')` returns a list of all the users subscribed to the channel (only for Presence Channels) ([docs](https://pusher.com/docs/channels/library_auth_reference/rest-api#get-channels-fetch-info-for-multiple-channels-)).
|
165
165
|
|
166
|
-
- `
|
166
|
+
- `pusher.channels({filter_by_prefix: 'presence-', info: 'user_count'})` returns a hash of occupied channels (optionally filtered by prefix, f.i. `presence-`), and optionally attributes for these channels ([docs](https://pusher.com/docs/channels/library_auth_reference/rest-api#get-channels-fetch-info-for-multiple-channels-)).
|
167
167
|
|
168
168
|
### Asynchronous requests
|
169
169
|
|
@@ -176,9 +176,9 @@ Asynchronous calls are supported either by using an event loop (eventmachine, pr
|
|
176
176
|
|
177
177
|
The following methods are available (in each case the calling interface matches the non-async version):
|
178
178
|
|
179
|
-
* `
|
180
|
-
* `
|
181
|
-
* `
|
179
|
+
* `pusher.get_async`
|
180
|
+
* `pusher.post_async`
|
181
|
+
* `pusher.trigger_async`
|
182
182
|
|
183
183
|
It is of course also possible to make calls to the Channels HTTP API via a job queue. This approach is recommended if you're sending a large number of events.
|
184
184
|
|
@@ -190,7 +190,7 @@ It is of course also possible to make calls to the Channels HTTP API via a job q
|
|
190
190
|
The `_async` methods return an `EM::Deferrable` which you can bind callbacks to:
|
191
191
|
|
192
192
|
``` ruby
|
193
|
-
|
193
|
+
pusher.get_async("/channels").callback { |response|
|
194
194
|
# use reponse[:channels]
|
195
195
|
}.errback { |error|
|
196
196
|
# error is an instance of Pusher::Error
|
@@ -213,7 +213,7 @@ It's possible to use the gem to authenticate subscription requests to private or
|
|
213
213
|
### Private channels
|
214
214
|
|
215
215
|
``` ruby
|
216
|
-
|
216
|
+
pusher.authenticate('private-my_channel', params[:socket_id])
|
217
217
|
```
|
218
218
|
|
219
219
|
### Presence channels
|
@@ -221,7 +221,7 @@ channels_client.authenticate('private-my_channel', params[:socket_id])
|
|
221
221
|
These work in a very similar way, but require a unique identifier for the user being authenticated, and optionally some attributes that are provided to clients via presence events:
|
222
222
|
|
223
223
|
``` ruby
|
224
|
-
|
224
|
+
pusher.authenticate('presence-my_channel', params[:socket_id],
|
225
225
|
user_id: 'user_id',
|
226
226
|
user_info: {} # optional
|
227
227
|
)
|
@@ -232,7 +232,7 @@ channels_client.authenticate('presence-my_channel', params[:socket_id],
|
|
232
232
|
A WebHook object may be created to validate received WebHooks against your app credentials, and to extract events. It should be created with the `Rack::Request` object (available as `request` in Rails controllers or Sinatra handlers for example).
|
233
233
|
|
234
234
|
``` ruby
|
235
|
-
webhook =
|
235
|
+
webhook = pusher.webhook(request)
|
236
236
|
if webhook.valid?
|
237
237
|
webhook.events.each do |event|
|
238
238
|
case event["name"]
|
@@ -247,3 +247,52 @@ else
|
|
247
247
|
render text: 'invalid', status: 401
|
248
248
|
end
|
249
249
|
```
|
250
|
+
|
251
|
+
### End-to-end encryption
|
252
|
+
|
253
|
+
This library supports [end-to-end encrypted channels](https://pusher.com/docs/channels/using_channels/encrypted-channels). This means that only you and your connected clients will be able to read your messages. Pusher cannot decrypt them. You can enable this feature by following these steps:
|
254
|
+
|
255
|
+
1. Install [Libsodium](https://github.com/jedisct1/libsodium), which we rely on to do the heavy lifting. [Follow the installation instructions for your platform.](https://github.com/RubyCrypto/rbnacl/wiki/Installing-libsodium)
|
256
|
+
|
257
|
+
2. Encrypted channel subscriptions must be authenticated in the exact same way as private channels. You should therefore [create an authentication endpoint on your server](https://pusher.com/docs/authenticating_users).
|
258
|
+
|
259
|
+
3. Next, generate your 32 byte master encryption key, encode it as base64 and pass it to the Pusher constructor.
|
260
|
+
|
261
|
+
This is secret and you should never share this with anyone.
|
262
|
+
Not even Pusher.
|
263
|
+
|
264
|
+
```bash
|
265
|
+
openssl rand -base64 32
|
266
|
+
```
|
267
|
+
|
268
|
+
```rb
|
269
|
+
pusher = new Pusher::Client.new({
|
270
|
+
app_id: 'your-app-id',
|
271
|
+
key: 'your-app-key',
|
272
|
+
secret: 'your-app-secret',
|
273
|
+
cluster: 'your-app-cluster',
|
274
|
+
use_tls: true
|
275
|
+
encryption_master_key_base64: '<KEY GENERATED BY PREVIOUS COMMAND>',
|
276
|
+
});
|
277
|
+
```
|
278
|
+
|
279
|
+
4. Channels where you wish to use end-to-end encryption should be prefixed with `private-encrypted-`.
|
280
|
+
|
281
|
+
5. Subscribe to these channels in your client, and you're done! You can verify it is working by checking out the debug console on the [https://dashboard.pusher.com/](dashboard) and seeing the scrambled ciphertext.
|
282
|
+
|
283
|
+
**Important note: This will __not__ encrypt messages on channels that are not prefixed by `private-encrypted-`.**
|
284
|
+
|
285
|
+
**Limitation**: you cannot trigger a single event on multiple channels in a call to `trigger`, e.g.
|
286
|
+
|
287
|
+
```rb
|
288
|
+
pusher.trigger(
|
289
|
+
['channel-1', 'private-encrypted-channel-2'],
|
290
|
+
'test_event',
|
291
|
+
{ message: 'hello world' },
|
292
|
+
)
|
293
|
+
```
|
294
|
+
|
295
|
+
Rationale: the methods in this library map directly to individual Channels HTTP API requests. If we allowed triggering a single event on multiple channels (some encrypted, some unencrypted), then it would require two API requests: one where the event is encrypted to the encrypted channels, and one where the event is unencrypted for unencrypted channels.
|
296
|
+
|
297
|
+
## Supported Ruby versions
|
298
|
+
2.4+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'sinatra/cookies'
|
3
|
+
require 'sinatra/json'
|
4
|
+
require 'pusher'
|
5
|
+
|
6
|
+
# You can get these variables from http://dashboard.pusher.com
|
7
|
+
pusher = Pusher::Client.new(
|
8
|
+
app_id: 'your-app-id',
|
9
|
+
key: 'your-app-key',
|
10
|
+
secret: 'your-app-secret',
|
11
|
+
cluster: 'your-app-cluster'
|
12
|
+
)
|
13
|
+
|
14
|
+
set :public_folder, 'public'
|
15
|
+
|
16
|
+
get "/" do
|
17
|
+
redirect '/presence_channels.html'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Emulate rails behaviour where this information would be stored in session
|
21
|
+
get '/signin' do
|
22
|
+
cookies[:user_id] = 'example_cookie'
|
23
|
+
'Ok'
|
24
|
+
end
|
25
|
+
|
26
|
+
# Auth endpoint: https://pusher.com/docs/channels/server_api/authenticating-users
|
27
|
+
post '/pusher/auth' do
|
28
|
+
channel_data = {
|
29
|
+
user_id: 'example_user',
|
30
|
+
user_info: {
|
31
|
+
name: 'example_name',
|
32
|
+
email: 'example_email'
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
if cookies[:user_id] == 'example_cookie'
|
37
|
+
response = pusher.authenticate(params[:channel_name], params[:socket_id], channel_data)
|
38
|
+
json response
|
39
|
+
else
|
40
|
+
status 403
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
get '/pusher_trigger' do
|
45
|
+
channels = ['presence-channel-test'];
|
46
|
+
|
47
|
+
begin
|
48
|
+
pusher.trigger(channels, 'test-event', {
|
49
|
+
message: 'hello world'
|
50
|
+
})
|
51
|
+
rescue Pusher::Error => e
|
52
|
+
# (Pusher::AuthenticationError, Pusher::HTTPError, or Pusher::Error)
|
53
|
+
end
|
54
|
+
|
55
|
+
'Triggered!'
|
56
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<head>
|
3
|
+
<title>Pusher Test</title>
|
4
|
+
<script src="https://js.pusher.com/5.0/pusher.min.js"></script>
|
5
|
+
<script>
|
6
|
+
|
7
|
+
// Enable pusher logging - don't include this in production
|
8
|
+
Pusher.logToConsole = true;
|
9
|
+
|
10
|
+
var pusher = new Pusher('your-app-key', {
|
11
|
+
cluster: 'your-app-cluster',
|
12
|
+
forceTLS: true,
|
13
|
+
authEndpoint: '/pusher/auth'
|
14
|
+
});
|
15
|
+
|
16
|
+
var channel = pusher.subscribe('presence-channel-test');
|
17
|
+
channel.bind('test-event', function(data) {
|
18
|
+
alert(JSON.stringify(data));
|
19
|
+
});
|
20
|
+
</script>
|
21
|
+
</head>
|
22
|
+
<body>
|
23
|
+
<h1>Pusher Test</h1>
|
24
|
+
<p>
|
25
|
+
Try publishing an event to channel <code>presence-channel-test</code>
|
26
|
+
with event name <code>test-event</code>.
|
27
|
+
</p>
|
28
|
+
</body>
|
data/lib/pusher/client.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
1
3
|
require 'pusher-signature'
|
2
4
|
|
3
5
|
module Pusher
|
4
6
|
class Client
|
5
|
-
attr_accessor :scheme, :host, :port, :app_id, :key, :secret, :notification_host, :notification_scheme
|
7
|
+
attr_accessor :scheme, :host, :port, :app_id, :key, :secret, :notification_host, :notification_scheme, :encryption_master_key
|
6
8
|
attr_reader :http_proxy, :proxy
|
7
9
|
attr_writer :connect_timeout, :send_timeout, :receive_timeout,
|
8
10
|
:keep_alive_timeout
|
@@ -55,6 +57,11 @@ module Pusher
|
|
55
57
|
:scheme, :host, :port, :app_id, :key, :secret, :notification_host, :notification_scheme
|
56
58
|
)
|
57
59
|
|
60
|
+
if options.has_key?(:encryption_master_key_base64)
|
61
|
+
@encryption_master_key =
|
62
|
+
Base64.decode64(options[:encryption_master_key_base64])
|
63
|
+
end
|
64
|
+
|
58
65
|
@http_proxy = nil
|
59
66
|
self.http_proxy = options[:http_proxy] if options[:http_proxy]
|
60
67
|
|
@@ -138,6 +145,12 @@ module Pusher
|
|
138
145
|
@connect_timeout, @send_timeout, @receive_timeout = value, value, value
|
139
146
|
end
|
140
147
|
|
148
|
+
# Set an encryption_master_key to use with private-encrypted channels from
|
149
|
+
# a base64 encoded string.
|
150
|
+
def encryption_master_key_base64=(s)
|
151
|
+
@encryption_master_key = s ? Base64.decode64(s) : nil
|
152
|
+
end
|
153
|
+
|
141
154
|
## INTERACT WITH THE API ##
|
142
155
|
|
143
156
|
def resource(path)
|
@@ -413,10 +426,17 @@ module Pusher
|
|
413
426
|
channels = Array(channels).map(&:to_s)
|
414
427
|
raise Pusher::Error, "Too many channels (#{channels.length}), max 10" if channels.length > 10
|
415
428
|
|
429
|
+
encoded_data = if channels.any?{ |c| c.match(/^private-encrypted-/) } then
|
430
|
+
raise Pusher::Error, "Cannot trigger to multiple channels if any are encrypted" if channels.length > 1
|
431
|
+
encrypt(channels[0], encode_data(data))
|
432
|
+
else
|
433
|
+
encode_data(data)
|
434
|
+
end
|
435
|
+
|
416
436
|
params.merge({
|
417
437
|
name: event_name,
|
418
438
|
channels: channels,
|
419
|
-
data:
|
439
|
+
data: encoded_data,
|
420
440
|
})
|
421
441
|
end
|
422
442
|
|
@@ -424,7 +444,11 @@ module Pusher
|
|
424
444
|
{
|
425
445
|
batch: events.map do |event|
|
426
446
|
event.dup.tap do |e|
|
427
|
-
e[:data] =
|
447
|
+
e[:data] = if e[:channel].match(/^private-encrypted-/) then
|
448
|
+
encrypt(e[:channel], encode_data(e[:data]))
|
449
|
+
else
|
450
|
+
encode_data(e[:data])
|
451
|
+
end
|
428
452
|
end
|
429
453
|
end
|
430
454
|
}
|
@@ -436,6 +460,28 @@ module Pusher
|
|
436
460
|
MultiJson.encode(data)
|
437
461
|
end
|
438
462
|
|
463
|
+
# Encrypts a message with a key derived from the master key and channel
|
464
|
+
# name
|
465
|
+
def encrypt(channel, encoded_data)
|
466
|
+
raise ConfigurationError, :encryption_master_key unless @encryption_master_key
|
467
|
+
|
468
|
+
# Only now load rbnacl, so that people that aren't using it don't need to
|
469
|
+
# install libsodium
|
470
|
+
require 'rbnacl'
|
471
|
+
|
472
|
+
secret_box = RbNaCl::SecretBox.new(
|
473
|
+
RbNaCl::Hash.sha256(channel + @encryption_master_key)
|
474
|
+
)
|
475
|
+
|
476
|
+
nonce = RbNaCl::Random.random_bytes(secret_box.nonce_bytes)
|
477
|
+
ciphertext = secret_box.encrypt(nonce, encoded_data)
|
478
|
+
|
479
|
+
MultiJson.encode({
|
480
|
+
"nonce" => Base64::encode64(nonce),
|
481
|
+
"ciphertext" => Base64::encode64(ciphertext),
|
482
|
+
})
|
483
|
+
end
|
484
|
+
|
439
485
|
def configured?
|
440
486
|
host && scheme && key && secret && app_id
|
441
487
|
end
|
data/lib/pusher/version.rb
CHANGED
data/pusher.gemspec
CHANGED
@@ -13,18 +13,19 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.description = %q{Wrapper for Pusher Channels REST api: : https://pusher.com/channels}
|
14
14
|
s.license = "MIT"
|
15
15
|
|
16
|
-
s.add_dependency "multi_json", "~> 1.
|
16
|
+
s.add_dependency "multi_json", "~> 1.15"
|
17
17
|
s.add_dependency 'pusher-signature', "~> 0.1.8"
|
18
|
-
s.add_dependency "httpclient", "~> 2.
|
18
|
+
s.add_dependency "httpclient", "~> 2.8"
|
19
19
|
s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
|
20
|
+
s.add_dependency "rbnacl", "~> 7.1"
|
20
21
|
|
21
|
-
s.add_development_dependency "rspec", "~> 3.
|
22
|
-
s.add_development_dependency "webmock"
|
23
|
-
s.add_development_dependency "em-http-request", "~> 1.1
|
24
|
-
s.add_development_dependency "addressable", "
|
25
|
-
s.add_development_dependency "rake", "~>
|
26
|
-
s.add_development_dependency "rack", "~>
|
27
|
-
s.add_development_dependency "json", "~>
|
22
|
+
s.add_development_dependency "rspec", "~> 3.9"
|
23
|
+
s.add_development_dependency "webmock", "~> 3.9"
|
24
|
+
s.add_development_dependency "em-http-request", "~> 1.1"
|
25
|
+
s.add_development_dependency "addressable", "~> 2.7"
|
26
|
+
s.add_development_dependency "rake", "~> 13.0"
|
27
|
+
s.add_development_dependency "rack", "~> 2.2"
|
28
|
+
s.add_development_dependency "json", "~> 2.3"
|
28
29
|
|
29
30
|
s.files = `git ls-files`.split("\n")
|
30
31
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/spec/client_spec.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'base64'
|
2
2
|
|
3
|
+
require 'rbnacl'
|
3
4
|
require 'em-http'
|
4
5
|
|
6
|
+
require 'spec_helper'
|
7
|
+
|
8
|
+
encryption_master_key = RbNaCl::Random.random_bytes(32)
|
9
|
+
|
5
10
|
describe Pusher do
|
6
11
|
# The behaviour should be the same when using the Client object, or the
|
7
12
|
# 'global' client delegated through the Pusher class
|
@@ -171,11 +176,22 @@ describe Pusher do
|
|
171
176
|
end
|
172
177
|
end
|
173
178
|
|
179
|
+
describe 'can set encryption_master_key_base64' do
|
180
|
+
it "sets encryption_master_key" do
|
181
|
+
@client.encryption_master_key_base64 =
|
182
|
+
Base64.encode64(encryption_master_key)
|
183
|
+
|
184
|
+
expect(@client.encryption_master_key).to eq(encryption_master_key)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
174
188
|
describe 'when configured' do
|
175
189
|
before :each do
|
176
190
|
@client.app_id = '20'
|
177
191
|
@client.key = '12345678900000001'
|
178
192
|
@client.secret = '12345678900000001'
|
193
|
+
@client.encryption_master_key_base64 =
|
194
|
+
Base64.encode64(encryption_master_key)
|
179
195
|
end
|
180
196
|
|
181
197
|
describe '#[]' do
|
@@ -321,6 +337,46 @@ describe Pusher do
|
|
321
337
|
}
|
322
338
|
end
|
323
339
|
end
|
340
|
+
|
341
|
+
it "should fail to publish to encrypted channels when missing key" do
|
342
|
+
@client.encryption_master_key_base64 = nil
|
343
|
+
expect {
|
344
|
+
@client.trigger('private-encrypted-channel', 'event', {'some' => 'data'})
|
345
|
+
}.to raise_error(Pusher::ConfigurationError)
|
346
|
+
expect(WebMock).not_to have_requested(:post, @api_path)
|
347
|
+
end
|
348
|
+
|
349
|
+
it "should fail to publish to multiple channels if one is encrypted" do
|
350
|
+
expect {
|
351
|
+
@client.trigger(
|
352
|
+
['private-encrypted-channel', 'some-other-channel'],
|
353
|
+
'event',
|
354
|
+
{'some' => 'data'},
|
355
|
+
)
|
356
|
+
}.to raise_error(Pusher::Error)
|
357
|
+
expect(WebMock).not_to have_requested(:post, @api_path)
|
358
|
+
end
|
359
|
+
|
360
|
+
it "should encrypt publishes to encrypted channels" do
|
361
|
+
@client.trigger(
|
362
|
+
'private-encrypted-channel',
|
363
|
+
'event',
|
364
|
+
{'some' => 'data'},
|
365
|
+
)
|
366
|
+
|
367
|
+
expect(WebMock).to have_requested(:post, @api_path).with { |req|
|
368
|
+
data = MultiJson.decode(MultiJson.decode(req.body)["data"])
|
369
|
+
|
370
|
+
key = RbNaCl::Hash.sha256(
|
371
|
+
'private-encrypted-channel' + encryption_master_key
|
372
|
+
)
|
373
|
+
|
374
|
+
expect(MultiJson.decode(RbNaCl::SecretBox.new(key).decrypt(
|
375
|
+
Base64.decode64(data["nonce"]),
|
376
|
+
Base64.decode64(data["ciphertext"]),
|
377
|
+
))).to eq({ 'some' => 'data' })
|
378
|
+
}
|
379
|
+
end
|
324
380
|
end
|
325
381
|
|
326
382
|
describe '#trigger_batch' do
|
@@ -352,6 +408,55 @@ describe Pusher do
|
|
352
408
|
)
|
353
409
|
}
|
354
410
|
end
|
411
|
+
|
412
|
+
it "should fail to publish to encrypted channels when missing key" do
|
413
|
+
@client.encryption_master_key_base64 = nil
|
414
|
+
expect {
|
415
|
+
@client.trigger_batch(
|
416
|
+
{
|
417
|
+
channel: 'private-encrypted-channel',
|
418
|
+
name: 'event',
|
419
|
+
data: {'some' => 'data'},
|
420
|
+
},
|
421
|
+
{channel: 'mychannel', name: 'event', data: 'already encoded'},
|
422
|
+
)
|
423
|
+
}.to raise_error(Pusher::ConfigurationError)
|
424
|
+
expect(WebMock).not_to have_requested(:post, @api_path)
|
425
|
+
end
|
426
|
+
|
427
|
+
it "should encrypt publishes to encrypted channels" do
|
428
|
+
@client.trigger_batch(
|
429
|
+
{
|
430
|
+
channel: 'private-encrypted-channel',
|
431
|
+
name: 'event',
|
432
|
+
data: {'some' => 'data'},
|
433
|
+
},
|
434
|
+
{channel: 'mychannel', name: 'event', data: 'already encoded'},
|
435
|
+
)
|
436
|
+
|
437
|
+
expect(WebMock).to have_requested(:post, @api_path).with { |req|
|
438
|
+
batch = MultiJson.decode(req.body)["batch"]
|
439
|
+
expect(batch.length).to eq(2)
|
440
|
+
|
441
|
+
expect(batch[0]["channel"]).to eq("private-encrypted-channel")
|
442
|
+
expect(batch[0]["name"]).to eq("event")
|
443
|
+
|
444
|
+
data = MultiJson.decode(batch[0]["data"])
|
445
|
+
|
446
|
+
key = RbNaCl::Hash.sha256(
|
447
|
+
'private-encrypted-channel' + encryption_master_key
|
448
|
+
)
|
449
|
+
|
450
|
+
expect(MultiJson.decode(RbNaCl::SecretBox.new(key).decrypt(
|
451
|
+
Base64.decode64(data["nonce"]),
|
452
|
+
Base64.decode64(data["ciphertext"]),
|
453
|
+
))).to eq({ 'some' => 'data' })
|
454
|
+
|
455
|
+
expect(batch[1]["channel"]).to eq("mychannel")
|
456
|
+
expect(batch[1]["name"]).to eq("event")
|
457
|
+
expect(batch[1]["data"]).to eq("already encoded")
|
458
|
+
}
|
459
|
+
end
|
355
460
|
end
|
356
461
|
|
357
462
|
describe '#trigger_async' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pusher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pusher
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.15'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.15'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pusher-signature
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,112 +44,126 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '2.
|
47
|
+
version: '2.8'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '2.
|
54
|
+
version: '2.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rbnacl
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '7.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '7.1'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
75
|
+
version: '3.9'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '3.
|
82
|
+
version: '3.9'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: webmock
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
89
|
+
version: '3.9'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
96
|
+
version: '3.9'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: em-http-request
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: 1.1
|
103
|
+
version: '1.1'
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version: 1.1
|
110
|
+
version: '1.1'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: addressable
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
|
-
- -
|
115
|
+
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: 2.
|
117
|
+
version: '2.7'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
|
-
- -
|
122
|
+
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: 2.
|
124
|
+
version: '2.7'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rake
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - "~>"
|
116
130
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
131
|
+
version: '13.0'
|
118
132
|
type: :development
|
119
133
|
prerelease: false
|
120
134
|
version_requirements: !ruby/object:Gem::Requirement
|
121
135
|
requirements:
|
122
136
|
- - "~>"
|
123
137
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
138
|
+
version: '13.0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: rack
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
128
142
|
requirements:
|
129
143
|
- - "~>"
|
130
144
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
145
|
+
version: '2.2'
|
132
146
|
type: :development
|
133
147
|
prerelease: false
|
134
148
|
version_requirements: !ruby/object:Gem::Requirement
|
135
149
|
requirements:
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
152
|
+
version: '2.2'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: json
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
157
|
- - "~>"
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
159
|
+
version: '2.3'
|
146
160
|
type: :development
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
164
|
- - "~>"
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
166
|
+
version: '2.3'
|
153
167
|
description: 'Wrapper for Pusher Channels REST api: : https://pusher.com/channels'
|
154
168
|
email:
|
155
169
|
- support@pusher.com
|
@@ -159,6 +173,7 @@ extra_rdoc_files: []
|
|
159
173
|
files:
|
160
174
|
- ".document"
|
161
175
|
- ".gemtest"
|
176
|
+
- ".github/stale.yml"
|
162
177
|
- ".gitignore"
|
163
178
|
- ".travis.yml"
|
164
179
|
- CHANGELOG.md
|
@@ -167,6 +182,8 @@ files:
|
|
167
182
|
- README.md
|
168
183
|
- Rakefile
|
169
184
|
- examples/async_message.rb
|
185
|
+
- examples/presence_channels/presence_channels.rb
|
186
|
+
- examples/presence_channels/public/presence_channels.html
|
170
187
|
- lib/pusher.rb
|
171
188
|
- lib/pusher/channel.rb
|
172
189
|
- lib/pusher/client.rb
|
@@ -199,8 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
216
|
- !ruby/object:Gem::Version
|
200
217
|
version: '0'
|
201
218
|
requirements: []
|
202
|
-
|
203
|
-
rubygems_version: 2.5.2.2
|
219
|
+
rubygems_version: 3.1.2
|
204
220
|
signing_key:
|
205
221
|
specification_version: 4
|
206
222
|
summary: Pusher Channels API client
|