pusher 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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