ably-rest 0.7.5 → 0.8.1
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 +8 -8
- data/.travis.yml +2 -0
- data/README.md +41 -15
- data/SPEC.md +654 -518
- data/lib/submodules/ably-ruby/.gitignore +1 -0
- data/lib/submodules/ably-ruby/.gitmodules +3 -0
- data/lib/submodules/ably-ruby/README.md +54 -26
- data/lib/submodules/ably-ruby/SPEC.md +468 -322
- data/lib/submodules/ably-ruby/ably.gemspec +4 -2
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +185 -131
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +31 -44
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +1 -2
- data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +67 -24
- data/lib/submodules/ably-ruby/lib/ably/models/stats_types.rb +131 -0
- data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +101 -0
- data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +3 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +3 -7
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +32 -5
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +4 -8
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +12 -1
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +3 -7
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +13 -10
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +19 -20
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +14 -12
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +74 -23
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +3 -3
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +18 -18
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +5 -5
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +12 -12
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +5 -5
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +56 -13
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +8 -8
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +262 -158
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +11 -9
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +28 -21
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +30 -27
- data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +10 -10
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +93 -56
- data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +50 -45
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/rspec_config.rb +3 -2
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +36 -28
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +3 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +3 -3
- data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +20 -33
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +18 -1
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +81 -72
- data/lib/submodules/ably-ruby/spec/unit/models/stats_spec.rb +289 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +111 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +110 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +8 -8
- data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +1 -1
- metadata +9 -7
- data/lib/submodules/ably-ruby/lib/ably/models/token.rb +0 -74
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +0 -56
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +0 -56
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +0 -113
- data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +0 -86
|
@@ -6,7 +6,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
6
6
|
|
|
7
7
|
vary_by_protocol do
|
|
8
8
|
let(:default_options) do
|
|
9
|
-
{
|
|
9
|
+
{ key: api_key, environment: environment, protocol: protocol }
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:client_options) { default_options }
|
|
@@ -16,12 +16,12 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
16
16
|
|
|
17
17
|
context 'authentication failure' do
|
|
18
18
|
let(:client_options) do
|
|
19
|
-
default_options.merge(
|
|
19
|
+
default_options.merge(key: invalid_key, log_level: :none)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
context 'when API key is invalid' do
|
|
23
23
|
context 'with invalid app part of the key' do
|
|
24
|
-
let(:invalid_key) { 'not_an_app.
|
|
24
|
+
let(:invalid_key) { 'not_an_app.invalid_key_name:invalid_key_value' }
|
|
25
25
|
|
|
26
26
|
it 'enters the failed state and returns a not found error' do
|
|
27
27
|
connection.on(:failed) do |error|
|
|
@@ -34,8 +34,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
context 'with invalid key
|
|
38
|
-
let(:invalid_key) { "#{app_id}.
|
|
37
|
+
context 'with invalid key name part of the key' do
|
|
38
|
+
let(:invalid_key) { "#{app_id}.invalid_key_name:invalid_key_value" }
|
|
39
39
|
|
|
40
40
|
it 'enters the failed state and returns an authorization error' do
|
|
41
41
|
connection.on(:failed) do |error|
|
|
@@ -7,7 +7,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
7
7
|
|
|
8
8
|
vary_by_protocol do
|
|
9
9
|
let(:default_options) do
|
|
10
|
-
{
|
|
10
|
+
{ key: api_key, environment: environment, protocol: protocol }
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
let(:client_options) { default_options }
|
|
@@ -52,7 +52,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
52
52
|
before do
|
|
53
53
|
# Reduce token expiry buffer to zero so that a token expired? predicate is exact
|
|
54
54
|
# Normally there is a buffer so that a token expiring soon is considered expired
|
|
55
|
-
stub_const 'Ably::Models::
|
|
55
|
+
stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
context 'for renewable tokens' do
|
|
@@ -93,17 +93,17 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
93
93
|
|
|
94
94
|
it 'renews the token on connect' do
|
|
95
95
|
sleep ttl + 0.1
|
|
96
|
-
expect(client.auth.
|
|
96
|
+
expect(client.auth.current_token_details).to be_expired
|
|
97
97
|
expect(client.auth).to receive(:authorise).at_least(:once).and_call_original
|
|
98
98
|
connection.once(:connected) do
|
|
99
|
-
expect(client.auth.
|
|
99
|
+
expect(client.auth.current_token_details).to_not be_expired
|
|
100
100
|
stop_reactor
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
context 'with immediately expiring token' do
|
|
106
|
-
let(:ttl) { 0.
|
|
106
|
+
let(:ttl) { 0.001 }
|
|
107
107
|
|
|
108
108
|
it 'renews the token on connect, and only makes one subsequent attempt to obtain a new token' do
|
|
109
109
|
expect(client.auth).to receive(:authorise).at_least(:twice).and_call_original
|
|
@@ -138,19 +138,19 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
138
138
|
|
|
139
139
|
context 'when connected with a valid non-expired token' do
|
|
140
140
|
context 'that then expires following the connection being opened' do
|
|
141
|
-
let(:ttl) {
|
|
141
|
+
let(:ttl) { 5 }
|
|
142
142
|
let(:channel) { client.channel('test') }
|
|
143
143
|
|
|
144
144
|
context 'the server' do
|
|
145
|
-
it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout:
|
|
146
|
-
original_token = client.auth.
|
|
145
|
+
it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 15 do
|
|
146
|
+
original_token = client.auth.current_token_details
|
|
147
147
|
expect(original_token).to_not be_expired
|
|
148
148
|
|
|
149
149
|
connection.once(:connected) do
|
|
150
150
|
started_at = Time.now
|
|
151
151
|
connection.once(:disconnected) do |error|
|
|
152
152
|
connection.once(:connected) do
|
|
153
|
-
expect(client.auth.
|
|
153
|
+
expect(client.auth.current_token_details).to_not be_expired
|
|
154
154
|
expect(Time.now - started_at >= ttl)
|
|
155
155
|
expect(original_token).to be_expired
|
|
156
156
|
expect(error.code).to eql(40140) # token expired
|
|
@@ -172,16 +172,16 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
172
172
|
|
|
173
173
|
context 'for non-renewable tokens' do
|
|
174
174
|
context 'that are expired' do
|
|
175
|
-
let!(:
|
|
175
|
+
let!(:expired_token_details) do
|
|
176
176
|
Ably::Realtime::Client.new(default_options).auth.request_token(ttl: 0.01)
|
|
177
177
|
end
|
|
178
178
|
|
|
179
179
|
context 'opening a new connection' do
|
|
180
|
-
let(:client_options) { default_options.merge(
|
|
180
|
+
let(:client_options) { default_options.merge(key: nil, token: expired_token_details.token, log_level: :none) }
|
|
181
181
|
|
|
182
182
|
it 'transitions state to failed', em_timeout: 10 do
|
|
183
183
|
EventMachine.add_timer(1) do # wait for token to expire
|
|
184
|
-
expect(
|
|
184
|
+
expect(expired_token_details).to be_expired
|
|
185
185
|
connection.once(:connected) { raise 'Connection should never connect as token has expired' }
|
|
186
186
|
connection.once(:failed) do
|
|
187
187
|
expect(client.connection.error_reason.code).to eql(40140)
|
|
@@ -6,7 +6,7 @@ require 'securerandom'
|
|
|
6
6
|
|
|
7
7
|
describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
8
8
|
vary_by_protocol do
|
|
9
|
-
let(:default_options) { options.merge(
|
|
9
|
+
let(:default_options) { options.merge(key: api_key, environment: environment, protocol: protocol) }
|
|
10
10
|
let(:client_options) { default_options }
|
|
11
11
|
let(:client) do
|
|
12
12
|
Ably::Realtime::Client.new(client_options)
|
|
@@ -77,8 +77,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
|
77
77
|
context 'when retrieved over REST' do
|
|
78
78
|
it 'matches the sender connection#id' do
|
|
79
79
|
channel.publish('event', payload) do
|
|
80
|
-
channel.history do |
|
|
81
|
-
expect(
|
|
80
|
+
channel.history do |page|
|
|
81
|
+
expect(page.items.first.connection_id).to eql(client.connection.id)
|
|
82
82
|
stop_reactor
|
|
83
83
|
end
|
|
84
84
|
end
|
|
@@ -175,7 +175,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
|
175
175
|
|
|
176
176
|
context 'without suitable publishing permissions' do
|
|
177
177
|
let(:restricted_client) do
|
|
178
|
-
Ably::Realtime::Client.new(options.merge(
|
|
178
|
+
Ably::Realtime::Client.new(options.merge(key: restricted_api_key, environment: environment, protocol: protocol))
|
|
179
179
|
end
|
|
180
180
|
let(:restricted_channel) { restricted_client.channel("cansubscribe:example") }
|
|
181
181
|
let(:payload) { 'Test message without permission to publish' }
|
|
@@ -293,7 +293,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
|
293
293
|
end
|
|
294
294
|
end
|
|
295
295
|
|
|
296
|
-
resources_root = File.expand_path('
|
|
296
|
+
resources_root = File.expand_path('../../../../lib/submodules/ably-common/test-resources', __FILE__)
|
|
297
297
|
|
|
298
298
|
def self.add_tests_for_data(data)
|
|
299
299
|
data['items'].each_with_index do |item, index|
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
|
3
3
|
|
|
4
4
|
describe Ably::Realtime::Presence, 'history', :event_machine do
|
|
5
5
|
vary_by_protocol do
|
|
6
|
-
let(:default_options) { {
|
|
6
|
+
let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
|
|
7
7
|
|
|
8
8
|
let(:channel_name) { "persisted:#{random_str(2)}" }
|
|
9
9
|
|
|
@@ -21,16 +21,16 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
|
|
|
21
21
|
it 'provides up to the moment presence history' do
|
|
22
22
|
presence_client_one.enter(data: data) do
|
|
23
23
|
presence_client_one.leave(data: leave_data) do
|
|
24
|
-
presence_client_one.history do |
|
|
25
|
-
expect(
|
|
24
|
+
presence_client_one.history do |history_page|
|
|
25
|
+
expect(history_page.items.count).to eql(2)
|
|
26
26
|
|
|
27
|
-
expect(
|
|
28
|
-
expect(
|
|
29
|
-
expect(
|
|
27
|
+
expect(history_page.items[1].action).to eq(:enter)
|
|
28
|
+
expect(history_page.items[1].client_id).to eq(client_one.client_id)
|
|
29
|
+
expect(history_page.items[1].data).to eql(data)
|
|
30
30
|
|
|
31
|
-
expect(
|
|
32
|
-
expect(
|
|
33
|
-
expect(
|
|
31
|
+
expect(history_page.items[0].action).to eq(:leave)
|
|
32
|
+
expect(history_page.items[0].client_id).to eq(client_one.client_id)
|
|
33
|
+
expect(history_page.items[0].data).to eql(leave_data)
|
|
34
34
|
|
|
35
35
|
stop_reactor
|
|
36
36
|
end
|
|
@@ -40,16 +40,59 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
|
|
|
40
40
|
|
|
41
41
|
it 'ensures REST presence history message IDs match ProtocolMessage wrapped message and connection IDs via Realtime' do
|
|
42
42
|
presence_client_one.subscribe(:enter) do |message|
|
|
43
|
-
presence_client_one.history do |
|
|
44
|
-
expect(
|
|
43
|
+
presence_client_one.history do |history_page|
|
|
44
|
+
expect(history_page.items.count).to eql(1)
|
|
45
45
|
|
|
46
|
-
expect(
|
|
47
|
-
expect(
|
|
46
|
+
expect(history_page.items[0].id).to eql(message.id)
|
|
47
|
+
expect(history_page.items[0].connection_id).to eql(message.connection_id)
|
|
48
48
|
stop_reactor
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
presence_client_one.enter(data: data)
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
context 'with option until_attach: true' do
|
|
56
|
+
let(:event) { random_str }
|
|
57
|
+
let(:presence_data_before_attach) { random_str }
|
|
58
|
+
let(:presence_data_after_attach) { random_str }
|
|
59
|
+
|
|
60
|
+
it 'retrieves all presence messages before channel was attached' do
|
|
61
|
+
presence_client_two.enter(data: presence_data_before_attach) do
|
|
62
|
+
presence_client_one.enter(data: presence_data_after_attach) do
|
|
63
|
+
presence_client_one.history(until_attach: true) do |presence_page|
|
|
64
|
+
expect(presence_page.items.count).to eql(1)
|
|
65
|
+
expect(presence_page.items.first.data).to eql(presence_data_before_attach)
|
|
66
|
+
stop_reactor
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context 'and two pages of messages' do
|
|
73
|
+
it 'retrieves two pages of messages before channel was attached' do
|
|
74
|
+
when_all(*10.times.map { |i| presence_client_two.enter_client("client:#{i}", data: presence_data_before_attach) }) do
|
|
75
|
+
when_all(*10.times.map { |i| presence_client_one.enter_client("client:#{i}", data: presence_data_after_attach) }) do
|
|
76
|
+
presence_client_one.history(until_attach: true, limit: 5) do |presence_page|
|
|
77
|
+
expect(presence_page.items.count).to eql(5)
|
|
78
|
+
expect(presence_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
|
|
79
|
+
|
|
80
|
+
presence_page.next do |presence_next_page|
|
|
81
|
+
expect(presence_next_page.items.count).to eql(5)
|
|
82
|
+
expect(presence_next_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
|
|
83
|
+
expect(presence_next_page).to be_last
|
|
84
|
+
stop_reactor
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'raises an exception unless state is attached' do
|
|
93
|
+
expect { presence_client_one.history(until_attach: true) }.to raise_error(ArgumentError, /not attached/)
|
|
94
|
+
stop_reactor
|
|
95
|
+
end
|
|
96
|
+
end
|
|
54
97
|
end
|
|
55
98
|
end
|
|
@@ -5,7 +5,7 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
|
5
5
|
include Ably::Modules::Conversions
|
|
6
6
|
|
|
7
7
|
vary_by_protocol do
|
|
8
|
-
let(:default_options) { {
|
|
8
|
+
let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
|
|
9
9
|
let(:client_options) { default_options }
|
|
10
10
|
|
|
11
11
|
let(:anonymous_client) { Ably::Realtime::Client.new(client_options) }
|
|
@@ -487,7 +487,7 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
|
487
487
|
|
|
488
488
|
context 'without necessary capabilities to join presence' do
|
|
489
489
|
let(:restricted_client) do
|
|
490
|
-
Ably::Realtime::Client.new(default_options.merge(
|
|
490
|
+
Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
|
|
491
491
|
end
|
|
492
492
|
let(:restricted_channel) { restricted_client.channel("cansubscribe:channel") }
|
|
493
493
|
let(:restricted_presence) { restricted_channel.presence }
|
|
@@ -688,7 +688,7 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
|
688
688
|
|
|
689
689
|
context 'without necessary capabilities to enter on behalf of another client' do
|
|
690
690
|
let(:restricted_client) do
|
|
691
|
-
Ably::Realtime::Client.new(default_options.merge(
|
|
691
|
+
Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
|
|
692
692
|
end
|
|
693
693
|
let(:restricted_channel) { restricted_client.channel("cansubscribe:channel") }
|
|
694
694
|
let(:restricted_presence) { restricted_channel.presence }
|
|
@@ -1123,8 +1123,8 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
|
1123
1123
|
context 'REST #get' do
|
|
1124
1124
|
it 'returns current members' do
|
|
1125
1125
|
presence_client_one.enter(data: data_payload) do
|
|
1126
|
-
|
|
1127
|
-
this_member =
|
|
1126
|
+
members_page = channel_rest_client_one.presence.get
|
|
1127
|
+
this_member = members_page.items.first
|
|
1128
1128
|
|
|
1129
1129
|
expect(this_member).to be_a(Ably::Models::PresenceMessage)
|
|
1130
1130
|
expect(this_member.client_id).to eql(client_one.client_id)
|
|
@@ -1137,8 +1137,8 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
|
1137
1137
|
it 'returns no members once left' do
|
|
1138
1138
|
presence_client_one.enter(data: data_payload) do
|
|
1139
1139
|
presence_client_one.leave do
|
|
1140
|
-
|
|
1141
|
-
expect(
|
|
1140
|
+
members_page = channel_rest_client_one.presence.get
|
|
1141
|
+
expect(members_page.items.count).to eql(0)
|
|
1142
1142
|
stop_reactor
|
|
1143
1143
|
end
|
|
1144
1144
|
end
|
|
@@ -1264,7 +1264,7 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
|
1264
1264
|
context 'REST #get' do
|
|
1265
1265
|
it 'returns a list of members with decrypted data' do
|
|
1266
1266
|
encrypted_channel.presence.enter(data: data) do
|
|
1267
|
-
member = channel_rest_client_one.presence.get.first
|
|
1267
|
+
member = channel_rest_client_one.presence.get.items.first
|
|
1268
1268
|
expect(member.encoding).to be_nil
|
|
1269
1269
|
expect(member.data).to eql(data)
|
|
1270
1270
|
stop_reactor
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
|
3
3
|
describe Ably::Realtime::Client, '#stats', :event_machine do
|
|
4
4
|
vary_by_protocol do
|
|
5
5
|
let(:client) do
|
|
6
|
-
Ably::Realtime::Client.new(
|
|
6
|
+
Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
describe 'fetching stats' do
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
|
3
3
|
describe Ably::Realtime::Client, '#time', :event_machine do
|
|
4
4
|
vary_by_protocol do
|
|
5
5
|
let(:client) do
|
|
6
|
-
Ably::Realtime::Client.new(
|
|
6
|
+
Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
describe 'fetching the service time' do
|
|
@@ -4,17 +4,17 @@ require 'spec_helper'
|
|
|
4
4
|
describe Ably::Auth do
|
|
5
5
|
include Ably::Modules::Conversions
|
|
6
6
|
|
|
7
|
-
def hmac_for(
|
|
8
|
-
|
|
7
|
+
def hmac_for(token_request_attributes, secret)
|
|
8
|
+
token_request= Ably::Models::IdiomaticRubyWrapper.new(token_request_attributes)
|
|
9
9
|
|
|
10
10
|
text = [
|
|
11
|
-
:
|
|
11
|
+
:key_name,
|
|
12
12
|
:ttl,
|
|
13
13
|
:capability,
|
|
14
14
|
:client_id,
|
|
15
15
|
:timestamp,
|
|
16
16
|
:nonce
|
|
17
|
-
].map { |key| "#{
|
|
17
|
+
].map { |key| "#{token_request.hash[key]}\n" }.join("")
|
|
18
18
|
|
|
19
19
|
encode64(
|
|
20
20
|
OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, text)
|
|
@@ -23,7 +23,7 @@ describe Ably::Auth do
|
|
|
23
23
|
|
|
24
24
|
vary_by_protocol do
|
|
25
25
|
let(:client) do
|
|
26
|
-
Ably::Rest::Client.new(
|
|
26
|
+
Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol)
|
|
27
27
|
end
|
|
28
28
|
let(:auth) { client.auth }
|
|
29
29
|
let(:content_type) do
|
|
@@ -52,37 +52,43 @@ describe Ably::Auth do
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
it 'has immutable options' do
|
|
55
|
-
expect { auth.options['
|
|
55
|
+
expect { auth.options['key_name'] = 'new_name' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
describe '#request_token' do
|
|
59
59
|
let(:ttl) { 30 * 60 }
|
|
60
60
|
let(:capability) { { :foo => ['publish'] } }
|
|
61
61
|
|
|
62
|
-
let(:
|
|
62
|
+
let(:token_details) do
|
|
63
63
|
auth.request_token(
|
|
64
64
|
ttl: ttl,
|
|
65
65
|
capability: capability
|
|
66
66
|
)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
it 'returns a valid requested token in the expected format with valid
|
|
70
|
-
expect(token
|
|
71
|
-
expect(
|
|
72
|
-
expect(
|
|
73
|
-
expect(
|
|
69
|
+
it 'returns a valid requested token in the expected format with valid issued and expires attributes' do
|
|
70
|
+
expect(token_details.token).to match(/^#{app_id}\.[\w-]+$/)
|
|
71
|
+
expect(token_details.key_name).to match(/^#{key_name}$/)
|
|
72
|
+
expect(token_details.issued).to be_within(2).of(Time.now)
|
|
73
|
+
expect(token_details.expires).to be_within(2).of(Time.now + ttl)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
%w(client_id capability nonce timestamp ttl).each do |option|
|
|
77
77
|
context "with option :#{option}", :webmock do
|
|
78
|
-
|
|
78
|
+
def coerce_if_time_value(field_name, value, options = {})
|
|
79
|
+
multiply = options[:multiply]
|
|
80
|
+
return value unless %w(timestamp ttl).include?(field_name)
|
|
81
|
+
value.to_i * (multiply ? multiply : 1)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
let(:random) { coerce_if_time_value(option, random_int_str) }
|
|
79
85
|
let(:options) { { option.to_sym => random } }
|
|
80
86
|
|
|
81
|
-
let(:token_response) { {
|
|
87
|
+
let(:token_response) { {} }
|
|
82
88
|
let!(:request_token_stub) do
|
|
83
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
|
89
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
|
84
90
|
with do |request|
|
|
85
|
-
request_body_includes(request, protocol, option, random)
|
|
91
|
+
request_body_includes(request, protocol, option, coerce_if_time_value(option, random, multiply: 1000))
|
|
86
92
|
end.to_return(
|
|
87
93
|
:status => 201,
|
|
88
94
|
:body => serialize(token_response, protocol),
|
|
@@ -92,25 +98,25 @@ describe Ably::Auth do
|
|
|
92
98
|
|
|
93
99
|
before { auth.request_token options }
|
|
94
100
|
|
|
95
|
-
it
|
|
101
|
+
it "overrides default and uses camelCase notation for attributes" do
|
|
96
102
|
expect(request_token_stub).to have_been_requested
|
|
97
103
|
end
|
|
98
104
|
end
|
|
99
105
|
end
|
|
100
106
|
|
|
101
|
-
context 'with :
|
|
102
|
-
let(:
|
|
107
|
+
context 'with :key option', :webmock do
|
|
108
|
+
let(:key_name) { "app.#{random_str}" }
|
|
103
109
|
let(:key_secret) { random_str }
|
|
104
110
|
let(:nonce) { random_str }
|
|
105
|
-
let(:token_options) { {
|
|
111
|
+
let(:token_options) { { key: "#{key_name}:#{key_secret}", nonce: nonce, timestamp: Time.now } }
|
|
106
112
|
let(:token_request) { auth.create_token_request(token_options) }
|
|
107
113
|
let(:mac) do
|
|
108
114
|
hmac_for(token_request, key_secret)
|
|
109
115
|
end
|
|
110
116
|
|
|
111
|
-
let(:token_response) { {
|
|
117
|
+
let(:token_response) { {} }
|
|
112
118
|
let!(:request_token_stub) do
|
|
113
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
|
119
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
|
114
120
|
with do |request|
|
|
115
121
|
request_body_includes(request, protocol, 'mac', mac)
|
|
116
122
|
end.to_return(
|
|
@@ -119,9 +125,38 @@ describe Ably::Auth do
|
|
|
119
125
|
:headers => { 'Content-Type' => content_type })
|
|
120
126
|
end
|
|
121
127
|
|
|
122
|
-
let!(:token) { auth.request_token(token_options) }
|
|
128
|
+
let!(:token) { puts token_options; auth.request_token(token_options) }
|
|
123
129
|
|
|
124
|
-
specify '
|
|
130
|
+
specify 'key_name is used in request and signing uses key_secret' do
|
|
131
|
+
expect(request_token_stub).to have_been_requested
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
context 'with :key_name & :key_secret options', :webmock do
|
|
136
|
+
let(:key_name) { "app.#{random_str}" }
|
|
137
|
+
let(:key_secret) { random_str }
|
|
138
|
+
let(:nonce) { random_str }
|
|
139
|
+
|
|
140
|
+
let(:name_secret_token_options) { { key_name: key_name, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
|
|
141
|
+
let(:token_request) { auth.create_token_request(name_secret_token_options) }
|
|
142
|
+
let(:mac) do
|
|
143
|
+
hmac_for(token_request, key_secret)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
let(:token_response) { {} }
|
|
147
|
+
let!(:request_token_stub) do
|
|
148
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
|
149
|
+
with do |request|
|
|
150
|
+
request_body_includes(request, protocol, 'mac', mac)
|
|
151
|
+
end.to_return(
|
|
152
|
+
:status => 201,
|
|
153
|
+
:body => serialize(token_response, protocol),
|
|
154
|
+
:headers => { 'Content-Type' => content_type })
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
let!(:token) { auth.request_token(name_secret_token_options); }
|
|
158
|
+
|
|
159
|
+
specify 'key_name is used in request and signing uses key_secret' do
|
|
125
160
|
expect(request_token_stub).to have_been_requested
|
|
126
161
|
end
|
|
127
162
|
end
|
|
@@ -146,8 +181,8 @@ describe Ably::Auth do
|
|
|
146
181
|
|
|
147
182
|
context 'with :auth_url option', :webmock do
|
|
148
183
|
let(:auth_url) { 'https://www.fictitious.com/get_token' }
|
|
149
|
-
let(:auth_url_response) { {
|
|
150
|
-
let(:token_response) { {
|
|
184
|
+
let(:auth_url_response) { { keyName: key_name }.to_json }
|
|
185
|
+
let(:token_response) { {} }
|
|
151
186
|
let(:query_params) { nil }
|
|
152
187
|
let(:headers) { nil }
|
|
153
188
|
let(:auth_method) { :get }
|
|
@@ -166,15 +201,16 @@ describe Ably::Auth do
|
|
|
166
201
|
stub.with(:headers => headers) unless headers.nil?
|
|
167
202
|
stub.to_return(
|
|
168
203
|
:status => 201,
|
|
169
|
-
:body => auth_url_response
|
|
170
|
-
:headers => { 'Content-Type' =>
|
|
204
|
+
:body => auth_url_response,
|
|
205
|
+
:headers => { 'Content-Type' => auth_url_content_type }
|
|
171
206
|
)
|
|
172
207
|
end
|
|
208
|
+
let(:auth_url_content_type) { 'application/json' }
|
|
173
209
|
|
|
174
210
|
let!(:request_token_stub) do
|
|
175
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
|
211
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
|
176
212
|
with do |request|
|
|
177
|
-
request_body_includes(request, protocol, '
|
|
213
|
+
request_body_includes(request, protocol, 'key_name', key_name)
|
|
178
214
|
end.to_return(
|
|
179
215
|
:status => 201,
|
|
180
216
|
:body => serialize(token_response, protocol),
|
|
@@ -191,7 +227,7 @@ describe Ably::Auth do
|
|
|
191
227
|
end
|
|
192
228
|
|
|
193
229
|
it 'returns a valid token generated from the token request' do
|
|
194
|
-
expect(token).to be_a(Ably::Models::
|
|
230
|
+
expect(token).to be_a(Ably::Models::TokenDetails)
|
|
195
231
|
end
|
|
196
232
|
|
|
197
233
|
context 'with :query_params' do
|
|
@@ -220,29 +256,45 @@ describe Ably::Auth do
|
|
|
220
256
|
end
|
|
221
257
|
end
|
|
222
258
|
|
|
223
|
-
context 'when response from :auth_url is a token' do
|
|
224
|
-
let(:
|
|
225
|
-
let(:
|
|
259
|
+
context 'when response from :auth_url is a token details object' do
|
|
260
|
+
let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
|
|
261
|
+
let(:issued) { Time.now }
|
|
226
262
|
let(:expires) { Time.now + 60}
|
|
227
263
|
let(:capability) { {'foo'=>['publish']} }
|
|
264
|
+
let(:capability_str) { JSON.dump(capability) }
|
|
228
265
|
let(:auth_url_response) do
|
|
229
266
|
{
|
|
230
|
-
'
|
|
231
|
-
'
|
|
232
|
-
'
|
|
233
|
-
'expires' => expires.to_i,
|
|
234
|
-
'capability'=>
|
|
235
|
-
}
|
|
267
|
+
'token' => token,
|
|
268
|
+
'key_name' => 'J_0Tlg.NxCRig',
|
|
269
|
+
'issued' => issued.to_i * 1000,
|
|
270
|
+
'expires' => expires.to_i * 1000,
|
|
271
|
+
'capability'=> capability_str
|
|
272
|
+
}.to_json
|
|
236
273
|
end
|
|
237
274
|
|
|
238
|
-
let!(:
|
|
275
|
+
let!(:token_details) { auth.request_token(options) }
|
|
239
276
|
|
|
240
|
-
it 'returns
|
|
277
|
+
it 'returns TokenDetails created from the token JSON' do
|
|
241
278
|
expect(request_token_stub).to_not have_been_requested
|
|
242
|
-
expect(
|
|
243
|
-
expect(token
|
|
244
|
-
expect(
|
|
245
|
-
expect(
|
|
279
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
|
280
|
+
expect(token_details.token).to eql(token)
|
|
281
|
+
expect(token_details.expires).to be_within(1).of(expires)
|
|
282
|
+
expect(token_details.issued).to be_within(1).of(issued)
|
|
283
|
+
expect(token_details.capability).to eql(capability)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
context 'when response from :auth_url is text/plain content type and a token string' do
|
|
288
|
+
let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
|
|
289
|
+
let(:auth_url_content_type) { 'text/plain' }
|
|
290
|
+
let(:auth_url_response) { token }
|
|
291
|
+
|
|
292
|
+
let!(:token_details) { auth.request_token(options) }
|
|
293
|
+
|
|
294
|
+
it 'returns TokenDetails created from the token JSON' do
|
|
295
|
+
expect(request_token_stub).to_not have_been_requested
|
|
296
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
|
297
|
+
expect(token_details.token).to eql(token)
|
|
246
298
|
end
|
|
247
299
|
end
|
|
248
300
|
|
|
@@ -270,90 +322,135 @@ describe Ably::Auth do
|
|
|
270
322
|
end
|
|
271
323
|
end
|
|
272
324
|
|
|
273
|
-
context 'with
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
325
|
+
context 'with a Proc for the :auth_callback option' do
|
|
326
|
+
context 'that returns a TokenRequest' do
|
|
327
|
+
let(:client_id) { random_str }
|
|
328
|
+
let(:options) { { client_id: client_id } }
|
|
329
|
+
let!(:request_token) do
|
|
330
|
+
auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
|
|
331
|
+
@block_called = true
|
|
332
|
+
@block_options = block_options
|
|
333
|
+
auth.create_token_request(client_id: client_id)
|
|
334
|
+
end))
|
|
281
335
|
end
|
|
282
|
-
end
|
|
283
336
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
337
|
+
it 'calls the Proc when authenticating to obtain the request token' do
|
|
338
|
+
expect(@block_called).to eql(true)
|
|
339
|
+
expect(@block_options).to include(options)
|
|
340
|
+
end
|
|
288
341
|
|
|
289
|
-
|
|
290
|
-
|
|
342
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
|
343
|
+
expect(request_token.client_id).to eql(client_id)
|
|
344
|
+
end
|
|
291
345
|
end
|
|
292
|
-
end
|
|
293
346
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
347
|
+
context 'that returns a TokenDetails JSON object' do
|
|
348
|
+
let(:client_id) { random_str }
|
|
349
|
+
let(:options) { { client_id: client_id } }
|
|
350
|
+
let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
|
|
351
|
+
let(:issued) { Time.now }
|
|
352
|
+
let(:expires) { Time.now + 60}
|
|
353
|
+
let(:capability) { {'foo'=>['publish']} }
|
|
354
|
+
let(:capability_str) { JSON.dump(capability) }
|
|
355
|
+
|
|
356
|
+
let!(:token_details) do
|
|
357
|
+
auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
|
|
358
|
+
@block_called = true
|
|
359
|
+
@block_options = block_options
|
|
360
|
+
{
|
|
361
|
+
'token' => token,
|
|
362
|
+
'keyName' => 'J_0Tlg.NxCRig',
|
|
363
|
+
'clientId' => client_id,
|
|
364
|
+
'issued' => issued.to_i * 1000,
|
|
365
|
+
'expires' => expires.to_i * 1000,
|
|
366
|
+
'capability'=> capability_str
|
|
367
|
+
}
|
|
368
|
+
end))
|
|
369
|
+
end
|
|
301
370
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
@
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
371
|
+
it 'calls the Proc when authenticating to obtain the request token' do
|
|
372
|
+
expect(@block_called).to eql(true)
|
|
373
|
+
expect(@block_options).to include(options)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
|
377
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
|
378
|
+
expect(token_details.token).to eql(token)
|
|
379
|
+
expect(token_details.client_id).to eql(client_id)
|
|
380
|
+
expect(token_details.expires).to be_within(1).of(expires)
|
|
381
|
+
expect(token_details.issued).to be_within(1).of(issued)
|
|
382
|
+
expect(token_details.capability).to eql(capability)
|
|
314
383
|
end
|
|
315
384
|
end
|
|
316
385
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
386
|
+
context 'that returns a TokenDetails object' do
|
|
387
|
+
let(:client_id) { random_str }
|
|
388
|
+
|
|
389
|
+
let!(:token_details) do
|
|
390
|
+
auth.request_token(auth_callback: Proc.new do |block_options|
|
|
391
|
+
auth.create_token_request({
|
|
392
|
+
client_id: client_id
|
|
393
|
+
})
|
|
394
|
+
end)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
|
398
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
|
399
|
+
expect(token_details.client_id).to eql(client_id)
|
|
400
|
+
end
|
|
320
401
|
end
|
|
321
402
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
403
|
+
context 'that returns a Token string' do
|
|
404
|
+
let(:second_client) { Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol) }
|
|
405
|
+
let(:token) { second_client.auth.request_token.token }
|
|
406
|
+
|
|
407
|
+
let!(:token_details) do
|
|
408
|
+
auth.request_token(auth_callback: Proc.new do |block_options|
|
|
409
|
+
token
|
|
410
|
+
end)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
|
414
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
|
415
|
+
expect(token_details.token).to eql(token)
|
|
416
|
+
end
|
|
329
417
|
end
|
|
330
418
|
end
|
|
331
419
|
|
|
332
420
|
context 'persisted option', api_private: true do
|
|
333
421
|
context 'when set to true', api_private: true do
|
|
334
422
|
let(:options) { { persisted: true } }
|
|
335
|
-
let(:
|
|
423
|
+
let(:token_details) { auth.request_token(options) }
|
|
336
424
|
|
|
337
425
|
it 'returns a token with a short token ID that is used to look up the token details' do
|
|
338
|
-
expect(token.
|
|
339
|
-
expect(token
|
|
426
|
+
expect(token_details.token.length).to be < 64
|
|
427
|
+
expect(token_details.token).to match(/^#{app_id}\.A/)
|
|
340
428
|
end
|
|
341
429
|
end
|
|
342
430
|
|
|
343
431
|
context 'when omitted', api_private: true do
|
|
344
432
|
let(:options) { { } }
|
|
345
|
-
let(:
|
|
433
|
+
let(:token_details) { auth.request_token(options) }
|
|
346
434
|
|
|
347
435
|
it 'returns a literal token' do
|
|
348
|
-
expect(token.
|
|
436
|
+
expect(token_details.token.length).to be > 64
|
|
349
437
|
end
|
|
350
438
|
end
|
|
351
439
|
end
|
|
440
|
+
|
|
441
|
+
context 'with client_id' do
|
|
442
|
+
let(:client_id) { random_str }
|
|
443
|
+
let(:token_details) { auth.request_token(client_id: client_id) }
|
|
444
|
+
|
|
445
|
+
it 'returns a token with the client_id' do
|
|
446
|
+
expect(token_details.client_id).to eql(client_id)
|
|
447
|
+
end
|
|
448
|
+
end
|
|
352
449
|
end
|
|
353
450
|
|
|
354
451
|
context 'before #authorise has been called' do
|
|
355
|
-
it 'has no
|
|
356
|
-
expect(auth.
|
|
452
|
+
it 'has no current_token_details' do
|
|
453
|
+
expect(auth.current_token_details).to be_nil
|
|
357
454
|
end
|
|
358
455
|
end
|
|
359
456
|
|
|
@@ -369,62 +466,62 @@ describe Ably::Auth do
|
|
|
369
466
|
end
|
|
370
467
|
|
|
371
468
|
it 'returns a valid token' do
|
|
372
|
-
expect(auth.authorise).to be_a(Ably::Models::
|
|
469
|
+
expect(auth.authorise).to be_a(Ably::Models::TokenDetails)
|
|
373
470
|
end
|
|
374
471
|
|
|
375
472
|
it 'issues a new token if option :force => true' do
|
|
376
|
-
expect { auth.authorise(force: true) }.to change { auth.
|
|
473
|
+
expect { auth.authorise(force: true) }.to change { auth.current_token_details }
|
|
377
474
|
end
|
|
378
475
|
end
|
|
379
476
|
|
|
380
477
|
context 'with previous authorisation' do
|
|
381
478
|
before do
|
|
382
479
|
auth.authorise
|
|
383
|
-
expect(auth.
|
|
480
|
+
expect(auth.current_token_details).to_not be_expired
|
|
384
481
|
end
|
|
385
482
|
|
|
386
|
-
it 'does not request a token if
|
|
483
|
+
it 'does not request a token if current_token_details has not expired' do
|
|
387
484
|
expect(auth).to_not receive(:request_token)
|
|
388
485
|
auth.authorise
|
|
389
486
|
end
|
|
390
487
|
|
|
391
488
|
it 'requests a new token if token is expired' do
|
|
392
|
-
allow(auth.
|
|
489
|
+
allow(auth.current_token_details).to receive(:expired?).and_return(true)
|
|
393
490
|
expect(auth).to receive(:request_token)
|
|
394
|
-
expect { auth.authorise }.to change { auth.
|
|
491
|
+
expect { auth.authorise }.to change { auth.current_token_details }
|
|
395
492
|
end
|
|
396
493
|
|
|
397
494
|
it 'issues a new token if option :force => true' do
|
|
398
|
-
expect { auth.authorise(force: true) }.to change { auth.
|
|
495
|
+
expect { auth.authorise(force: true) }.to change { auth.current_token_details }
|
|
399
496
|
end
|
|
400
497
|
end
|
|
401
498
|
|
|
402
|
-
it 'updates the persisted auth options
|
|
499
|
+
it 'updates the persisted auth options that are then used for subsequent authorise requests' do
|
|
403
500
|
expect(auth.options[:ttl]).to_not eql(26)
|
|
404
501
|
auth.authorise(ttl: 26)
|
|
405
502
|
expect(auth.options[:ttl]).to eql(26)
|
|
406
503
|
end
|
|
407
504
|
|
|
408
|
-
context 'with
|
|
505
|
+
context 'with a Proc for the :auth_callback option' do
|
|
409
506
|
let(:client_id) { random_str }
|
|
410
507
|
let!(:token) do
|
|
411
|
-
auth.authorise do
|
|
508
|
+
auth.authorise(auth_callback: Proc.new do
|
|
412
509
|
@block_called ||= 0
|
|
413
510
|
@block_called += 1
|
|
414
511
|
auth.create_token_request(client_id: client_id)
|
|
415
|
-
end
|
|
512
|
+
end)
|
|
416
513
|
end
|
|
417
514
|
|
|
418
|
-
it 'calls the
|
|
515
|
+
it 'calls the Proc' do
|
|
419
516
|
expect(@block_called).to eql(1)
|
|
420
517
|
end
|
|
421
518
|
|
|
422
|
-
it 'uses the token request returned from the
|
|
519
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
|
423
520
|
expect(token.client_id).to eql(client_id)
|
|
424
521
|
end
|
|
425
522
|
|
|
426
523
|
context 'for every subsequent #request_token' do
|
|
427
|
-
context 'without a
|
|
524
|
+
context 'without a :auth_callback Proc' do
|
|
428
525
|
it 'calls the originally provided block' do
|
|
429
526
|
auth.request_token
|
|
430
527
|
expect(@block_called).to eql(2)
|
|
@@ -432,8 +529,8 @@ describe Ably::Auth do
|
|
|
432
529
|
end
|
|
433
530
|
|
|
434
531
|
context 'with a provided block' do
|
|
435
|
-
it 'does not call the originally provided
|
|
436
|
-
auth.request_token { @request_block_called = true; auth.create_token_request }
|
|
532
|
+
it 'does not call the originally provided Proc and calls the new #request_token :auth_callback Proc' do
|
|
533
|
+
auth.request_token(auth_callback: Proc.new { @request_block_called = true; auth.create_token_request })
|
|
437
534
|
expect(@block_called).to eql(1)
|
|
438
535
|
expect(@request_block_called).to eql(true)
|
|
439
536
|
end
|
|
@@ -445,19 +542,19 @@ describe Ably::Auth do
|
|
|
445
542
|
describe '#create_token_request' do
|
|
446
543
|
let(:ttl) { 60 * 60 }
|
|
447
544
|
let(:capability) { { :foo => ["publish"] } }
|
|
448
|
-
let(:
|
|
449
|
-
subject { auth.create_token_request(
|
|
545
|
+
let(:token_request_options) { Hash.new }
|
|
546
|
+
subject { auth.create_token_request(token_request_options) }
|
|
450
547
|
|
|
451
|
-
it 'uses the key
|
|
452
|
-
expect(subject['
|
|
548
|
+
it 'uses the key name from the client' do
|
|
549
|
+
expect(subject['keyName']).to eql(key_name)
|
|
453
550
|
end
|
|
454
551
|
|
|
455
552
|
it 'uses the default TTL' do
|
|
456
|
-
expect(subject['ttl']).to eql(Ably::
|
|
553
|
+
expect(subject['ttl']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl) * 1000)
|
|
457
554
|
end
|
|
458
555
|
|
|
459
556
|
it 'uses the default capability' do
|
|
460
|
-
expect(subject['capability']).to eql(Ably::
|
|
557
|
+
expect(subject['capability']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability).to_json)
|
|
461
558
|
end
|
|
462
559
|
|
|
463
560
|
context 'the nonce' do
|
|
@@ -471,25 +568,25 @@ describe Ably::Auth do
|
|
|
471
568
|
end
|
|
472
569
|
end
|
|
473
570
|
|
|
474
|
-
%w(ttl
|
|
571
|
+
%w(ttl nonce client_id).each do |attribute|
|
|
475
572
|
context "with option :#{attribute}" do
|
|
476
|
-
let(:option_value) { random_int_str(1_000_000_000) }
|
|
573
|
+
let(:option_value) { random_int_str(1_000_000_000).to_i }
|
|
477
574
|
before do
|
|
478
|
-
|
|
575
|
+
token_request_options[attribute.to_sym] = option_value
|
|
479
576
|
end
|
|
480
577
|
it "overrides default" do
|
|
481
|
-
expect(subject
|
|
578
|
+
expect(subject.public_send(attribute).to_s).to eql(option_value.to_s)
|
|
482
579
|
end
|
|
483
580
|
end
|
|
484
581
|
end
|
|
485
582
|
|
|
486
583
|
context 'with additional invalid attributes' do
|
|
487
|
-
let(:
|
|
584
|
+
let(:token_request_options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
|
|
488
585
|
specify 'are ignored' do
|
|
489
|
-
expect(subject.keys).to_not include(:is_not_used_by_token_request)
|
|
490
|
-
expect(subject.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
|
|
491
|
-
expect(subject.keys).to include(
|
|
492
|
-
expect(subject
|
|
586
|
+
expect(subject.hash.keys).to_not include(:is_not_used_by_token_request)
|
|
587
|
+
expect(subject.hash.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
|
|
588
|
+
expect(subject.hash.keys).to include(:nonce)
|
|
589
|
+
expect(subject.nonce).to eql('valid')
|
|
493
590
|
end
|
|
494
591
|
end
|
|
495
592
|
|
|
@@ -497,47 +594,52 @@ describe Ably::Auth do
|
|
|
497
594
|
let(:client) { Ably::Rest::Client.new(auth_url: 'http://example.com', protocol: protocol) }
|
|
498
595
|
|
|
499
596
|
it 'should raise an exception if key secret is missing' do
|
|
500
|
-
expect { auth.create_token_request(
|
|
597
|
+
expect { auth.create_token_request(key_name: 'name') }.to raise_error Ably::Exceptions::TokenRequestError
|
|
501
598
|
end
|
|
502
599
|
|
|
503
|
-
it 'should raise an exception if key
|
|
600
|
+
it 'should raise an exception if key name is missing' do
|
|
504
601
|
expect { auth.create_token_request(key_secret: 'secret') }.to raise_error Ably::Exceptions::TokenRequestError
|
|
505
602
|
end
|
|
506
603
|
end
|
|
507
604
|
|
|
508
605
|
context 'with :query_time option' do
|
|
509
606
|
let(:time) { Time.now - 30 }
|
|
510
|
-
let(:
|
|
607
|
+
let(:token_request_options) { { query_time: true } }
|
|
511
608
|
|
|
512
609
|
it 'queries the server for the timestamp' do
|
|
513
610
|
expect(client).to receive(:time).and_return(time)
|
|
514
|
-
expect(subject['timestamp']).to
|
|
611
|
+
expect(subject['timestamp']).to be_within(1).of(time.to_f * 1000)
|
|
515
612
|
end
|
|
516
613
|
end
|
|
517
614
|
|
|
518
615
|
context 'with :timestamp option' do
|
|
519
616
|
let(:token_request_time) { Time.now + 5 }
|
|
520
|
-
let(:
|
|
617
|
+
let(:token_request_options) { { timestamp: token_request_time } }
|
|
521
618
|
|
|
522
619
|
it 'uses the provided timestamp in the token request' do
|
|
523
|
-
expect(subject['timestamp']).to
|
|
620
|
+
expect(subject['timestamp']).to be_within(1).of(token_request_time.to_f * 1000)
|
|
524
621
|
end
|
|
525
622
|
end
|
|
526
623
|
|
|
527
624
|
context 'signing' do
|
|
528
|
-
let(:
|
|
625
|
+
let(:token_request_options) do
|
|
529
626
|
{
|
|
530
|
-
|
|
531
|
-
ttl:
|
|
627
|
+
key_name: random_str,
|
|
628
|
+
ttl: random_int_str.to_i,
|
|
532
629
|
capability: random_str,
|
|
533
630
|
client_id: random_str,
|
|
534
|
-
timestamp: random_int_str,
|
|
631
|
+
timestamp: random_int_str.to_i,
|
|
535
632
|
nonce: random_str
|
|
536
633
|
}
|
|
537
634
|
end
|
|
538
635
|
|
|
636
|
+
# TokenRequest expects times in milliseconds, whereas create_token_request assumes Ruby default of seconds
|
|
637
|
+
let(:token_request_attributes) do
|
|
638
|
+
token_request_options.merge(timestamp: token_request_options[:timestamp] * 1000, ttl: token_request_options[:ttl] * 1000)
|
|
639
|
+
end
|
|
640
|
+
|
|
539
641
|
it 'generates a valid HMAC' do
|
|
540
|
-
hmac = hmac_for(
|
|
642
|
+
hmac = hmac_for(Ably::Models::TokenRequest(token_request_attributes).hash, key_secret)
|
|
541
643
|
expect(subject['mac']).to eql(hmac)
|
|
542
644
|
end
|
|
543
645
|
end
|
|
@@ -546,20 +648,20 @@ describe Ably::Auth do
|
|
|
546
648
|
context 'using token authentication' do
|
|
547
649
|
let(:capability) { { :foo => ["publish"] } }
|
|
548
650
|
|
|
549
|
-
describe 'with :
|
|
651
|
+
describe 'with :token option' do
|
|
550
652
|
let(:ttl) { 60 * 60 }
|
|
551
|
-
let(:
|
|
653
|
+
let(:token_details) do
|
|
552
654
|
auth.request_token(
|
|
553
655
|
ttl: ttl,
|
|
554
656
|
capability: capability
|
|
555
657
|
)
|
|
556
658
|
end
|
|
557
|
-
let(:
|
|
659
|
+
let(:token) { token_details.token }
|
|
558
660
|
let(:token_auth_client) do
|
|
559
|
-
Ably::Rest::Client.new(
|
|
661
|
+
Ably::Rest::Client.new(token: token, environment: environment, protocol: protocol)
|
|
560
662
|
end
|
|
561
663
|
|
|
562
|
-
it 'authenticates successfully using the provided :
|
|
664
|
+
it 'authenticates successfully using the provided :token' do
|
|
563
665
|
expect(token_auth_client.channel('foo').publish('event', 'data')).to be_truthy
|
|
564
666
|
end
|
|
565
667
|
|
|
@@ -587,25 +689,23 @@ describe Ably::Auth do
|
|
|
587
689
|
context 'when implicit as a result of using :client id' do
|
|
588
690
|
let(:client_id) { '999' }
|
|
589
691
|
let(:client) do
|
|
590
|
-
Ably::Rest::Client.new(
|
|
692
|
+
Ably::Rest::Client.new(key: api_key, client_id: client_id, environment: environment, protocol: protocol)
|
|
591
693
|
end
|
|
592
|
-
let(:
|
|
694
|
+
let(:token) { 'unique-token' }
|
|
593
695
|
let(:token_response) do
|
|
594
696
|
{
|
|
595
|
-
|
|
596
|
-
id: token_id
|
|
597
|
-
}
|
|
697
|
+
token: token
|
|
598
698
|
}.to_json
|
|
599
699
|
end
|
|
600
700
|
|
|
601
701
|
context 'and requests to the Ably server are mocked', :webmock do
|
|
602
702
|
let!(:request_token_stub) do
|
|
603
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
|
703
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
|
604
704
|
to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' })
|
|
605
705
|
end
|
|
606
706
|
let!(:publish_message_stub) do
|
|
607
707
|
stub_request(:post, "#{client.endpoint}/channels/foo/publish").
|
|
608
|
-
with(headers: { 'Authorization' => "Bearer #{encode64(
|
|
708
|
+
with(headers: { 'Authorization' => "Bearer #{encode64(token)}" }).
|
|
609
709
|
to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
|
|
610
710
|
end
|
|
611
711
|
|
|
@@ -616,7 +716,7 @@ describe Ably::Auth do
|
|
|
616
716
|
end
|
|
617
717
|
|
|
618
718
|
describe 'a token is created' do
|
|
619
|
-
let(:token) { client.auth.
|
|
719
|
+
let(:token) { client.auth.current_token_details }
|
|
620
720
|
|
|
621
721
|
it 'before a request is made' do
|
|
622
722
|
expect(token).to be_nil
|
|
@@ -629,22 +729,26 @@ describe Ably::Auth do
|
|
|
629
729
|
it 'with capability and TTL defaults' do
|
|
630
730
|
client.channel('foo').publish('event', 'data')
|
|
631
731
|
|
|
632
|
-
expect(token).to be_a(Ably::Models::
|
|
633
|
-
capability_with_str_key = Ably::
|
|
634
|
-
capability = Hash[capability_with_str_key.keys.map(&:
|
|
732
|
+
expect(token).to be_a(Ably::Models::TokenDetails)
|
|
733
|
+
capability_with_str_key = Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)
|
|
734
|
+
capability = Hash[capability_with_str_key.keys.map(&:to_s).zip(capability_with_str_key.values)]
|
|
635
735
|
expect(token.capability).to eq(capability)
|
|
636
|
-
expect(token.
|
|
736
|
+
expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl))
|
|
637
737
|
expect(token.client_id).to eq(client_id)
|
|
638
738
|
end
|
|
639
739
|
end
|
|
640
740
|
end
|
|
641
741
|
end
|
|
642
742
|
|
|
643
|
-
context 'when using an :
|
|
743
|
+
context 'when using an :key and basic auth' do
|
|
644
744
|
specify '#using_token_auth? is false' do
|
|
645
745
|
expect(auth).to_not be_using_token_auth
|
|
646
746
|
end
|
|
647
747
|
|
|
748
|
+
specify '#key attribute contains the key string' do
|
|
749
|
+
expect(auth.key).to eql(api_key)
|
|
750
|
+
end
|
|
751
|
+
|
|
648
752
|
specify '#using_basic_auth? is true' do
|
|
649
753
|
expect(auth).to be_using_basic_auth
|
|
650
754
|
end
|