ably 0.7.6 → 0.8.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 +4 -4
- data/README.md +11 -7
- data/SPEC.md +310 -269
- data/lib/ably/auth.rb +177 -127
- data/lib/ably/models/presence_message.rb +1 -1
- data/lib/ably/models/protocol_message.rb +1 -2
- data/lib/ably/models/token_details.rb +101 -0
- data/lib/ably/models/token_request.rb +108 -0
- data/lib/ably/modules/http_helpers.rb +1 -1
- data/lib/ably/realtime.rb +2 -6
- data/lib/ably/realtime/channel.rb +14 -8
- data/lib/ably/realtime/client.rb +2 -6
- data/lib/ably/realtime/connection.rb +4 -2
- data/lib/ably/rest.rb +2 -6
- data/lib/ably/rest/channel.rb +10 -6
- data/lib/ably/rest/client.rb +15 -16
- data/lib/ably/rest/presence.rb +12 -10
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/client_spec.rb +15 -15
- data/spec/acceptance/realtime/connection_failures_spec.rb +3 -3
- data/spec/acceptance/realtime/connection_spec.rb +9 -9
- data/spec/acceptance/rest/auth_spec.rb +248 -172
- data/spec/acceptance/rest/base_spec.rb +8 -6
- data/spec/acceptance/rest/channel_spec.rb +9 -2
- data/spec/acceptance/rest/client_spec.rb +21 -21
- data/spec/acceptance/rest/presence_spec.rb +12 -5
- data/spec/acceptance/rest/stats_spec.rb +4 -4
- data/spec/rspec_config.rb +3 -2
- data/spec/shared/client_initializer_behaviour.rb +21 -24
- data/spec/support/api_helper.rb +3 -3
- data/spec/support/test_app.rb +9 -9
- data/spec/unit/auth_spec.rb +17 -0
- data/spec/unit/models/token_details_spec.rb +111 -0
- data/spec/unit/models/token_request_spec.rb +110 -0
- data/spec/unit/rest/client_spec.rb +1 -1
- metadata +8 -5
- data/lib/ably/models/token.rb +0 -74
- data/spec/unit/models/token_spec.rb +0 -86
data/lib/ably/rest/presence.rb
CHANGED
@@ -23,15 +23,14 @@ module Ably
|
|
23
23
|
# Obtain the set of members currently present for a channel
|
24
24
|
#
|
25
25
|
# @param [Hash] options the options for the set of members present
|
26
|
-
# @option options [Integer
|
27
|
-
# @option options [Integer,Time] :end Time or millisecond since epoch
|
28
|
-
# @option options [Symbol] :direction `:forwards` or `:backwards`
|
29
|
-
# @option options [Integer] :limit Maximum number of members to retrieve up to 10,000
|
26
|
+
# @option options [Integer] :limit Maximum number of members to retrieve up to 1,000, defaults to 100
|
30
27
|
#
|
31
28
|
# @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResource page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResource#items #items}.
|
32
29
|
#
|
33
30
|
def get(options = {})
|
34
|
-
options = options
|
31
|
+
options = options = {
|
32
|
+
:limit => 100
|
33
|
+
}.merge(options)
|
35
34
|
|
36
35
|
paginated_options = {
|
37
36
|
coerce_into: 'Ably::Models::PresenceMessage',
|
@@ -50,16 +49,19 @@ module Ably
|
|
50
49
|
# Return the presence messages history for the channel
|
51
50
|
#
|
52
51
|
# @param [Hash] options the options for the message history request
|
53
|
-
# @option options [Integer,Time] :start
|
54
|
-
# @option options [Integer,Time] :end
|
55
|
-
# @option options [Symbol] :direction
|
56
|
-
# @option options [Integer] :limit Maximum number of
|
52
|
+
# @option options [Integer,Time] :start Ensure earliest time or millisecond since epoch for any presence messages retrieved is +:start+
|
53
|
+
# @option options [Integer,Time] :end Ensure latest time or millisecond since epoch for any presence messages retrieved is +:end+
|
54
|
+
# @option options [Symbol] :direction +:forwards+ or +:backwards+, defaults to +:backwards+
|
55
|
+
# @option options [Integer] :limit Maximum number of messages to retrieve up to 1,000, defaults to 100
|
57
56
|
#
|
58
57
|
# @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResource page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResource#items #items}.
|
59
58
|
#
|
60
59
|
def history(options = {})
|
61
60
|
url = "#{base_path}/history"
|
62
|
-
options = options
|
61
|
+
options = options = {
|
62
|
+
:direction => :backwards,
|
63
|
+
:limit => 100
|
64
|
+
}.merge(options)
|
63
65
|
|
64
66
|
[:start, :end].each { |option| options[option] = as_since_epoch(options[option]) if options.has_key?(option) }
|
65
67
|
|
data/lib/ably/version.rb
CHANGED
@@ -17,9 +17,9 @@ describe Ably::Realtime::Client, :event_machine do
|
|
17
17
|
context 'basic auth' do
|
18
18
|
it 'is enabled by default with a provided :key option' do
|
19
19
|
connection.on(:connected) do
|
20
|
-
expect(auth_params[:
|
20
|
+
expect(auth_params[:key]).to_not be_nil
|
21
21
|
expect(auth_params[:access_token]).to be_nil
|
22
|
-
expect(subject.auth.
|
22
|
+
expect(subject.auth.current_token_details).to be_nil
|
23
23
|
stop_reactor
|
24
24
|
end
|
25
25
|
end
|
@@ -44,15 +44,15 @@ describe Ably::Realtime::Client, :event_machine do
|
|
44
44
|
[true, false].each do |tls_enabled|
|
45
45
|
context "with TLS #{tls_enabled ? 'enabled' : 'disabled'}" do
|
46
46
|
let(:capability) { { :foo => ["publish"] } }
|
47
|
-
let(:
|
48
|
-
let(:client_options) { default_options.merge(
|
47
|
+
let(:token_details) { Ably::Realtime::Client.new(default_options).auth.request_token(capability: capability) }
|
48
|
+
let(:client_options) { default_options.merge(token: token_details.token) }
|
49
49
|
|
50
|
-
context 'and a pre-generated Token provided with the :
|
50
|
+
context 'and a pre-generated Token provided with the :token option' do
|
51
51
|
it 'connects using token auth' do
|
52
52
|
connection.on(:connected) do
|
53
53
|
expect(auth_params[:access_token]).to_not be_nil
|
54
|
-
expect(auth_params[:
|
55
|
-
expect(subject.auth.
|
54
|
+
expect(auth_params[:key]).to be_nil
|
55
|
+
expect(subject.auth.current_token_details).to be_nil
|
56
56
|
stop_reactor
|
57
57
|
end
|
58
58
|
end
|
@@ -63,7 +63,7 @@ describe Ably::Realtime::Client, :event_machine do
|
|
63
63
|
|
64
64
|
it 'automatically authorises on connect and generates a token' do
|
65
65
|
connection.on(:connected) do
|
66
|
-
expect(subject.auth.
|
66
|
+
expect(subject.auth.current_token_details).to_not be_nil
|
67
67
|
expect(auth_params[:access_token]).to_not be_nil
|
68
68
|
stop_reactor
|
69
69
|
end
|
@@ -79,7 +79,7 @@ describe Ably::Realtime::Client, :event_machine do
|
|
79
79
|
connection.on(:connected) do
|
80
80
|
expect(connection.state).to eq(:connected)
|
81
81
|
expect(auth_params[:access_token]).to_not be_nil
|
82
|
-
expect(auth_params[:
|
82
|
+
expect(auth_params[:key]).to be_nil
|
83
83
|
stop_reactor
|
84
84
|
end
|
85
85
|
end
|
@@ -88,27 +88,27 @@ describe Ably::Realtime::Client, :event_machine do
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
context 'with
|
91
|
+
context 'with a Proc for the :auth_callback option' do
|
92
92
|
let(:client_id) { random_str }
|
93
93
|
let(:auth) { subject.auth }
|
94
94
|
|
95
95
|
subject do
|
96
|
-
Ably::Realtime::Client.new(client_options
|
96
|
+
Ably::Realtime::Client.new(client_options.merge(auth_callback: Proc.new do
|
97
97
|
@block_called = true
|
98
98
|
auth.create_token_request(client_id: client_id)
|
99
|
-
end
|
99
|
+
end))
|
100
100
|
end
|
101
101
|
|
102
|
-
it 'calls the
|
102
|
+
it 'calls the Proc' do
|
103
103
|
connection.on(:connected) do
|
104
104
|
expect(@block_called).to eql(true)
|
105
105
|
stop_reactor
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
it 'uses the token request when requesting a new token' do
|
109
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
110
110
|
connection.on(:connected) do
|
111
|
-
expect(auth.
|
111
|
+
expect(auth.current_token_details.client_id).to eql(client_id)
|
112
112
|
stop_reactor
|
113
113
|
end
|
114
114
|
end
|
@@ -21,7 +21,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
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|
|
@@ -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
|
@@ -143,14 +143,14 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
143
143
|
|
144
144
|
context 'the server' do
|
145
145
|
it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 15 do
|
146
|
-
original_token = client.auth.
|
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(key: nil,
|
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)
|
@@ -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)
|
@@ -52,37 +52,42 @@ 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, multiply: false)
|
79
|
+
return value unless %w(timestamp ttl).include?(field_name)
|
80
|
+
value.to_i * (multiply ? multiply : 1)
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:random) { coerce_if_time_value(option, random_int_str) }
|
79
84
|
let(:options) { { option.to_sym => random } }
|
80
85
|
|
81
|
-
let(:token_response) { {
|
86
|
+
let(:token_response) { {} }
|
82
87
|
let!(:request_token_stub) do
|
83
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
88
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
84
89
|
with do |request|
|
85
|
-
request_body_includes(request, protocol, option, random)
|
90
|
+
request_body_includes(request, protocol, option, coerce_if_time_value(option, random, multiply: 1000))
|
86
91
|
end.to_return(
|
87
92
|
:status => 201,
|
88
93
|
:body => serialize(token_response, protocol),
|
@@ -92,25 +97,25 @@ describe Ably::Auth do
|
|
92
97
|
|
93
98
|
before { auth.request_token options }
|
94
99
|
|
95
|
-
it
|
100
|
+
it "overrides default and uses camelCase notation for attributes" do
|
96
101
|
expect(request_token_stub).to have_been_requested
|
97
102
|
end
|
98
103
|
end
|
99
104
|
end
|
100
105
|
|
101
|
-
context 'with :
|
102
|
-
let(:
|
106
|
+
context 'with :key option', :webmock do
|
107
|
+
let(:key_name) { "app.#{random_str}" }
|
103
108
|
let(:key_secret) { random_str }
|
104
109
|
let(:nonce) { random_str }
|
105
|
-
let(:token_options) { {
|
110
|
+
let(:token_options) { { key: "#{key_name}:#{key_secret}", nonce: nonce, timestamp: Time.now } }
|
106
111
|
let(:token_request) { auth.create_token_request(token_options) }
|
107
112
|
let(:mac) do
|
108
113
|
hmac_for(token_request, key_secret)
|
109
114
|
end
|
110
115
|
|
111
|
-
let(:token_response) { {
|
116
|
+
let(:token_response) { {} }
|
112
117
|
let!(:request_token_stub) do
|
113
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
118
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
114
119
|
with do |request|
|
115
120
|
request_body_includes(request, protocol, 'mac', mac)
|
116
121
|
end.to_return(
|
@@ -119,9 +124,38 @@ describe Ably::Auth do
|
|
119
124
|
:headers => { 'Content-Type' => content_type })
|
120
125
|
end
|
121
126
|
|
122
|
-
let!(:token) { auth.request_token(token_options) }
|
127
|
+
let!(:token) { puts token_options; auth.request_token(token_options) }
|
123
128
|
|
124
|
-
specify '
|
129
|
+
specify 'key_name is used in request and signing uses key_secret' do
|
130
|
+
expect(request_token_stub).to have_been_requested
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'with :key_name & :key_secret options', :webmock do
|
135
|
+
let(:key_name) { "app.#{random_str}" }
|
136
|
+
let(:key_secret) { random_str }
|
137
|
+
let(:nonce) { random_str }
|
138
|
+
|
139
|
+
let(:name_secret_token_options) { { key_name: key_name, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
|
140
|
+
let(:token_request) { auth.create_token_request(name_secret_token_options) }
|
141
|
+
let(:mac) do
|
142
|
+
hmac_for(token_request, key_secret)
|
143
|
+
end
|
144
|
+
|
145
|
+
let(:token_response) { {} }
|
146
|
+
let!(:request_token_stub) do
|
147
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
148
|
+
with do |request|
|
149
|
+
request_body_includes(request, protocol, 'mac', mac)
|
150
|
+
end.to_return(
|
151
|
+
:status => 201,
|
152
|
+
:body => serialize(token_response, protocol),
|
153
|
+
:headers => { 'Content-Type' => content_type })
|
154
|
+
end
|
155
|
+
|
156
|
+
let!(:token) { auth.request_token(name_secret_token_options); }
|
157
|
+
|
158
|
+
specify 'key_name is used in request and signing uses key_secret' do
|
125
159
|
expect(request_token_stub).to have_been_requested
|
126
160
|
end
|
127
161
|
end
|
@@ -146,8 +180,8 @@ describe Ably::Auth do
|
|
146
180
|
|
147
181
|
context 'with :auth_url option', :webmock do
|
148
182
|
let(:auth_url) { 'https://www.fictitious.com/get_token' }
|
149
|
-
let(:auth_url_response) { {
|
150
|
-
let(:token_response) { {
|
183
|
+
let(:auth_url_response) { { keyName: key_name }.to_json }
|
184
|
+
let(:token_response) { {} }
|
151
185
|
let(:query_params) { nil }
|
152
186
|
let(:headers) { nil }
|
153
187
|
let(:auth_method) { :get }
|
@@ -166,15 +200,16 @@ describe Ably::Auth do
|
|
166
200
|
stub.with(:headers => headers) unless headers.nil?
|
167
201
|
stub.to_return(
|
168
202
|
:status => 201,
|
169
|
-
:body => auth_url_response
|
170
|
-
:headers => { 'Content-Type' =>
|
203
|
+
:body => auth_url_response,
|
204
|
+
:headers => { 'Content-Type' => auth_url_content_type }
|
171
205
|
)
|
172
206
|
end
|
207
|
+
let(:auth_url_content_type) { 'application/json' }
|
173
208
|
|
174
209
|
let!(:request_token_stub) do
|
175
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
210
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
176
211
|
with do |request|
|
177
|
-
request_body_includes(request, protocol, '
|
212
|
+
request_body_includes(request, protocol, 'key_name', key_name)
|
178
213
|
end.to_return(
|
179
214
|
:status => 201,
|
180
215
|
:body => serialize(token_response, protocol),
|
@@ -191,7 +226,7 @@ describe Ably::Auth do
|
|
191
226
|
end
|
192
227
|
|
193
228
|
it 'returns a valid token generated from the token request' do
|
194
|
-
expect(token).to be_a(Ably::Models::
|
229
|
+
expect(token).to be_a(Ably::Models::TokenDetails)
|
195
230
|
end
|
196
231
|
|
197
232
|
context 'with :query_params' do
|
@@ -220,29 +255,45 @@ describe Ably::Auth do
|
|
220
255
|
end
|
221
256
|
end
|
222
257
|
|
223
|
-
context 'when response from :auth_url is a token' do
|
224
|
-
let(:
|
225
|
-
let(:
|
258
|
+
context 'when response from :auth_url is a token details object' do
|
259
|
+
let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
|
260
|
+
let(:issued) { Time.now }
|
226
261
|
let(:expires) { Time.now + 60}
|
227
262
|
let(:capability) { {'foo'=>['publish']} }
|
263
|
+
let(:capability_str) { JSON.dump(capability) }
|
228
264
|
let(:auth_url_response) do
|
229
265
|
{
|
230
|
-
'
|
231
|
-
'
|
232
|
-
'
|
233
|
-
'expires' => expires.to_i,
|
234
|
-
'capability'=>
|
235
|
-
}
|
266
|
+
'token' => token,
|
267
|
+
'key_name' => 'J_0Tlg.NxCRig',
|
268
|
+
'issued' => issued.to_i * 1000,
|
269
|
+
'expires' => expires.to_i * 1000,
|
270
|
+
'capability'=> capability_str
|
271
|
+
}.to_json
|
236
272
|
end
|
237
273
|
|
238
|
-
let!(:
|
274
|
+
let!(:token_details) { auth.request_token(options) }
|
275
|
+
|
276
|
+
it 'returns TokenDetails created from the token JSON' do
|
277
|
+
expect(request_token_stub).to_not have_been_requested
|
278
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
279
|
+
expect(token_details.token).to eql(token)
|
280
|
+
expect(token_details.expires).to be_within(1).of(expires)
|
281
|
+
expect(token_details.issued).to be_within(1).of(issued)
|
282
|
+
expect(token_details.capability).to eql(capability)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'when response from :auth_url is text/plain content type and a token string' do
|
287
|
+
let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
|
288
|
+
let(:auth_url_content_type) { 'text/plain' }
|
289
|
+
let(:auth_url_response) { token }
|
239
290
|
|
240
|
-
|
291
|
+
let!(:token_details) { auth.request_token(options) }
|
292
|
+
|
293
|
+
it 'returns TokenDetails created from the token JSON' do
|
241
294
|
expect(request_token_stub).to_not have_been_requested
|
242
|
-
expect(
|
243
|
-
expect(token
|
244
|
-
expect(token.issued_at).to be_within(1).of(issued_at)
|
245
|
-
expect(token.capability.to_json).to eql(capability.to_json)
|
295
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
296
|
+
expect(token_details.token).to eql(token)
|
246
297
|
end
|
247
298
|
end
|
248
299
|
|
@@ -270,99 +321,135 @@ describe Ably::Auth do
|
|
270
321
|
end
|
271
322
|
end
|
272
323
|
|
273
|
-
context 'with
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
324
|
+
context 'with a Proc for the :auth_callback option' do
|
325
|
+
context 'that returns a TokenRequest' do
|
326
|
+
let(:client_id) { random_str }
|
327
|
+
let(:options) { { client_id: client_id } }
|
328
|
+
let!(:request_token) do
|
329
|
+
auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
|
330
|
+
@block_called = true
|
331
|
+
@block_options = block_options
|
332
|
+
auth.create_token_request(client_id: client_id)
|
333
|
+
end))
|
281
334
|
end
|
282
|
-
end
|
283
335
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
336
|
+
it 'calls the Proc when authenticating to obtain the request token' do
|
337
|
+
expect(@block_called).to eql(true)
|
338
|
+
expect(@block_options).to include(options)
|
339
|
+
end
|
288
340
|
|
289
|
-
|
290
|
-
|
341
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
342
|
+
expect(request_token.client_id).to eql(client_id)
|
343
|
+
end
|
291
344
|
end
|
292
|
-
end
|
293
345
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
346
|
+
context 'that returns a TokenDetails JSON object' do
|
347
|
+
let(:client_id) { random_str }
|
348
|
+
let(:options) { { client_id: client_id } }
|
349
|
+
let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
|
350
|
+
let(:issued) { Time.now }
|
351
|
+
let(:expires) { Time.now + 60}
|
352
|
+
let(:capability) { {'foo'=>['publish']} }
|
353
|
+
let(:capability_str) { JSON.dump(capability) }
|
354
|
+
|
355
|
+
let!(:token_details) do
|
356
|
+
auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
|
357
|
+
@block_called = true
|
358
|
+
@block_options = block_options
|
359
|
+
{
|
360
|
+
'token' => token,
|
361
|
+
'keyName' => 'J_0Tlg.NxCRig',
|
362
|
+
'clientId' => client_id,
|
363
|
+
'issued' => issued.to_i * 1000,
|
364
|
+
'expires' => expires.to_i * 1000,
|
365
|
+
'capability'=> capability_str
|
366
|
+
}
|
367
|
+
end))
|
368
|
+
end
|
301
369
|
|
302
|
-
|
303
|
-
|
304
|
-
@
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
370
|
+
it 'calls the Proc when authenticating to obtain the request token' do
|
371
|
+
expect(@block_called).to eql(true)
|
372
|
+
expect(@block_options).to include(options)
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
376
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
377
|
+
expect(token_details.token).to eql(token)
|
378
|
+
expect(token_details.client_id).to eql(client_id)
|
379
|
+
expect(token_details.expires).to be_within(1).of(expires)
|
380
|
+
expect(token_details.issued).to be_within(1).of(issued)
|
381
|
+
expect(token_details.capability).to eql(capability)
|
314
382
|
end
|
315
383
|
end
|
316
384
|
|
317
|
-
|
318
|
-
|
319
|
-
|
385
|
+
context 'that returns a TokenDetails object' do
|
386
|
+
let(:client_id) { random_str }
|
387
|
+
|
388
|
+
let!(:token_details) do
|
389
|
+
auth.request_token(auth_callback: Proc.new do |block_options|
|
390
|
+
auth.create_token_request({
|
391
|
+
client_id: client_id
|
392
|
+
})
|
393
|
+
end)
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
397
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
398
|
+
expect(token_details.client_id).to eql(client_id)
|
399
|
+
end
|
320
400
|
end
|
321
401
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
402
|
+
context 'that returns a Token string' do
|
403
|
+
let(:second_client) { Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol) }
|
404
|
+
let(:token) { second_client.auth.request_token.token }
|
405
|
+
|
406
|
+
let!(:token_details) do
|
407
|
+
auth.request_token(auth_callback: Proc.new do |block_options|
|
408
|
+
token
|
409
|
+
end)
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
413
|
+
expect(token_details).to be_a(Ably::Models::TokenDetails)
|
414
|
+
expect(token_details.token).to eql(token)
|
415
|
+
end
|
329
416
|
end
|
330
417
|
end
|
331
418
|
|
332
419
|
context 'persisted option', api_private: true do
|
333
420
|
context 'when set to true', api_private: true do
|
334
421
|
let(:options) { { persisted: true } }
|
335
|
-
let(:
|
422
|
+
let(:token_details) { auth.request_token(options) }
|
336
423
|
|
337
424
|
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
|
425
|
+
expect(token_details.token.length).to be < 64
|
426
|
+
expect(token_details.token).to match(/^#{app_id}\.A/)
|
340
427
|
end
|
341
428
|
end
|
342
429
|
|
343
430
|
context 'when omitted', api_private: true do
|
344
431
|
let(:options) { { } }
|
345
|
-
let(:
|
432
|
+
let(:token_details) { auth.request_token(options) }
|
346
433
|
|
347
434
|
it 'returns a literal token' do
|
348
|
-
expect(token.
|
435
|
+
expect(token_details.token.length).to be > 64
|
349
436
|
end
|
350
437
|
end
|
351
438
|
end
|
352
439
|
|
353
440
|
context 'with client_id' do
|
354
441
|
let(:client_id) { random_str }
|
355
|
-
let(:
|
442
|
+
let(:token_details) { auth.request_token(client_id: client_id) }
|
356
443
|
|
357
444
|
it 'returns a token with the client_id' do
|
358
|
-
expect(
|
445
|
+
expect(token_details.client_id).to eql(client_id)
|
359
446
|
end
|
360
447
|
end
|
361
448
|
end
|
362
449
|
|
363
450
|
context 'before #authorise has been called' do
|
364
|
-
it 'has no
|
365
|
-
expect(auth.
|
451
|
+
it 'has no current_token_details' do
|
452
|
+
expect(auth.current_token_details).to be_nil
|
366
453
|
end
|
367
454
|
end
|
368
455
|
|
@@ -378,62 +465,62 @@ describe Ably::Auth do
|
|
378
465
|
end
|
379
466
|
|
380
467
|
it 'returns a valid token' do
|
381
|
-
expect(auth.authorise).to be_a(Ably::Models::
|
468
|
+
expect(auth.authorise).to be_a(Ably::Models::TokenDetails)
|
382
469
|
end
|
383
470
|
|
384
471
|
it 'issues a new token if option :force => true' do
|
385
|
-
expect { auth.authorise(force: true) }.to change { auth.
|
472
|
+
expect { auth.authorise(force: true) }.to change { auth.current_token_details }
|
386
473
|
end
|
387
474
|
end
|
388
475
|
|
389
476
|
context 'with previous authorisation' do
|
390
477
|
before do
|
391
478
|
auth.authorise
|
392
|
-
expect(auth.
|
479
|
+
expect(auth.current_token_details).to_not be_expired
|
393
480
|
end
|
394
481
|
|
395
|
-
it 'does not request a token if
|
482
|
+
it 'does not request a token if current_token_details has not expired' do
|
396
483
|
expect(auth).to_not receive(:request_token)
|
397
484
|
auth.authorise
|
398
485
|
end
|
399
486
|
|
400
487
|
it 'requests a new token if token is expired' do
|
401
|
-
allow(auth.
|
488
|
+
allow(auth.current_token_details).to receive(:expired?).and_return(true)
|
402
489
|
expect(auth).to receive(:request_token)
|
403
|
-
expect { auth.authorise }.to change { auth.
|
490
|
+
expect { auth.authorise }.to change { auth.current_token_details }
|
404
491
|
end
|
405
492
|
|
406
493
|
it 'issues a new token if option :force => true' do
|
407
|
-
expect { auth.authorise(force: true) }.to change { auth.
|
494
|
+
expect { auth.authorise(force: true) }.to change { auth.current_token_details }
|
408
495
|
end
|
409
496
|
end
|
410
497
|
|
411
|
-
it 'updates the persisted auth options
|
498
|
+
it 'updates the persisted auth options that are then used for subsequent authorise requests' do
|
412
499
|
expect(auth.options[:ttl]).to_not eql(26)
|
413
500
|
auth.authorise(ttl: 26)
|
414
501
|
expect(auth.options[:ttl]).to eql(26)
|
415
502
|
end
|
416
503
|
|
417
|
-
context 'with
|
504
|
+
context 'with a Proc for the :auth_callback option' do
|
418
505
|
let(:client_id) { random_str }
|
419
506
|
let!(:token) do
|
420
|
-
auth.authorise do
|
507
|
+
auth.authorise(auth_callback: Proc.new do
|
421
508
|
@block_called ||= 0
|
422
509
|
@block_called += 1
|
423
510
|
auth.create_token_request(client_id: client_id)
|
424
|
-
end
|
511
|
+
end)
|
425
512
|
end
|
426
513
|
|
427
|
-
it 'calls the
|
514
|
+
it 'calls the Proc' do
|
428
515
|
expect(@block_called).to eql(1)
|
429
516
|
end
|
430
517
|
|
431
|
-
it 'uses the token request returned from the
|
518
|
+
it 'uses the token request returned from the callback when requesting a new token' do
|
432
519
|
expect(token.client_id).to eql(client_id)
|
433
520
|
end
|
434
521
|
|
435
522
|
context 'for every subsequent #request_token' do
|
436
|
-
context 'without a
|
523
|
+
context 'without a :auth_callback Proc' do
|
437
524
|
it 'calls the originally provided block' do
|
438
525
|
auth.request_token
|
439
526
|
expect(@block_called).to eql(2)
|
@@ -441,8 +528,8 @@ describe Ably::Auth do
|
|
441
528
|
end
|
442
529
|
|
443
530
|
context 'with a provided block' do
|
444
|
-
it 'does not call the originally provided
|
445
|
-
auth.request_token { @request_block_called = true; auth.create_token_request }
|
531
|
+
it 'does not call the originally provided Proc and calls the new #request_token :auth_callback Proc' do
|
532
|
+
auth.request_token(auth_callback: Proc.new { @request_block_called = true; auth.create_token_request })
|
446
533
|
expect(@block_called).to eql(1)
|
447
534
|
expect(@request_block_called).to eql(true)
|
448
535
|
end
|
@@ -454,19 +541,19 @@ describe Ably::Auth do
|
|
454
541
|
describe '#create_token_request' do
|
455
542
|
let(:ttl) { 60 * 60 }
|
456
543
|
let(:capability) { { :foo => ["publish"] } }
|
457
|
-
let(:
|
458
|
-
subject { auth.create_token_request(
|
544
|
+
let(:token_request_options) { Hash.new }
|
545
|
+
subject { auth.create_token_request(token_request_options) }
|
459
546
|
|
460
|
-
it 'uses the key
|
461
|
-
expect(subject['
|
547
|
+
it 'uses the key name from the client' do
|
548
|
+
expect(subject['keyName']).to eql(key_name)
|
462
549
|
end
|
463
550
|
|
464
551
|
it 'uses the default TTL' do
|
465
|
-
expect(subject['ttl']).to eql(Ably::
|
552
|
+
expect(subject['ttl']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl) * 1000)
|
466
553
|
end
|
467
554
|
|
468
555
|
it 'uses the default capability' do
|
469
|
-
expect(subject['capability']).to eql(Ably::
|
556
|
+
expect(subject['capability']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability).to_json)
|
470
557
|
end
|
471
558
|
|
472
559
|
context 'the nonce' do
|
@@ -480,25 +567,25 @@ describe Ably::Auth do
|
|
480
567
|
end
|
481
568
|
end
|
482
569
|
|
483
|
-
%w(ttl
|
570
|
+
%w(ttl nonce client_id).each do |attribute|
|
484
571
|
context "with option :#{attribute}" do
|
485
|
-
let(:option_value) { random_int_str(1_000_000_000) }
|
572
|
+
let(:option_value) { random_int_str(1_000_000_000).to_i }
|
486
573
|
before do
|
487
|
-
|
574
|
+
token_request_options[attribute.to_sym] = option_value
|
488
575
|
end
|
489
576
|
it "overrides default" do
|
490
|
-
expect(subject
|
577
|
+
expect(subject.public_send(attribute).to_s).to eql(option_value.to_s)
|
491
578
|
end
|
492
579
|
end
|
493
580
|
end
|
494
581
|
|
495
582
|
context 'with additional invalid attributes' do
|
496
|
-
let(:
|
583
|
+
let(:token_request_options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
|
497
584
|
specify 'are ignored' do
|
498
|
-
expect(subject.keys).to_not include(:is_not_used_by_token_request)
|
499
|
-
expect(subject.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
|
500
|
-
expect(subject.keys).to include(
|
501
|
-
expect(subject
|
585
|
+
expect(subject.hash.keys).to_not include(:is_not_used_by_token_request)
|
586
|
+
expect(subject.hash.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
|
587
|
+
expect(subject.hash.keys).to include(:nonce)
|
588
|
+
expect(subject.nonce).to eql('valid')
|
502
589
|
end
|
503
590
|
end
|
504
591
|
|
@@ -506,47 +593,52 @@ describe Ably::Auth do
|
|
506
593
|
let(:client) { Ably::Rest::Client.new(auth_url: 'http://example.com', protocol: protocol) }
|
507
594
|
|
508
595
|
it 'should raise an exception if key secret is missing' do
|
509
|
-
expect { auth.create_token_request(
|
596
|
+
expect { auth.create_token_request(key_name: 'name') }.to raise_error Ably::Exceptions::TokenRequestError
|
510
597
|
end
|
511
598
|
|
512
|
-
it 'should raise an exception if key
|
599
|
+
it 'should raise an exception if key name is missing' do
|
513
600
|
expect { auth.create_token_request(key_secret: 'secret') }.to raise_error Ably::Exceptions::TokenRequestError
|
514
601
|
end
|
515
602
|
end
|
516
603
|
|
517
604
|
context 'with :query_time option' do
|
518
605
|
let(:time) { Time.now - 30 }
|
519
|
-
let(:
|
606
|
+
let(:token_request_options) { { query_time: true } }
|
520
607
|
|
521
608
|
it 'queries the server for the timestamp' do
|
522
609
|
expect(client).to receive(:time).and_return(time)
|
523
|
-
expect(subject['timestamp']).to
|
610
|
+
expect(subject['timestamp']).to be_within(1).of(time.to_f * 1000)
|
524
611
|
end
|
525
612
|
end
|
526
613
|
|
527
614
|
context 'with :timestamp option' do
|
528
615
|
let(:token_request_time) { Time.now + 5 }
|
529
|
-
let(:
|
616
|
+
let(:token_request_options) { { timestamp: token_request_time } }
|
530
617
|
|
531
618
|
it 'uses the provided timestamp in the token request' do
|
532
|
-
expect(subject['timestamp']).to
|
619
|
+
expect(subject['timestamp']).to be_within(1).of(token_request_time.to_f * 1000)
|
533
620
|
end
|
534
621
|
end
|
535
622
|
|
536
623
|
context 'signing' do
|
537
|
-
let(:
|
624
|
+
let(:token_request_options) do
|
538
625
|
{
|
539
|
-
|
540
|
-
ttl:
|
626
|
+
key_name: random_str,
|
627
|
+
ttl: random_int_str.to_i,
|
541
628
|
capability: random_str,
|
542
629
|
client_id: random_str,
|
543
|
-
timestamp: random_int_str,
|
630
|
+
timestamp: random_int_str.to_i,
|
544
631
|
nonce: random_str
|
545
632
|
}
|
546
633
|
end
|
547
634
|
|
635
|
+
# TokenRequest expects times in milliseconds, whereas create_token_request assumes Ruby default of seconds
|
636
|
+
let(:token_request_attributes) do
|
637
|
+
token_request_options.merge(timestamp: token_request_options[:timestamp] * 1000, ttl: token_request_options[:ttl] * 1000)
|
638
|
+
end
|
639
|
+
|
548
640
|
it 'generates a valid HMAC' do
|
549
|
-
hmac = hmac_for(
|
641
|
+
hmac = hmac_for(Ably::Models::TokenRequest(token_request_attributes).hash, key_secret)
|
550
642
|
expect(subject['mac']).to eql(hmac)
|
551
643
|
end
|
552
644
|
end
|
@@ -555,20 +647,20 @@ describe Ably::Auth do
|
|
555
647
|
context 'using token authentication' do
|
556
648
|
let(:capability) { { :foo => ["publish"] } }
|
557
649
|
|
558
|
-
describe 'with :
|
650
|
+
describe 'with :token option' do
|
559
651
|
let(:ttl) { 60 * 60 }
|
560
|
-
let(:
|
652
|
+
let(:token_details) do
|
561
653
|
auth.request_token(
|
562
654
|
ttl: ttl,
|
563
655
|
capability: capability
|
564
656
|
)
|
565
657
|
end
|
566
|
-
let(:
|
658
|
+
let(:token) { token_details.token }
|
567
659
|
let(:token_auth_client) do
|
568
|
-
Ably::Rest::Client.new(
|
660
|
+
Ably::Rest::Client.new(token: token, environment: environment, protocol: protocol)
|
569
661
|
end
|
570
662
|
|
571
|
-
it 'authenticates successfully using the provided :
|
663
|
+
it 'authenticates successfully using the provided :token' do
|
572
664
|
expect(token_auth_client.channel('foo').publish('event', 'data')).to be_truthy
|
573
665
|
end
|
574
666
|
|
@@ -598,23 +690,21 @@ describe Ably::Auth do
|
|
598
690
|
let(:client) do
|
599
691
|
Ably::Rest::Client.new(key: api_key, client_id: client_id, environment: environment, protocol: protocol)
|
600
692
|
end
|
601
|
-
let(:
|
693
|
+
let(:token) { 'unique-token' }
|
602
694
|
let(:token_response) do
|
603
695
|
{
|
604
|
-
|
605
|
-
id: token_id
|
606
|
-
}
|
696
|
+
token: token
|
607
697
|
}.to_json
|
608
698
|
end
|
609
699
|
|
610
700
|
context 'and requests to the Ably server are mocked', :webmock do
|
611
701
|
let!(:request_token_stub) do
|
612
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
702
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
|
613
703
|
to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' })
|
614
704
|
end
|
615
705
|
let!(:publish_message_stub) do
|
616
706
|
stub_request(:post, "#{client.endpoint}/channels/foo/publish").
|
617
|
-
with(headers: { 'Authorization' => "Bearer #{encode64(
|
707
|
+
with(headers: { 'Authorization' => "Bearer #{encode64(token)}" }).
|
618
708
|
to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
|
619
709
|
end
|
620
710
|
|
@@ -625,7 +715,7 @@ describe Ably::Auth do
|
|
625
715
|
end
|
626
716
|
|
627
717
|
describe 'a token is created' do
|
628
|
-
let(:token) { client.auth.
|
718
|
+
let(:token) { client.auth.current_token_details }
|
629
719
|
|
630
720
|
it 'before a request is made' do
|
631
721
|
expect(token).to be_nil
|
@@ -638,11 +728,11 @@ describe Ably::Auth do
|
|
638
728
|
it 'with capability and TTL defaults' do
|
639
729
|
client.channel('foo').publish('event', 'data')
|
640
730
|
|
641
|
-
expect(token).to be_a(Ably::Models::
|
642
|
-
capability_with_str_key = Ably::
|
643
|
-
capability = Hash[capability_with_str_key.keys.map(&:
|
644
|
-
expect(token.capability).to eq(
|
645
|
-
expect(token.
|
731
|
+
expect(token).to be_a(Ably::Models::TokenDetails)
|
732
|
+
capability_with_str_key = Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)
|
733
|
+
capability = Hash[capability_with_str_key.keys.map(&:to_s).zip(capability_with_str_key.values)]
|
734
|
+
expect(token.capability).to eq(capability)
|
735
|
+
expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl))
|
646
736
|
expect(token.client_id).to eq(client_id)
|
647
737
|
end
|
648
738
|
end
|
@@ -662,19 +752,5 @@ describe Ably::Auth do
|
|
662
752
|
expect(auth).to be_using_basic_auth
|
663
753
|
end
|
664
754
|
end
|
665
|
-
|
666
|
-
context 'when using legacy :api_key option and basic auth' do
|
667
|
-
let(:client) do
|
668
|
-
Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
|
669
|
-
end
|
670
|
-
|
671
|
-
specify '#using_token_auth? is false' do
|
672
|
-
expect(auth).to_not be_using_token_auth
|
673
|
-
end
|
674
|
-
|
675
|
-
specify '#key attribute contains the key string' do
|
676
|
-
expect(auth.key).to eql(api_key)
|
677
|
-
end
|
678
|
-
end
|
679
755
|
end
|
680
756
|
end
|