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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7460762ae77605d5132b729dfccf7610ec5eb956
4
- data.tar.gz: bf8ce2cb6c8a433e261d0c314dc65ec56e6fe9f7
2
+ SHA256:
3
+ metadata.gz: d9eaaf4fa5fbc3d5223411cb0e1375ccee4f93275f27c20cb7bdd8f31e371fa1
4
+ data.tar.gz: be5be46dc24a06be058606742ee56497a6128e39af6cf7f03ddaadc86ac95f12
5
5
  SHA512:
6
- metadata.gz: 98f95bce77fe09335f14f45b99edcf7e5d40f59cd1fd7660b3a6ba86678d34169747534bcdb9af64cdb364d60ca42ea03e1070e28594a5c4eacc5d34bfd75553
7
- data.tar.gz: b79d4eba9c63c4a4e421b3d2f8a93f4017c9053d5dd6994d4405460c02f14a2c12aaadbff27408c88fc2a0b0df9871a45b309ca6683b9803c793b33e3c92eac6
6
+ metadata.gz: bd778cf80415b3d2af763583ebf41a4b1164339d0a458203cb55225aab078bdcfe4bf8801a824eeb6802a2f2b12db49df00569ceb631eb3ca20251ead29d3987
7
+ data.tar.gz: 187c968e1d1afef5a572bfe4bc22929843ef2843969fef9ac89862ff77b0d415b50cae82daa1b0d992374be2749053c0d8d52c1f1efa22ce7412e3c727c3edee
@@ -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.
@@ -1,12 +1,12 @@
1
+ before_install:
2
+ - sudo apt-get -y install libsodium18
3
+
1
4
  language: ruby
2
5
  sudo: false
3
6
  rvm:
4
- - 1.9.3
5
- - 2.0
6
- - 2.1
7
- - 2.2
8
- - 2.3.0
9
- - 2.4.1
7
+ - 2.4
8
+ - 2.5
9
+ - 2.6
10
10
  - jruby
11
11
  - rbx-2
12
12
 
@@ -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/rest_api).
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
- [![Build Status](https://secure.travis-ci.org/pusher/pusher-http-ruby.svg?branch=master)](http://travis-ci.org/pusher/pusher-http-ruby)
5
+ [![Build Status](https://secure.travis-ci.org/pusher/pusher-http-ruby.svg?branch=master)](http://travis-ci.org/pusher/pusher-http-ruby) [![Gem Version](https://badge.fury.io/rb/pusher.svg)](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 <https://dashboard.pusher.com/>, configure your Channels app with the security credentials.
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
- channels_client = Pusher::Client.new(
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
- channels_client = Pusher::Client.new(
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
- channels_client = Pusher::Client.from_env
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
- channels_client.trigger('a_channel', 'an_event', :some => 'data')
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
- channels_client.trigger('channel', 'event', foo: 'bar')
125
- channels_client.trigger(['channel_1', 'channel_2'], 'event_name', foo: 'bar')
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](http://pusher.com/docs/publisher_api_guide/publisher_excluding_recipients).
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
- channels_client.trigger('channel', 'event', {foo: 'bar'}, {socket_id: '123.456'})
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
- channels_client.trigger_batch([
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 `channels_client.trigger` which supports one or multiple channels.
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/rest_api). The documentation also shows an example of the responses from each of the API endpoints.
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
- - `channels_client.channel_info('channel_name')` returns information about that channel.
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
- - `channels_client.channel_users('channel_name')` returns a list of all the users subscribed to the channel.
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
- - `channels_client.channels` returns information about all the channels in your Channels application.
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
- * `channels_client.get_async`
180
- * `channels_client.post_async`
181
- * `channels_client.trigger_async`
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
- channels_client.get_async("/channels").callback { |response|
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
- channels_client.authenticate('private-my_channel', params[:socket_id])
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
- channels_client.authenticate('presence-my_channel', params[:socket_id],
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 = channels_client.webhook(request)
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>
@@ -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: encode_data(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] = encode_data(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
@@ -1,3 +1,3 @@
1
1
  module Pusher
2
- VERSION = '1.3.3'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -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.0"
16
+ s.add_dependency "multi_json", "~> 1.15"
17
17
  s.add_dependency 'pusher-signature', "~> 0.1.8"
18
- s.add_dependency "httpclient", "~> 2.7"
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.0"
22
- s.add_development_dependency "webmock"
23
- s.add_development_dependency "em-http-request", "~> 1.1.0"
24
- s.add_development_dependency "addressable", "=2.4.0"
25
- s.add_development_dependency "rake", "~> 10.4.2"
26
- s.add_development_dependency "rack", "~> 1.6.4"
27
- s.add_development_dependency "json", "~> 1.8.3"
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")
@@ -1,7 +1,12 @@
1
- require 'spec_helper'
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.3.3
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: 2019-07-02 00:00:00.000000000 Z
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.0'
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.0'
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.7'
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.7'
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.0'
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.0'
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: '0'
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: '0'
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.0
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.0
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.4.0
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.4.0
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: 10.4.2
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: 10.4.2
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: 1.6.4
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: 1.6.4
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: 1.8.3
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: 1.8.3
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
- rubyforge_project:
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